refactoring

This commit is contained in:
Sascha Bach 2025-11-26 12:01:50 +01:00
parent eacb5fc724
commit c587bec2c7
44 changed files with 1989 additions and 2032 deletions

1114
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,6 @@
"deploy:prod": "npm run build && node deploy.js" "deploy:prod": "npm run build && node deploy.js"
}, },
"dependencies": { "dependencies": {
"@types/react-router-dom": "^5.3.3",
"lucide-react": "^0.542.0", "lucide-react": "^0.542.0",
"react": "^19.1.1", "react": "^19.1.1",
"react-dom": "^19.1.1", "react-dom": "^19.1.1",
@ -23,6 +22,7 @@
"@eslint/js": "^9.32.0", "@eslint/js": "^9.32.0",
"@types/react": "^19.1.9", "@types/react": "^19.1.9",
"@types/react-dom": "^19.1.7", "@types/react-dom": "^19.1.7",
"@types/react-router-dom": "^5.3.3",
"@vitejs/plugin-react": "^4.7.0", "@vitejs/plugin-react": "^4.7.0",
"babel-plugin-react-compiler": "1.0.0", "babel-plugin-react-compiler": "1.0.0",
"dotenv": "^17.2.2", "dotenv": "^17.2.2",

View File

@ -1,11 +1,8 @@
import { GitBranch, ExternalLink } from 'lucide-react'; import { GitBranch, ExternalLink } from 'lucide-react';
import { useState, useRef, useEffect, type KeyboardEvent } from 'react'; import { useState, useRef, useEffect, type KeyboardEvent } from 'react';
import { personalConfig } from '../../config/personal';
import { getTexts } from '../../config/texts'; import { getTexts } from '../../config/texts';
import { projectsData } from '../../config/projects-config';
import type { Project } from '../../data/Project'; import type { Project } from '../../data/Project';
import ergoVRImage from '../../assets/ErgoVR.PNG';
import portfolioImage from '../../assets/portfolio.PNG'
import icaraceImage from '../../assets/icarace.PNG'
interface ProjectsSectionProps { interface ProjectsSectionProps {
@ -26,24 +23,11 @@ export default function ProjectsSection(props: ProjectsSectionProps = {}) {
const { const {
title = texts.title, title = texts.title,
subtitle = texts.subtitle, subtitle = texts.subtitle,
projects = texts.projectItems.map((project, index) => ({ projects = [...projectsData].sort((a, b) => {
id: index + 1, const yearA = parseInt(a.year || '0');
title: project.title, const yearB = parseInt(b.year || '0');
description: project.description, return yearB - yearA; // Descending order (newest first)
image: [portfolioImage, ergoVRImage, icaraceImage][index] || portfolioImage, })
technologies: [
["React", "TypeScript", "SCSS", "Github Copilot"],
["Unity3D", "C#", "Oculus SDK"],
["Angular 4", "Typescript", "HTML", "CSS"]
][index] || [],
year: ["2025", "2015", "2018"][index] || "2025",
github: [
personalConfig.gitProjects.portfolio,
personalConfig.gitProjects.ergoVR,
personalConfig.gitProjects.ergoVR
][index],
live: index === 0 ? personalConfig.projectsUrls.portfolio : undefined
}))
} = props; } = props;
// Get available actions for a project // Get available actions for a project

View File

@ -0,0 +1,35 @@
export const about = {
title: 'Über mich',
subtitle:
'Leidenschaftlicher Entwickler mit Fokus auf sauberen Code und innovative Lösungen.',
name: 'Sascha Bach',
bio: [
'Ich habe Informatik an der TU Darmstadt studiert und 2016 meinen Bachelor mit einer Arbeit über Motion Sickness in Virtual Reality abgeschlossen. Seitdem arbeite ich als Software- und Webentwickler mit Schwerpunkt auf Angular und React.',
'In meiner Arbeit habe ich gesehen, dass jedes Unternehmen anders ist, aber viele stehen vor der gleichen Herausforderung: ihre digitale Präsenz zugänglich, modern und zukunftssicher zu halten. Vielen Unternehmen ist bereits bewusst, dass Barrierefreiheit gesetzlich vorgeschrieben ist, aber in der Praxis kommt es vor allem darauf an, dass eine Website für alle Nutzer einfach und angenehm funktioniert.',
'Meine Spezialisierung liegt in Web Accessibility (WCAG 2.1, BFSG, European Accessibility Act). Ich helfe Unternehmen dabei, ihre Websites und Anwendungen zu aktualisieren, sodass sie konform und gleichzeitig für jeden einfach zu bedienen sind. Was meine Kunden schätzen, ist, dass ich technisches Fachwissen in Angular, React und TypeScript mit einem menschenzentrierten Ansatz verbinde. Denn Barrierefreiheit geht nicht nur um Code, sondern um Menschen.',
'Wenn Sie sich fragen, ob Ihre Website bereits den Standards für Barrierefreiheit entspricht, oder wenn Sie das Gefühl haben, dass es Zeit ist, Ihre Online-Präsenz inklusiver und rechtssicher zu gestalten, würde ich Sie gerne unterstützen. Oft können bereits wenige klare Schritte einen großen Unterschied machen und Ihrer Website helfen, mehr Menschen zu erreichen und sich von der Konkurrenz abzuheben.',
'Lassen Sie uns verbinden und schauen, wie wir Ihre Website barrierefrei, modern und zukunftssicher machen können.',
],
featureCards: [
{
title: 'Webentwicklung',
description:
'Entwicklung moderner Webanwendungen mit Angular und React Frameworks unter Verwendung von TypeScript und JavaScript für robuste, skalierbare Lösungen.',
},
{
title: 'Responsive Design',
description:
'Erstellung pixelgenauer, responsiver Benutzeroberflächen, die nahtlos auf allen Geräten und Bildschirmgrößen funktionieren.',
},
{
title: 'Cross-Browser-Kompatibilität',
description:
'Gewährleistung konsistenter Benutzererlebnisse in allen gängigen Browsern durch gründliches Testen und Optimierung.',
},
{
title: 'Web-Barrierefreiheit',
description:
'Implementierung von Best Practices für Barrierefreiheit zur Bereitstellung von Websites, die Screenreader, Tastaturnavigation und assistive Technologien unterstützen.',
},
],
};

View File

@ -0,0 +1,52 @@
export const accessibility = {
skipLinks: {
skipToHero: 'Zum Hauptinhalt springen',
skipToAbout: 'Zum Über-mich-Bereich springen',
skipToServices: 'Zum Leistungs-Bereich springen',
skipToSkills: 'Zum Fähigkeiten-Bereich springen',
skipToProjects: 'Zum Projekt-Bereich springen',
skipToCertifications: 'Zum Zertifikate-Bereich springen',
skipToContact: 'Zum Kontakt-Bereich springen',
},
navigation: {
keyboardHelp: 'Tastaturnavigations-Hilfe',
keyboardHelpExpanded: [
'Verwenden Sie Tab, um zwischen Elementen zu navigieren',
'Verwenden Sie Enter oder Leertaste, um Buttons und Links zu aktivieren',
'Verwenden Sie Pfeiltasten, um zwischen Projektkarten zu navigieren',
'Verwenden Sie Escape, um diesen Hilfedialog zu schließen',
'Verwenden Sie Strg+F, um die Seite zu durchsuchen',
],
useTabToNavigate: 'Verwenden Sie Tab, um zwischen Elementen zu navigieren',
useTabToNavigateCards:
'Verwenden Sie Tab, um zwischen Projektkarten zu navigieren, oder Pfeiltasten, um sich im Raster zu bewegen',
useTabToNavigateOptions: 'Verwenden Sie Tab, um zwischen Optionen zu navigieren',
closeKeyboardHelp: 'Tastaturhilfe schließen',
},
screenReader: {
professionalStatistics: 'Berufliche Statistiken',
projectCard: 'Projektkarte',
viewSourceCode: 'Quellcode anzeigen',
viewLiveDemo: 'Live-Demo anzeigen',
downloadResume: 'Lebenslauf herunterladen',
downloadResumeDescription:
'Dies lädt meinen aktuellen Lebenslauf als PDF-Datei herunter',
keyboardNavigationHelp: 'Tastaturnavigations-Hilfe',
themeSwitchButton: 'Theme-Wechsel-Button',
switchToTheme: 'Zu Theme wechseln',
certificationCard: 'Zertifikats-Karte',
serviceCard: 'Leistungs-Karte',
skillBadge: 'Fähigkeiten-Badge',
},
ariaLabels: {
mainNavigation: 'Hauptnavigation',
socialLinks: 'Social-Media-Links',
themeToggle: 'Theme wechseln',
mobileMenuToggle: 'Mobilmenü umschalten',
contactForm: 'Kontaktformular',
projectsGrid: 'Projektraster',
skillsGrid: 'Fähigkeiten-Raster',
certificationsGrid: 'Zertifikate-Raster',
servicesGrid: 'Leistungs-Raster',
},
};

View File

@ -0,0 +1,26 @@
export const certifications = {
title: 'Zertifizierungen & Erfolge',
subtitle: 'Meine beruflichen Qualifikationen und kontinuierliche Weiterbildung',
certificationItems: [
{
name: 'Practical Prompt Engineering Masterclass: Hands-On Learning',
issuer: 'Udemy Kurs - Asif Farooqi, Abdullah Dar',
},
{
name: 'Understanding Typescript',
issuer: 'Udemy Kurs - Maximilian Schwarzmüller',
},
{
name: 'Angular Step by Step',
issuer: 'Udemy Kurs - Shivprasad Koirala',
},
{
name: 'Angular and Typescript',
issuer: 'LinkedIn TestDome',
},
{
name: 'Bachelor of Science in Informatik',
issuer: 'TU Darmstadt',
},
],
};

View File

@ -0,0 +1,21 @@
export const contact = {
title: 'Kontakt aufnehmen',
subtitle: 'Lassen Sie uns Ihr Projekt besprechen und Ihre Ideen zum Leben erwecken',
connectTitle: 'Lassen Sie uns verbinden',
connectDescription:
'Ich bin immer an neuen Möglichkeiten und spannenden Projekten interessiert. Ob Sie eine Frage haben oder einfach nur Hallo sagen möchten, zögern Sie nicht, mich zu kontaktieren!',
buttonText: 'Per E-Mail kontaktieren',
socialTitle: 'Weitere Kontaktmöglichkeiten',
availabilityText: 'Verfügbar für Projekte',
responseTimeLabel: 'Antwortzeit:',
responseTimeValue: 'Innerhalb von 24 Stunden',
preferredContactLabel: 'Bevorzugter Kontakt:',
preferredContactValue: 'E-Mail',
locationLabel: 'Standort:',
locationValue: 'Deutschland',
emailSubject: 'Portfolio-Anfrage - Lassen Sie uns Ihr Projekt besprechen',
emailBody:
'Hallo Sascha,\n\nIch habe Ihr Portfolio besucht und würde gerne ein potenzielles Projekt oder eine Zusammenarbeit besprechen.\n\nMit freundlichen Grüßen,',
emailAnnouncement: 'E-Mail-Client wird mit vorausgefüllter Nachricht geöffnet',
socialAnnouncement: '{platform}-Profil wird in neuem Tab geöffnet',
};

View File

@ -0,0 +1,7 @@
export const footer = {
imprintText: 'Impressum und Datenschutz',
copyrightText: 'Alle Rechte vorbehalten.',
githubAriaLabel: 'GitHub-Profil',
linkedinAriaLabel: 'LinkedIn-Profil',
emailAriaLabel: 'E-Mail senden',
};

View File

@ -0,0 +1,14 @@
export const hero = {
title: 'Jeder verdient ein reibungsloses Online-Erlebnis',
description:
'Ich helfe Ihnen, barrierefreie Websites zu erstellen, die jeden Besucher einbeziehen, im Einklang mit dem Barrierefreiheitsstärkungsgesetz.',
primaryButtonText: 'Projekte ansehen',
secondaryButtonText: 'Lebenslauf herunterladen',
tertiaryButtonText: 'Kontakt aufnehmen',
statItems: [
{ label: 'Jahre Erfahrung', value: '10+' },
{ label: 'Responsive Design', value: '100%' },
{ label: 'Verwendete Technologien', value: '10+' },
{ label: 'Frameworks', value: '3+' },
],
};

View File

@ -0,0 +1,154 @@
export const imprint = {
title: 'Impressum und Datenschutz',
subtitle: 'Rechtliche Informationen',
companyInfoTitle: 'Angaben gemäß § 5 DDG',
contactTitle: 'Kontakt',
responsibilityTitle: 'Vertretungsberechtigt',
disclaimerTitle: 'Haftungsausschluss',
contentLiabilityTitle: 'Haftung für Inhalte',
contentLiabilityText: [
'Als Diensteanbieter sind wir gemäß § 7 Abs.1 DDG für eigene Inhalte auf diesen Seiten nach den allgemeinen Gesetzen verantwortlich. Nach §§ 8 bis 10 TMG sind wir als Diensteanbieter jedoch nicht unter der Verpflichtung, übermittelte oder gespeicherte fremde Informationen zu überwachen oder nach Umständen zu forschen, die auf eine rechtswidrige Tätigkeit hinweisen.',
'Verpflichtungen zur Entfernung oder Sperrung der Nutzung von Informationen nach den allgemeinen Gesetzen bleiben hiervon unberührt. Eine diesbezügliche Haftung ist jedoch erst ab dem Zeitpunkt der Kenntnis einer konkreten Rechtsverletzung möglich. Bei Bekanntwerden von entsprechenden Rechtsverletzungen werden wir diese Inhalte umgehend entfernen.',
],
copyrightTitle: 'Urheberrecht',
copyrightText: [
'Die durch die Seitenbetreiber erstellten Inhalte und Werke auf diesen Seiten unterliegen dem deutschen Urheberrecht. Die Vervielfältigung, Bearbeitung, Verbreitung und jede Art der Verwertung außerhalb der Grenzen des Urheberrechtes bedürfen der schriftlichen Zustimmung des jeweiligen Autors bzw. Erstellers.',
'Downloads und Kopien dieser Seite sind nur für den privaten, nicht kommerziellen Gebrauch gestattet. Soweit die Inhalte auf dieser Seite nicht vom Betreiber erstellt wurden, werden die Urheberrechte Dritter beachtet. Insbesondere werden Inhalte Dritter als solche gekennzeichnet. Sollten Sie trotzdem auf eine Urheberrechtsverletzung aufmerksam werden, bitten wir um einen entsprechenden Hinweis. Bei Bekanntwerden von Rechtsverletzungen werden wir derartige Inhalte umgehend entfernen.',
],
privacyTitle: 'Datenschutz',
privacyText:
'Die Nutzung unserer Webseite ist in der Regel ohne Angabe personenbezogener Daten möglich. Soweit auf unseren Seiten personenbezogene Daten (beispielsweise Name, Anschrift oder E-Mail-Adressen) erhoben werden, erfolgt dies, soweit möglich, stets auf freiwilliger Basis. Diese Daten werden ohne Ihre ausdrückliche Zustimmung nicht an Dritte weitergegeben.',
detailedPrivacyPolicy: {
title: 'Datenschutzerklärung',
introduction:
'Mit dieser Datenschutzerklärung möchten wir Sie über Art, Umfang und Zweck der Verarbeitung von personenbezogenen Daten (im Folgenden auch nur als "Daten" bezeichnet) aufklären. Personenbezogene Daten sind alle Daten, die einen persönlichen Bezug zu Ihnen aufweisen, z. B. Name, Adresse, E-Mail-Adresse oder Ihr Nutzerverhalten. Die Datenschutzerklärung gilt für alle von uns vorgenommene Daten-Verarbeitungsvorgänge sowohl im Rahmen unserer Kerntätigkeit als auch für die von uns vorgehaltenen Online-Medien.',
responsibleTitle:
'Wer bei uns für die Datenverarbeitung verantwortlich ist',
responsibleText: 'Verantwortlich für die Datenverarbeitung ist:',
responsibleContact:
'Sascha Bach\nAlt-Tempelhof 15\n12099 Berlin\nDE\n01799364867\ninfo@sascha-bach.de',
dataProtectionOfficerTitle:
'Kontaktdaten unseres Datenschutzbeauftragten',
dataProtectionOfficerText:
'Unseren Datenschutzbeauftragten können Sie per E-Mail unter info [at] sascha-bach.de oder unter unserer Postadresse mit dem Zusatz „an den Datenschutzbeauftragten" erreichen.',
rightsTitle: 'Ihre Rechte nach der DSGVO',
rightsIntro:
'Nach der DSGVO stehen Ihnen die nachfolgend aufgeführten Rechte zu, die Sie jederzeit bei dem in Ziffer 1. dieser Datenschutzerklärung genannten Verantwortlichen geltend machen können:',
rights: [
{
title: 'Recht auf Auskunft',
text: 'Sie haben das Recht, von uns Auskunft darüber zu verlangen, ob und welche Daten wir von Ihnen verarbeiten.',
},
{
title: 'Recht auf Berichtigung',
text: 'Sie haben das Recht, die Berichtigung unrichtiger oder Vervollständigung unvollständiger Daten zu verlangen.',
},
{
title: 'Recht auf Löschung',
text: 'Sie haben das Recht, die Löschung Ihrer Daten zu verlangen.',
},
{
title: 'Recht auf Einschränkung',
text: 'Sie haben in bestimmten Fällen das Recht zu verlangen, dass wir Ihre Daten nur noch eingeschränkt bearbeiten.',
},
{
title: 'Recht auf Datenübertragbarkeit',
text: 'Sie haben das Recht zu verlangen, dass wir Ihnen oder einem anderen Verantwortlichen Ihre Daten in einem strukturierten, gängigen und maschinenlesebaren Format übermitteln.',
},
{
title: 'Beschwerderecht',
text: 'Sie haben das Recht, sich bei einer Aufsichtsbehörde zu beschweren. Zuständig ist die Aufsichtsbehörde Ihres üblichen Aufenthaltsortes, Ihres Arbeitsplatzes oder unseres Firmensitzes.',
},
],
revocationTitle: 'Widerrufsrecht',
revocationText:
'Sie haben das Recht, die von Ihnen erteilte Einwilligung zur Datenverarbeitung jederzeit zu widerrufen.',
objectionTitle: 'Widerspruchsrecht',
objectionText:
'Sie haben das Recht, jederzeit gegen die Verarbeitung Ihrer Daten, die wir auf unser berechtigtes Interesse nach Art. 6 Abs. 1 lit. f DSGVO stützen, Widerspruch einzulegen. Sofern Sie von Ihrem Widerspruchsrecht Gebrauch machen, bitten wir Sie um die Darlegung der Gründe. Wir werden Ihre personenbezogenen Daten dann nicht mehr verarbeiten, es sei denn, wir können Ihnen gegenüber nachweisen, dass zwingende schutzwürdige Gründe an der Datenverarbeitung Ihre Interessen und Rechte überwiegen.',
objectionHighlight:
'Unabhängig vom vorstehend Gesagten, haben Sie das jederzeitige Recht, der Verarbeitung Ihrer personenbezogenen Daten für Zwecke der Werbung und Datenanalyse zu widersprechen.',
objectionContact:
'Ihren Widerspruch richten Sie bitte an die oben angegebene Kontaktadresse des Verantwortlichen.',
dataDeletionTitle: 'Wann löschen wir Ihre Daten?',
dataDeletionIntro:
'Wir löschen Ihre Daten dann, wenn wir diese nicht mehr brauchen oder Sie uns dies vorgeben. Das bedeutet, dass - sofern sich aus den einzelnen Datenschutzhinweisen dieser Datenschutzerklärung nichts anderes ergibt - wir Ihre Daten löschen,',
dataDeletionReasons: [
'wenn der Zweck der Datenverarbeitung weggefallen ist und damit die jeweilige in den einzelnen Datenschutzhinweisen genannte Rechtsgrundlage nicht mehr besteht, also bspw.',
'nach Beendigung der zwischen uns bestehenden vertraglichen oder mitgliedschaftlichen Beziehungen (Art. 6 Abs. 1 lit. a DSGVO) oder',
'nach Wegfall unseres berechtigten Interesses an der weiteren Verarbeitung oder Speicherung Ihrer Daten (Art. 6 Abs. 1 lit. f DSGVO),',
'wenn Sie von Ihrem Widerrufsrecht Gebrauch machen und keine anderweitige gesetzliche Rechtsgrundlage für die Verarbeitung im Sinne von Art. 6 Abs. 1 lit. b-f DSGVO eingreift,',
'wenn Sie vom Ihrem Widerspruchsrecht Gebrauch machen und der Löschung keine zwingenden schutzwürdigen Gründe entgegenstehen.',
],
retentionText:
'Sofern wir (bestimmte Teile) Ihre(r) Daten jedoch noch für andere Zwecke vorhalten müssen, weil dies etwa steuerliche Aufbewahrungsfristen (in der Regel 6 Jahre für Geschäftskorrespondenz bzw. 10 Jahre für Buchungsbelege) oder die Geltendmachung, Ausübung oder Verteidigung von Rechtsansprüchen aus vertraglichen Beziehungen (bis zu vier Jahren) erforderlich machen oder die Daten zum Schutz der Rechte einer anderen natürlichen oder juristischen Person gebraucht werden, löschen wir (den Teil) Ihre(r) Daten erst nach Ablauf dieser Fristen. Bis zum Ablauf dieser Fristen beschränken wir die Verarbeitung dieser Daten jedoch auf diese Zwecke (Erfüllung der Aufbewahrungspflichten).',
webhostingTitle: 'Webhosting',
webhostingText:
'Wir bedienen uns zum Vorhalten unserer Internetseiten eines Anbieters, auf dessen Server unsere Internetseiten gespeichert und für den Abruf im Internet verfügbar gemacht werden (Hosting). Hierbei können von dem Anbieter all diejenigen über den von Ihnen genutzten Browser übertragenen Daten verarbeitet werden, die bei der Nutzung unserer Internetseiten anfallen. Hierzu gehören insbesondere Ihre IP-Adresse, die der Anbieter benötigt, um unser Online-Angebot an den von Ihnen genutzten Browser ausliefern zu können sowie sämtliche von Ihnen über unsere Internetseite getätigten Eingaben. Daneben kann der von uns genutzte Anbieter',
webhostingDataTypes: [
'das Datum und die Uhrzeit des Zugriffs auf unsere Internetseite',
'Zeitzonendifferenz zur Greenwich Mean Time (GMT)',
'Zugriffsstatus (HTTP-Status)',
'die übertragene Datenmenge',
'der Internet-Service-Provider des zugreifenden Systems',
'der von Ihnen verwendete Browsertyp und dessen Version',
'das von Ihnen verwendete Betriebssystem',
'die Internetseite, von welcher Sie gegebenenfalls auf unsere Internetseite gelangt sind',
'die Seiten bzw. Unterseiten, welche Sie auf unserer Internetseite besuchen.',
],
webhostingPurpose:
'erheben. Die vorgenannten Daten werden als Logfiles auf den Servern unseres Anbieters gespeichert. Dies ist erforderlich, um die Stabilität und Sicherheit des Betriebs unserer Internetseite zu gewährleisten.',
webhostingDataCategories: {
affectedData: 'Betroffene Daten:',
affectedDataList: [
'Inhaltsdaten (bspw. Posts, Fotos, Videos)',
'Nutzungsdaten (bspw. Zugriffszeiten, angeklickte Webseiten)',
'Kommunikationsdaten (bspw. Informationen über das genutzte Gerät, IP-Adresse)',
],
affectedPersons: 'Betroffene Personen: Nutzer unserer Internetpräsenz',
processingPurpose:
'Verarbeitungszweck: Ausspielen unserer Internetseiten, Gewährleistung des Betriebs unserer Internetseiten',
legalBasis:
'Rechtsgrundlage: Berechtigtes Interesse, Art. 6 Abs. 1 lit. f DSGVO',
provider: 'Von uns beauftragte(r) Webhoster:',
},
hostingProvider: {
name: 'Bitpalast GmbH',
address: 'Postfach 19 15 64, D-14005 Berlin, Deutschland',
website: 'https://preiswerter-webserver-de.bitpalast.net/',
},
contactTitle: 'Kontaktaufnahme',
contactText:
'Soweit Sie uns über E-Mail, Soziale Medien, Telefon, Fax, Post, unser Kontaktformular oder sonstwie ansprechen und uns hierbei personenbezogene Daten wie Ihren Namen, Ihre Telefonnummer oder Ihre E-Mail-Adresse zur Verfügung stellen oder weitere Angaben zur Ihrer Person oder Ihrem Anliegen machen, verarbeiten wir diese Daten zur Beantwortung Ihrer Anfrage im Rahmen des zwischen uns bestehenden vorvertraglichen oder vertraglichen Beziehungen.',
contactDataCategories: {
affectedData: [
'Bestandsdaten (bspw. Namen, Adressen)',
'Kontakdaten (bspw. E-Mail-Adresse, Telefonnummer, Postanschrift)',
'Inhaltsdaten (Texte, Fotos, Videos)',
'Vertragsdaten (bspw. Vertragsgegenstand, Vertragsdauer)',
],
affectedPersons:
'Interessenten, Kunden, Geschäfts- und Vertragspartner',
processingPurpose:
'Kommunikation sowie Beantwortung von Kontaktanfragen, Büro und Organisationsverfahren',
legalBasis:
'Vertragserfüllung und vorvertragliche Anfragen, Art. 6 Abs. 1 lit. b DSGVO, berechtigtes Interesse, Art. 6 Abs. 1 lit. f DSGVO',
},
securityTitle: 'Sicherheitsmaßnahmen',
securityText:
'Wir treffen im Übrigen technische und organisatorische Sicherheitsmaßnahmen nach dem Stand der Technik, um die Vorschriften der Datenschutzgesetze einzuhalten und Ihre Daten gegen zufällige oder vorsätzliche Manipulationen, teilweisen oder vollständigen Verlust, Zerstörung oder gegen den unbefugten Zugriff Dritter zu schützen.',
changesTitle: 'Aktualität und Änderung dieser Datenschutzerklärung',
changesText:
'Diese Datenschutzerklärung ist aktuell gültig und hat den Stand September 2025. Aufgrund geänderter gesetzlicher bzw. behördlicher Vorgaben kann es notwendig werden, diese Datenschutzerklärung anzupassen.',
disclaimer:
'Diese Datenschutzerklärung wurde mit Hilfe des Datenschutz-Generators von SOS Recht erstellt. SOS Recht ist ein Angebot der Mueller.legal Rechtsanwälte Partnerschaft mit Sitz in Berlin.',
},
address: {
street: 'Alt-Tempelhof 15',
city: '12099 Berlin',
country: 'Deutschland',
},
contact: {
phone: '+49 (0) 179 9364867',
},
};

View File

@ -0,0 +1,29 @@
import { navigation } from './navigation';
import { themeToggle } from '../../theme-toggle';
import { footer } from './footer';
import { hero } from './hero';
import { about } from './about';
import { services } from './services';
import { skills } from './skills';
import { projects } from './projects';
import { certifications } from './certifications';
import { contact } from './contact';
import { imprint } from './imprint';
import { privacyPolicy } from './privacy-policy';
import { accessibility } from './accessibility';
export const de = {
navigation,
themeToggle,
footer,
hero,
about,
services,
skills,
projects,
certifications,
contact,
imprint,
privacyPolicy,
accessibility,
};

View File

@ -0,0 +1,11 @@
export const navigation = {
menuItems: [
'Über mich',
'Leistungen',
'Fähigkeiten',
'Zertifikate',
'Projekte',
'Kontakt',
],
mobileMenuAriaLabel: 'Menü öffnen',
};

View File

@ -0,0 +1,36 @@
export const privacyPolicy = {
title: 'Datenschutzerklärung (GDPR/DSGVO)',
lastUpdated: 'Zuletzt aktualisiert: November 2025',
introTitle: 'Überblick',
introText:
'Diese Website ist eine statische Portfolio-Website, die keine personenbezogenen Daten erhebt, verarbeitet oder speichert. Es werden keine Cookies, Tracking- oder Analyse-Tools eingesetzt.',
dataCollectionTitle: 'Datenerhebung',
dataCollectionText:
'Diese Website erhebt keine personenbezogenen Daten. Wenn Sie auf Kontaktlinks klicken (E-Mail, LinkedIn, GitHub), werden Sie zu externen Diensten weitergeleitet, die eigene Datenschutzrichtlinien haben:',
dataCollectionList: [
'E-Mail: Öffnet Ihr Standard-E-Mail-Programm (keine Datenübertragung über diese Website)',
'LinkedIn: Weiterleitung zu LinkedIn (unterliegt den Datenschutzbestimmungen von LinkedIn)',
'GitHub: Weiterleitung zu GitHub (unterliegt den Datenschutzbestimmungen von GitHub)',
],
webhostingTitle: 'Webhosting',
webhostingText:
'Diese Website wird von Bitpalast GmbH gehostet. Der Hosting-Anbieter kann technische Daten wie IP-Adressen, Browsertyp und Zugriffszeiten in Server-Logdateien erfassen. Diese Daten sind für den technischen Betrieb und die Sicherheit der Website erforderlich.',
webhostingProvider: 'Hosting-Anbieter:',
webhostingProviderName: 'Bitpalast GmbH',
webhostingProviderAddress: 'Postfach 19 15 64, D-14005 Berlin, Deutschland',
webhostingProviderWebsite: 'https://preiswerter-webserver-de.bitpalast.net/',
rightsTitle: 'Ihre Rechte nach der DSGVO',
rightsText: 'Nach der DSGVO haben Sie folgende Rechte:',
rightsList: [
'Recht auf Auskunft über gespeicherte Daten',
'Recht auf Berichtigung falscher Daten',
'Recht auf Löschung Ihrer Daten',
'Recht auf Einschränkung der Verarbeitung',
'Recht auf Datenübertragbarkeit',
'Recht auf Widerspruch gegen die Datenverarbeitung',
],
contactTitle: 'Kontakt für Datenschutzangelegenheiten',
contactText:
'Bei Fragen zum Datenschutz wenden Sie sich bitte an:',
contactEmail: 'info [at] sascha-bach.de',
};

View File

@ -0,0 +1,32 @@
export const projects = {
title: 'Ausgewählte Projekte',
subtitle: 'Eine Übersicht meiner Arbeiten und persönlichen Projekte',
codeButtonText: 'Code',
liveButtonText: 'Live',
projectItems: [
{
id: 1,
title: 'Portfolio-Website',
description:
'Eine persönliche Portfolio-Website zur Präsentation meiner Fähigkeiten, Projekte und Erfahrungen.',
},
{
id: 2,
title: 'ErgoVR',
description:
'Eine Virtual-Reality-Anwendung zur Analyse von Motion Sickness in VR-Umgebungen aus dem Jahr 2015.',
},
{
id: 3,
title: 'Icarace',
description:
'Beteiligung an der Entwicklung einer Web-Plattform für ein Fitness-Rennspiel für die Icaros GmbH bis 2018.',
},
{
id: 4,
title: 'Danca Alegria Vereins-Website',
description:
'Unterstützung bei freiwilligen Änderungen zur Einhaltung der WCAG 2.1 Level AA Barrierefreiheit.',
}
],
};

View File

@ -0,0 +1,36 @@
export const services = {
title: 'Leistungen',
subtitle: 'Umfassende Webentwicklungslösungen maßgeschneidert für Ihre Bedürfnisse',
serviceItems: [
{
title: 'Web-Barrierefreiheit',
description:
'Entwicklung inklusiver und barrierefreier Webanwendungen durch Umsetzung der Web Content Accessibility Guidelines, semantischem HTML und Unterstützung für assistive Technologien.',
},
{
title: 'Responsive Webdesign',
description:
'Erstellung von Mobile-First, responsiven Benutzeroberflächen, die auf allen Geräten und Bildschirmgrößen optimale Darstellung bieten.',
},
{
title: 'Cross-Browser-Testing',
description:
'Sicherstellung konsistenter Funktionalität und Darstellung in allen gängigen Browsern, einschließlich Chrome, Firefox, Safari und Edge.',
},
{
title: 'Frontend-Architektur',
description:
'Entwurf und Implementierung skalierbarer Frontend-Architekturen mit komponentenbasierter Entwicklung und modernem Tooling.',
},
{
title: 'Performance-Optimierung',
description:
'Optimierung von Webanwendungen für Geschwindigkeit und Effizienz durch Code-Splitting, Lazy Loading und Performance-Best-Practices.',
},
{
title: 'Testing & Qualitätssicherung',
description:
'Implementierung umfassender Teststrategien mit Cypress für End-to-End-Tests und Sicherstellung der Code-Qualität.',
},
],
};

View File

@ -0,0 +1,60 @@
export const skills = {
title: 'Fähigkeiten & Technologien',
subtitle: 'Technisches Fachwissen über den gesamten Entwicklungsstack',
skillCategories: [
{
title: 'Web-Frameworks',
skills: [
{ skill: 'Angular', value: 90 },
{ skill: 'React', value: 70 },
{ skill: 'Next.js', value: 70 },
{ skill: 'TypeScript', value: 95 },
],
},
{
title: 'Styling & Design',
skills: [
{ skill: 'Barrierefreiheit (a11y) Standards', value: 90 },
{ skill: 'SCSS/Sass', value: 95 },
{ skill: 'CSS3', value: 95 },
{ skill: 'Bootstrap', value: 80 },
],
},
{
title: 'Backend-Entwicklung',
skills: [
{ skill: 'Node.js', value: 75 },
{ skill: 'Express.js', value: 70 },
{ skill: 'REST APIs', value: 85 },
{ skill: 'Datenbank-Design', value: 70 },
],
},
{
title: 'Entwicklungswerkzeuge',
skills: [
{ skill: 'Git & GitHub', value: 90 },
{ skill: 'VS Code', value: 95 },
{ skill: 'npm/yarn', value: 85 },
{ skill: 'Webpack/Vite', value: 75 },
],
},
{
title: 'Testing & Qualität',
skills: [
{ skill: 'Unit-Testing', value: 80 },
{ skill: 'E2E-Testing', value: 70 },
{ skill: 'Code-Review', value: 85 },
{ skill: 'Performance-Optimierung', value: 75 },
],
},
{
title: 'KI-Tools',
skills: [
{ skill: 'GitHub Copilot', value: 95 },
{ skill: 'ChatGPT', value: 90 },
{ skill: 'Claude', value: 85 },
{ skill: 'KI-gestützte Entwicklung', value: 90 },
],
},
],
};

View File

@ -0,0 +1,35 @@
export const about = {
title: 'About Me',
subtitle:
'Passionate developer with a focus on clean code and innovative solutions.',
name: 'Sascha Bach',
bio: [
'I studied Computer Science at the Technical University of Darmstadt and graduated in 2016 with my bachelor thesis on Motion Sickness in Virtual Reality. Since then I have been working as a Software and Web Developer with a focus on Angular and React.',
'In my work I have seen that every company is different, but many face the same challenge: keeping their digital presence accessible, modern and future proof. A lot of businesses already know that accessibility is required by law, but in practice what matters most is that a website works for all users in a simple and enjoyable way.',
'My specialization is Web Accessibility (WCAG 2.1, BFSG, European Accessibility Act). I help companies update their websites and applications so they are compliant and at the same time easy to use for everyone. What my clients value is that I combine technical expertise in Angular, React and TypeScript with a human centered approach. Because accessibility is not just about code, it is about people.',
'If you are wondering whether your website already meets accessibility standards, or if you feel it is time to make your online presence more inclusive and legally compliant, I would be glad to support you. Often just a few clear steps can make a big difference, helping your website reach more people and stand out from the competition.',
"Let's connect and see how we can make your website barrier free, modern and ready for the future.",
],
featureCards: [
{
title: 'Web Development',
description:
'Building modern web applications with Angular and React frameworks, leveraging TypeScript and JavaScript for robust, scalable solutions.',
},
{
title: 'Responsive Design',
description:
'Creating pixel-perfect, responsive interfaces that work seamlessly across all devices and screen sizes.',
},
{
title: 'Cross-Browser Compatibility',
description:
'Ensuring consistent user experiences across all major browsers with thorough testing and optimization.',
},
{
title: 'Web Accessibility',
description:
'Implementing accessibility best practices to deliver websites that support screen readers, keyboard navigation, and assistive technologies.',
},
],
};

View File

@ -0,0 +1,52 @@
export const accessibility = {
skipLinks: {
skipToHero: 'Skip to main content',
skipToAbout: 'Skip to about section',
skipToServices: 'Skip to services section',
skipToSkills: 'Skip to skills section',
skipToProjects: 'Skip to projects section',
skipToCertifications: 'Skip to certifications section',
skipToContact: 'Skip to contact section',
},
navigation: {
keyboardHelp: 'Keyboard Navigation Help',
keyboardHelpExpanded: [
'Use Tab to navigate between elements',
'Use Enter or Space to activate buttons and links',
'Use Arrow keys to navigate between project cards',
'Use Escape to close this help dialog',
'Use Ctrl+F to search the page',
],
useTabToNavigate: 'Use Tab to navigate between elements',
useTabToNavigateCards:
'Use Tab to navigate between project cards, or Arrow keys to move within the grid',
useTabToNavigateOptions: 'Use Tab to navigate between options',
closeKeyboardHelp: 'Close keyboard help',
},
screenReader: {
professionalStatistics: 'Professional Statistics',
projectCard: 'Project card',
viewSourceCode: 'View source code',
viewLiveDemo: 'View live demo',
downloadResume: 'Download Resume',
downloadResumeDescription:
'This will download my current resume as a PDF file',
keyboardNavigationHelp: 'Keyboard Navigation Help',
themeSwitchButton: 'Theme switch button',
switchToTheme: 'Switch to theme',
certificationCard: 'Certification card',
serviceCard: 'Service card',
skillBadge: 'Skill badge',
},
ariaLabels: {
mainNavigation: 'Main navigation',
socialLinks: 'Social media links',
themeToggle: 'Toggle theme',
mobileMenuToggle: 'Toggle mobile menu',
contactForm: 'Contact form',
projectsGrid: 'Projects grid',
skillsGrid: 'Skills grid',
certificationsGrid: 'Certifications grid',
servicesGrid: 'Services grid',
},
};

View File

@ -0,0 +1,26 @@
export const certifications = {
title: 'Certifications & Achievements',
subtitle: 'My professional qualifications and continuous learning',
certificationItems: [
{
name: 'Practical Prompt Engineering Masterclass: Hands-On Learning',
issuer: 'Udemy Lecture - Asif Farooqi, Abdullah Dar',
},
{
name: 'Understanding Typescript',
issuer: 'Udemy Lecture - Maximilian Schwarzmüller',
},
{
name: 'Angular Step by Step',
issuer: 'Udemy Lecture - Shivprasad Koirala',
},
{
name: 'Angular and Typescript',
issuer: 'LinkedIn TestDome',
},
{
name: 'Bachelor of Science in Computer Science',
issuer: 'TU Darmstadt',
},
],
};

View File

@ -0,0 +1,21 @@
export const contact = {
title: 'Get In Touch',
subtitle: "Let's discuss your project and bring your ideas to life",
connectTitle: "Let's Connect",
connectDescription:
"I'm always interested in new opportunities and exciting projects. Whether you have a question or just want to say hi, feel free to reach out!",
buttonText: 'Contact via Email',
socialTitle: 'Other ways to connect',
availabilityText: 'Available for projects',
responseTimeLabel: 'Response time:',
responseTimeValue: 'Within 24 hours',
preferredContactLabel: 'Preferred contact:',
preferredContactValue: 'Email',
locationLabel: 'Location:',
locationValue: 'Germany',
emailSubject: "Portfolio Inquiry - Let's discuss your project",
emailBody:
'Hello Sascha,\n\nI visited your portfolio and would like to discuss a potential project or collaboration.\n\nBest regards,',
emailAnnouncement: 'Opening email client with pre-filled message',
socialAnnouncement: 'Opening {platform} profile in new tab',
};

View File

@ -0,0 +1,7 @@
export const footer = {
imprintText: 'Impressum und Datenschutz',
copyrightText: 'All rights reserved.',
githubAriaLabel: 'GitHub Profile',
linkedinAriaLabel: 'LinkedIn Profile',
emailAriaLabel: 'Send Email',
};

View File

@ -0,0 +1,14 @@
export const hero = {
title: 'Everyone deserves a smooth online experience',
description:
'I help you build accessible websites that make every visitor feel included, in line with the Accessibility Act.',
primaryButtonText: 'View Projects',
secondaryButtonText: 'Download CV',
tertiaryButtonText: 'Get in Touch',
statItems: [
{ label: 'Years of Experience', value: '10+' },
{ label: 'Responsive Design', value: '100%' },
{ label: 'Technologies Used', value: '10+' },
{ label: 'Frameworks', value: '3+' },
],
};

View File

@ -0,0 +1,154 @@
export const imprint = {
title: 'Impressum und Datenschutz',
subtitle: 'Legal Information',
companyInfoTitle: 'Angaben gemäß § 5 DDG',
contactTitle: 'Kontakt',
responsibilityTitle: 'Vertretungsberechtigt',
disclaimerTitle: 'Haftungsausschluss',
contentLiabilityTitle: 'Haftung für Inhalte',
contentLiabilityText: [
'Als Diensteanbieter sind wir gemäß § 7 Abs.1 DDG für eigene Inhalte auf diesen Seiten nach den allgemeinen Gesetzen verantwortlich. Nach §§ 8 bis 10 TMG sind wir als Diensteanbieter jedoch nicht unter der Verpflichtung, übermittelte oder gespeicherte fremde Informationen zu überwachen oder nach Umständen zu forschen, die auf eine rechtswidrige Tätigkeit hinweisen.',
'Verpflichtungen zur Entfernung oder Sperrung der Nutzung von Informationen nach den allgemeinen Gesetzen bleiben hiervon unberührt. Eine diesbezügliche Haftung ist jedoch erst ab dem Zeitpunkt der Kenntnis einer konkreten Rechtsverletzung möglich. Bei Bekanntwerden von entsprechenden Rechtsverletzungen werden wir diese Inhalte umgehend entfernen.',
],
copyrightTitle: 'Urheberrecht',
copyrightText: [
'Die durch die Seitenbetreiber erstellten Inhalte und Werke auf diesen Seiten unterliegen dem deutschen Urheberrecht. Die Vervielfältigung, Bearbeitung, Verbreitung und jede Art der Verwertung außerhalb der Grenzen des Urheberrechtes bedürfen der schriftlichen Zustimmung des jeweiligen Autors bzw. Erstellers.',
'Downloads und Kopien dieser Seite sind nur für den privaten, nicht kommerziellen Gebrauch gestattet. Soweit die Inhalte auf dieser Seite nicht vom Betreiber erstellt wurden, werden die Urheberrechte Dritter beachtet. Insbesondere werden Inhalte Dritter als solche gekennzeichnet. Sollten Sie trotzdem auf eine Urheberrechtsverletzung aufmerksam werden, bitten wir um einen entsprechenden Hinweis. Bei Bekanntwerden von Rechtsverletzungen werden wir derartige Inhalte umgehend entfernen.',
],
privacyTitle: 'Datenschutz',
privacyText:
'Die Nutzung unserer Webseite ist in der Regel ohne Angabe personenbezogener Daten möglich. Soweit auf unseren Seiten personenbezogene Daten (beispielsweise Name, Anschrift oder E-Mail-Adressen) erhoben werden, erfolgt dies, soweit möglich, stets auf freiwilliger Basis. Diese Daten werden ohne Ihre ausdrückliche Zustimmung nicht an Dritte weitergegeben.',
detailedPrivacyPolicy: {
title: 'Datenschutzerklärung',
introduction:
'Mit dieser Datenschutzerklärung möchten wir Sie über Art, Umfang und Zweck der Verarbeitung von personenbezogenen Daten (im Folgenden auch nur als "Daten" bezeichnet) aufklären. Personenbezogene Daten sind alle Daten, die einen persönlichen Bezug zu Ihnen aufweisen, z. B. Name, Adresse, E-Mail-Adresse oder Ihr Nutzerverhalten. Die Datenschutzerklärung gilt für alle von uns vorgenommene Daten-Verarbeitungsvorgänge sowohl im Rahmen unserer Kerntätigkeit als auch für die von uns vorgehaltenen Online-Medien.',
responsibleTitle:
'Wer bei uns für die Datenverarbeitung verantwortlich ist',
responsibleText: 'Verantwortlich für die Datenverarbeitung ist:',
responsibleContact:
'Sascha Bach\nAlt-Tempelhof 15\n12099 Berlin\nDE\n01799364867\ninfo@sascha-bach.de',
dataProtectionOfficerTitle:
'Kontaktdaten unseres Datenschutzbeauftragten',
dataProtectionOfficerText:
'Unseren Datenschutzbeauftragten können Sie per E-Mail unter info [at] sascha-bach.de oder unter unserer Postadresse mit dem Zusatz „an den Datenschutzbeauftragten" erreichen.',
rightsTitle: 'Ihre Rechte nach der DSGVO',
rightsIntro:
'Nach der DSGVO stehen Ihnen die nachfolgend aufgeführten Rechte zu, die Sie jederzeit bei dem in Ziffer 1. dieser Datenschutzerklärung genannten Verantwortlichen geltend machen können:',
rights: [
{
title: 'Recht auf Auskunft',
text: 'Sie haben das Recht, von uns Auskunft darüber zu verlangen, ob und welche Daten wir von Ihnen verarbeiten.',
},
{
title: 'Recht auf Berichtigung',
text: 'Sie haben das Recht, die Berichtigung unrichtiger oder Vervollständigung unvollständiger Daten zu verlangen.',
},
{
title: 'Recht auf Löschung',
text: 'Sie haben das Recht, die Löschung Ihrer Daten zu verlangen.',
},
{
title: 'Recht auf Einschränkung',
text: 'Sie haben in bestimmten Fällen das Recht zu verlangen, dass wir Ihre Daten nur noch eingeschränkt bearbeiten.',
},
{
title: 'Recht auf Datenübertragbarkeit',
text: 'Sie haben das Recht zu verlangen, dass wir Ihnen oder einem anderen Verantwortlichen Ihre Daten in einem strukturierten, gängigen und maschinenlesebaren Format übermitteln.',
},
{
title: 'Beschwerderecht',
text: 'Sie haben das Recht, sich bei einer Aufsichtsbehörde zu beschweren. Zuständig ist die Aufsichtsbehörde Ihres üblichen Aufenthaltsortes, Ihres Arbeitsplatzes oder unseres Firmensitzes.',
},
],
revocationTitle: 'Widerrufsrecht',
revocationText:
'Sie haben das Recht, die von Ihnen erteilte Einwilligung zur Datenverarbeitung jederzeit zu widerrufen.',
objectionTitle: 'Widerspruchsrecht',
objectionText:
'Sie haben das Recht, jederzeit gegen die Verarbeitung Ihrer Daten, die wir auf unser berechtigtes Interesse nach Art. 6 Abs. 1 lit. f DSGVO stützen, Widerspruch einzulegen. Sofern Sie von Ihrem Widerspruchsrecht Gebrauch machen, bitten wir Sie um die Darlegung der Gründe. Wir werden Ihre personenbezogenen Daten dann nicht mehr verarbeiten, es sei denn, wir können Ihnen gegenüber nachweisen, dass zwingende schutzwürdige Gründe an der Datenverarbeitung Ihre Interessen und Rechte überwiegen.',
objectionHighlight:
'Unabhängig vom vorstehend Gesagten, haben Sie das jederzeitige Recht, der Verarbeitung Ihrer personenbezogenen Daten für Zwecke der Werbung und Datenanalyse zu widersprechen.',
objectionContact:
'Ihren Widerspruch richten Sie bitte an die oben angegebene Kontaktadresse des Verantwortlichen.',
dataDeletionTitle: 'Wann löschen wir Ihre Daten?',
dataDeletionIntro:
'Wir löschen Ihre Daten dann, wenn wir diese nicht mehr brauchen oder Sie uns dies vorgeben. Das bedeutet, dass - sofern sich aus den einzelnen Datenschutzhinweisen dieser Datenschutzerklärung nichts anderes ergibt - wir Ihre Daten löschen,',
dataDeletionReasons: [
'wenn der Zweck der Datenverarbeitung weggefallen ist und damit die jeweilige in den einzelnen Datenschutzhinweisen genannte Rechtsgrundlage nicht mehr besteht, also bspw.',
'nach Beendigung der zwischen uns bestehenden vertraglichen oder mitgliedschaftlichen Beziehungen (Art. 6 Abs. 1 lit. a DSGVO) oder',
'nach Wegfall unseres berechtigten Interesses an der weiteren Verarbeitung oder Speicherung Ihrer Daten (Art. 6 Abs. 1 lit. f DSGVO),',
'wenn Sie von Ihrem Widerrufsrecht Gebrauch machen und keine anderweitige gesetzliche Rechtsgrundlage für die Verarbeitung im Sinne von Art. 6 Abs. 1 lit. b-f DSGVO eingreift,',
'wenn Sie vom Ihrem Widerspruchsrecht Gebrauch machen und der Löschung keine zwingenden schutzwürdigen Gründe entgegenstehen.',
],
retentionText:
'Sofern wir (bestimmte Teile) Ihre(r) Daten jedoch noch für andere Zwecke vorhalten müssen, weil dies etwa steuerliche Aufbewahrungsfristen (in der Regel 6 Jahre für Geschäftskorrespondenz bzw. 10 Jahre für Buchungsbelege) oder die Geltendmachung, Ausübung oder Verteidigung von Rechtsansprüchen aus vertraglichen Beziehungen (bis zu vier Jahren) erforderlich machen oder die Daten zum Schutz der Rechte einer anderen natürlichen oder juristischen Person gebraucht werden, löschen wir (den Teil) Ihre(r) Daten erst nach Ablauf dieser Fristen. Bis zum Ablauf dieser Fristen beschränken wir die Verarbeitung dieser Daten jedoch auf diese Zwecke (Erfüllung der Aufbewahrungspflichten).',
webhostingTitle: 'Webhosting',
webhostingText:
'Wir bedienen uns zum Vorhalten unserer Internetseiten eines Anbieters, auf dessen Server unsere Internetseiten gespeichert und für den Abruf im Internet verfügbar gemacht werden (Hosting). Hierbei können von dem Anbieter all diejenigen über den von Ihnen genutzten Browser übertragenen Daten verarbeitet werden, die bei der Nutzung unserer Internetseiten anfallen. Hierzu gehören insbesondere Ihre IP-Adresse, die der Anbieter benötigt, um unser Online-Angebot an den von Ihnen genutzten Browser ausliefern zu können sowie sämtliche von Ihnen über unsere Internetseite getätigten Eingaben. Daneben kann der von uns genutzte Anbieter',
webhostingDataTypes: [
'das Datum und die Uhrzeit des Zugriffs auf unsere Internetseite',
'Zeitzonendifferenz zur Greenwich Mean Time (GMT)',
'Zugriffsstatus (HTTP-Status)',
'die übertragene Datenmenge',
'der Internet-Service-Provider des zugreifenden Systems',
'der von Ihnen verwendete Browsertyp und dessen Version',
'das von Ihnen verwendete Betriebssystem',
'die Internetseite, von welcher Sie gegebenenfalls auf unsere Internetseite gelangt sind',
'die Seiten bzw. Unterseiten, welche Sie auf unserer Internetseite besuchen.',
],
webhostingPurpose:
'erheben. Die vorgenannten Daten werden als Logfiles auf den Servern unseres Anbieters gespeichert. Dies ist erforderlich, um die Stabilität und Sicherheit des Betriebs unserer Internetseite zu gewährleisten.',
webhostingDataCategories: {
affectedData: 'Betroffene Daten:',
affectedDataList: [
'Inhaltsdaten (bspw. Posts, Fotos, Videos)',
'Nutzungsdaten (bspw. Zugriffszeiten, angeklickte Webseiten)',
'Kommunikationsdaten (bspw. Informationen über das genutzte Gerät, IP-Adresse)',
],
affectedPersons: 'Betroffene Personen: Nutzer unserer Internetpräsenz',
processingPurpose:
'Verarbeitungszweck: Ausspielen unserer Internetseiten, Gewährleistung des Betriebs unserer Internetseiten',
legalBasis:
'Rechtsgrundlage: Berechtigtes Interesse, Art. 6 Abs. 1 lit. f DSGVO',
provider: 'Von uns beauftragte(r) Webhoster:',
},
hostingProvider: {
name: 'Bitpalast GmbH',
address: 'Postfach 19 15 64, D-14005 Berlin, Deutschland',
website: 'https://preiswerter-webserver-de.bitpalast.net/',
},
contactTitle: 'Kontaktaufnahme',
contactText:
'Soweit Sie uns über E-Mail, Soziale Medien, Telefon, Fax, Post, unser Kontaktformular oder sonstwie ansprechen und uns hierbei personenbezogene Daten wie Ihren Namen, Ihre Telefonnummer oder Ihre E-Mail-Adresse zur Verfügung stellen oder weitere Angaben zur Ihrer Person oder Ihrem Anliegen machen, verarbeiten wir diese Daten zur Beantwortung Ihrer Anfrage im Rahmen des zwischen uns bestehenden vorvertraglichen oder vertraglichen Beziehungen.',
contactDataCategories: {
affectedData: [
'Bestandsdaten (bspw. Namen, Adressen)',
'Kontakdaten (bspw. E-Mail-Adresse, Telefonnummer, Postanschrift)',
'Inhaltsdaten (Texte, Fotos, Videos)',
'Vertragsdaten (bspw. Vertragsgegenstand, Vertragsdauer)',
],
affectedPersons:
'Interessenten, Kunden, Geschäfts- und Vertragspartner',
processingPurpose:
'Kommunikation sowie Beantwortung von Kontaktanfragen, Büro und Organisationsverfahren',
legalBasis:
'Vertragserfüllung und vorvertragliche Anfragen, Art. 6 Abs. 1 lit. b DSGVO, berechtigtes Interesse, Art. 6 Abs. 1 lit. f DSGVO',
},
securityTitle: 'Sicherheitsmaßnahmen',
securityText:
'Wir treffen im Übrigen technische und organisatorische Sicherheitsmaßnahmen nach dem Stand der Technik, um die Vorschriften der Datenschutzgesetze einzuhalten und Ihre Daten gegen zufällige oder vorsätzliche Manipulationen, teilweisen oder vollständigen Verlust, Zerstörung oder gegen den unbefugten Zugriff Dritter zu schützen.',
changesTitle: 'Aktualität und Änderung dieser Datenschutzerklärung',
changesText:
'Diese Datenschutzerklärung ist aktuell gültig und hat den Stand September 2025. Aufgrund geänderter gesetzlicher bzw. behördlicher Vorgaben kann es notwendig werden, diese Datenschutzerklärung anzupassen.',
disclaimer:
'Diese Datenschutzerklärung wurde mit Hilfe des Datenschutz-Generators von SOS Recht erstellt. SOS Recht ist ein Angebot der Mueller.legal Rechtsanwälte Partnerschaft mit Sitz in Berlin.',
},
address: {
street: 'Alt-Tempelhof 15',
city: '12099 Berlin',
country: 'Deutschland',
},
contact: {
phone: '+49 (0) 179 9364867',
},
};

View File

@ -0,0 +1,293 @@
import { navigation } from './navigation';
import { themeToggle } from '../../theme-toggle';
import { footer } from './footer';
import { hero } from './hero';
import { about } from './about';
import { services } from './services';
import { skills } from './skills';
import { projects } from './projects';
import { certifications } from './certifications';
import { contact } from './contact';
import { imprint } from './imprint';
import { privacyPolicy } from './privacy-policy';
import { accessibility } from './accessibility';
// TypeScript interfaces for type safety
export interface TextConfig {
// Navigation
navigation: {
menuItems: string[];
mobileMenuAriaLabel: string;
};
// Theme Toggle
themeToggle: {
lightIcon: string;
darkIcon: string;
lightModeText: string;
darkModeText: string;
};
// Footer
footer: {
imprintText: string;
copyrightText: string;
githubAriaLabel: string;
linkedinAriaLabel: string;
emailAriaLabel: string;
};
// Hero Section
hero: {
title: string;
description: string;
primaryButtonText: string;
secondaryButtonText: string;
tertiaryButtonText: string;
statItems: Array<{
label: string;
value: string;
}>;
};
// About Section
about: {
title: string;
subtitle: string;
name: string;
bio: string[];
featureCards: Array<{
title: string;
description: string;
}>;
};
// Services Section
services: {
title: string;
subtitle: string;
serviceItems: Array<{
title: string;
description: string;
}>;
};
// Skills Section
skills: {
title: string;
subtitle: string;
skillCategories: Array<{
title: string;
skills: Array<{
skill: string;
value: number;
}>;
}>;
};
// Projects Section
projects: {
title: string;
subtitle: string;
codeButtonText: string;
liveButtonText: string;
projectItems: Array<{
id: number;
title: string;
description: string;
}>;
};
// Certifications Section
certifications: {
title: string;
subtitle: string;
certificationItems: Array<{
name: string;
issuer: string;
}>;
};
// Contact Section
contact: {
title: string;
subtitle: string;
connectTitle: string;
connectDescription: string;
buttonText: string;
socialTitle: string;
availabilityText: string;
responseTimeLabel: string;
responseTimeValue: string;
preferredContactLabel: string;
preferredContactValue: string;
locationLabel: string;
locationValue: string;
emailSubject: string;
emailBody: string;
emailAnnouncement: string;
socialAnnouncement: string;
};
// Imprint Page
imprint: {
title: string;
subtitle: string;
companyInfoTitle: string;
contactTitle: string;
responsibilityTitle: string;
disclaimerTitle: string;
contentLiabilityTitle: string;
contentLiabilityText: string[];
copyrightTitle: string;
copyrightText: string[];
privacyTitle: string;
privacyText: string;
detailedPrivacyPolicy: {
title: string;
introduction: string;
responsibleTitle: string;
responsibleText: string;
responsibleContact: string;
dataProtectionOfficerTitle: string;
dataProtectionOfficerText: string;
rightsTitle: string;
rightsIntro: string;
rights: Array<{ title: string; text: string }>;
revocationTitle: string;
revocationText: string;
objectionTitle: string;
objectionText: string;
objectionHighlight: string;
objectionContact: string;
dataDeletionTitle: string;
dataDeletionIntro: string;
dataDeletionReasons: string[];
retentionText: string;
webhostingTitle: string;
webhostingText: string;
webhostingDataTypes: string[];
webhostingPurpose: string;
webhostingDataCategories: {
affectedData: string;
affectedDataList: string[];
affectedPersons: string;
processingPurpose: string;
legalBasis: string;
provider: string;
};
hostingProvider: {
name: string;
address: string;
website: string;
};
contactTitle: string;
contactText: string;
contactDataCategories: {
affectedData: string[];
affectedPersons: string;
processingPurpose: string;
legalBasis: string;
};
securityTitle: string;
securityText: string;
changesTitle: string;
changesText: string;
disclaimer: string;
};
address: {
street: string;
city: string;
country: string;
};
contact: {
phone: string;
};
};
// Privacy Policy Page
privacyPolicy: {
title: string;
lastUpdated: string;
dataCollectionTitle: string;
dataCollectionText: string;
dataCollectionList: string[];
purposeTitle: string;
purposeText: string;
providerTitle: string;
providerText: string;
retentionTitle: string;
retentionText: string;
rightsTitle: string;
rightsText: string;
rightsList: string[];
contactTitle: string;
contactText: string;
contactEmail: string;
legalBasisTitle: string;
legalBasisText: string;
};
// Accessibility and Navigation
accessibility: {
skipLinks: {
skipToHero: string;
skipToAbout: string;
skipToServices: string;
skipToSkills: string;
skipToProjects: string;
skipToCertifications: string;
skipToContact: string;
};
navigation: {
keyboardHelp: string;
keyboardHelpExpanded: string[];
useTabToNavigate: string;
useTabToNavigateCards: string;
useTabToNavigateOptions: string;
closeKeyboardHelp: string;
};
screenReader: {
professionalStatistics: string;
projectCard: string;
viewSourceCode: string;
viewLiveDemo: string;
downloadResume: string;
downloadResumeDescription: string;
keyboardNavigationHelp: string;
themeSwitchButton: string;
switchToTheme: string;
certificationCard: string;
serviceCard: string;
skillBadge: string;
};
ariaLabels: {
mainNavigation: string;
socialLinks: string;
themeToggle: string;
mobileMenuToggle: string;
contactForm: string;
projectsGrid: string;
skillsGrid: string;
certificationsGrid: string;
servicesGrid: string;
};
};
}
// English text configuration
export const en: TextConfig = {
navigation,
themeToggle,
footer,
hero,
about,
services,
skills,
projects,
certifications,
contact,
imprint,
privacyPolicy,
accessibility,
};

View File

@ -0,0 +1,11 @@
export const navigation = {
menuItems: [
'About',
'Services',
'Skills',
'Certifications',
'Projects',
'Contact',
],
mobileMenuAriaLabel: 'Menü öffnen',
};

View File

@ -0,0 +1,36 @@
export const privacyPolicy = {
title: 'Privacy Policy (GDPR/DSGVO)',
lastUpdated: 'Last updated: November 2025',
introTitle: 'Overview',
introText:
'This website is a static portfolio website that does not collect, process, or store any personal data. No cookies, tracking, or analytics tools are used.',
dataCollectionTitle: 'Data Collection',
dataCollectionText:
'This website does not collect any personal data. When you click on contact links (email, LinkedIn, GitHub), you will be redirected to external services that have their own privacy policies:',
dataCollectionList: [
'Email: Opens your default email client (no data transmitted through this website)',
'LinkedIn: Redirects to LinkedIn (subject to LinkedIn\'s privacy policy)',
'GitHub: Redirects to GitHub (subject to GitHub\'s privacy policy)',
],
webhostingTitle: 'Web Hosting',
webhostingText:
'This website is hosted by Bitpalast GmbH. The hosting provider may collect technical data such as IP addresses, browser type, and access times in server log files. This data is necessary for the technical operation and security of the website.',
webhostingProvider: 'Hosting Provider:',
webhostingProviderName: 'Bitpalast GmbH',
webhostingProviderAddress: 'Postfach 19 15 64, D-14005 Berlin, Germany',
webhostingProviderWebsite: 'https://preiswerter-webserver-de.bitpalast.net/',
rightsTitle: 'Your Rights Under GDPR',
rightsText: 'Under the GDPR, you have the following rights:',
rightsList: [
'Right to information about any data stored',
'Right to rectification of incorrect data',
'Right to deletion of your data',
'Right to restriction of processing',
'Right to data portability',
'Right to object to data processing',
],
contactTitle: 'Contact for Privacy Matters',
contactText:
'For any privacy-related questions, please contact:',
contactEmail: 'info [at] sascha-bach.de',
};

View File

@ -0,0 +1,32 @@
export const projects = {
title: 'Featured Projects',
subtitle: 'A showcase of my work and personal projects',
codeButtonText: 'Code',
liveButtonText: 'Live',
projectItems: [
{
id: 1,
title: 'Portfolio Website',
description:
'A personal portfolio website to showcase my skills, projects, and experience.',
},
{
id: 2,
title: 'ErgoVR',
description:
'A virtual reality application for analysis of motion sickness in VR environments from 2015.',
},
{
id: 3,
title: 'Icarace',
description:
'Participated in the development of a web-platform for a fitness racing game for Icaros GmbH until 2018.',
},
{
id: 4,
title: 'Danca Alegria Association Website',
description:
'Assist in volunatary changes to accomodate WCAG 2.1 Level AA accessibility.',
}
],
};

View File

@ -0,0 +1,36 @@
export const services = {
title: 'Services',
subtitle: 'Comprehensive web development solutions tailored to your needs',
serviceItems: [
{
title: 'Web Accessibility',
description:
'Building inclusive and accessible web applications by implementing Web Content Accessibility Guidelines, semantic HTML, and support for assistive technologies.',
},
{
title: 'Responsive Web Design',
description:
'Creating mobile-first, responsive interfaces that provide optimal viewing experiences across all devices and screen sizes.',
},
{
title: 'Cross-Browser Testing',
description:
'Ensuring consistent functionality and appearance across all major browsers including Chrome, Firefox, Safari, and Edge.',
},
{
title: 'Frontend Architecture',
description:
'Designing and implementing scalable frontend architectures with component-based development and modern tooling.',
},
{
title: 'Performance Optimization',
description:
'Optimizing web applications for speed and efficiency through code splitting, lazy loading, and performance best practices.',
},
{
title: 'Testing & Quality Assurance',
description:
'Implementing comprehensive testing strategies using Cypress for end-to-end testing and ensuring code quality.',
},
],
};

View File

@ -0,0 +1,60 @@
export const skills = {
title: 'Skills & Technologies',
subtitle: 'Technical expertise across the full development stack',
skillCategories: [
{
title: 'Web Frameworks',
skills: [
{ skill: 'Angular', value: 90 },
{ skill: 'React', value: 70 },
{ skill: 'Next.js', value: 70 },
{ skill: 'TypeScript', value: 95 },
],
},
{
title: 'Styling & Design',
skills: [
{ skill: 'Accessibility (a11y) Standards', value: 90 },
{ skill: 'SCSS/Sass', value: 95 },
{ skill: 'CSS3', value: 95 },
{ skill: 'Bootstrap', value: 80 },
],
},
{
title: 'Backend Development',
skills: [
{ skill: 'Node.js', value: 75 },
{ skill: 'Express.js', value: 70 },
{ skill: 'REST APIs', value: 85 },
{ skill: 'Database Design', value: 70 },
],
},
{
title: 'Development Tools',
skills: [
{ skill: 'Git & GitHub', value: 90 },
{ skill: 'VS Code', value: 95 },
{ skill: 'npm/yarn', value: 85 },
{ skill: 'Webpack/Vite', value: 75 },
],
},
{
title: 'Testing & Quality',
skills: [
{ skill: 'Unit Testing', value: 80 },
{ skill: 'E2E Testing', value: 70 },
{ skill: 'Code Review', value: 85 },
{ skill: 'Performance Optimization', value: 75 },
],
},
{
title: 'AI-Tools',
skills: [
{ skill: 'GitHub Copilot', value: 95 },
{ skill: 'ChatGPT', value: 90 },
{ skill: 'Claude', value: 85 },
{ skill: 'AI-Assisted Development', value: 90 },
],
},
],
};

View File

@ -22,16 +22,6 @@ export const personalConfig = {
}, },
}, },
projectsUrls: {
portfolio: 'https://www.sascha-bach.de',
},
// Project Repository
gitProjects: {
portfolio: 'https://github.com/LuciusShadow/portfolio-page',
ergoVR: 'https://github.com/LuciusShadow/ErgoVR',
},
// Professional Details // Professional Details
location: 'Germany', location: 'Germany',
timezone: 'CET/CEST', timezone: 'CET/CEST',

View File

@ -0,0 +1,53 @@
import portfolioImage from '../assets/portfolio.PNG';
import ergoVRImage from '../assets/ErgoVR.PNG';
import icaraceImage from '../assets/icarace.PNG';
import dancaAlegriaImage from '../assets/Danca-Alegria.png';
import { personalConfig } from './personal';
import { projects } from './locales/en/projects';
import type { Project } from '../data/Project';
const getProjectText = (id: number) => {
const item = projects.projectItems.find(p => p.id === id);
if (!item) throw new Error(`Project with id ${id} not found`);
return item;
};
export const projectsData: Project[] = [
{
id: 1,
title: getProjectText(1).title,
description: getProjectText(1).description,
image: portfolioImage,
technologies: ['React', 'TypeScript', 'SCSS', 'Github Copilot'],
year: '2025',
github: 'https://github.com/LuciusShadow/portfolio-page',
live: 'https://www.sascha-bach.de',
},
{
id: 2,
title: getProjectText(2).title,
description: getProjectText(2).description,
image: ergoVRImage,
technologies: ['Unity3D', 'C#', 'Oculus SDK'],
year: '2015',
github: 'https://github.com/LuciusShadow/ErgoVR',
},
{
id: 3,
title: getProjectText(3).title,
description: getProjectText(3).description,
image: icaraceImage,
technologies: ['Angular 4', 'Typescript', 'HTML', 'CSS'],
year: '2018',
github: 'https://github.com/LuciusShadow/ErgoVR'
},
{
id: 4,
title: getProjectText(4).title,
description: getProjectText(4).description,
image: dancaAlegriaImage,
technologies: ['Wordpress', 'PHP', 'HTML', 'CSS'],
year: '2025',
live: 'https://www.danca-alegria.de'
}
];

View File

@ -1,765 +1,44 @@
export interface TextConfig { /**
// Navigation * Main text configuration file
navigation: { *
menuItems: string[]; * This file re-exports the localized text configurations and provides
mobileMenuAriaLabel: string; * backward compatibility with the existing API.
}; *
* For multi-language support, add new locale imports and update the
* getTexts() function to select the appropriate language based on
* user preference or browser settings.
*/
// Theme Toggle import { type TextConfig, en } from './locales/en';
themeToggle: {
lightIcon: string;
darkIcon: string;
lightModeText: string;
darkModeText: string;
};
// Footer // Re-export the TextConfig type for backward compatibility
footer: { export type { TextConfig };
imprintText: string;
copyrightText: string;
githubAriaLabel: string;
linkedinAriaLabel: string;
emailAriaLabel: string;
};
// Hero Section // Re-export English texts as default for backward compatibility
hero: { export const defaultTexts: TextConfig = en;
title: string;
description: string;
primaryButtonText: string;
secondaryButtonText: string;
tertiaryButtonText: string;
statItems: Array<{
label: string;
value: string;
}>;
};
// About Section /**
about: { * Get the current text configuration based on locale
title: string; *
subtitle: string; * @param _locale - Optional locale string (e.g., 'en', 'de') - Reserved for future use
name: string; * @returns TextConfig object with all localized strings
bio: string[]; *
featureCards: Array<{ * Future implementation:
title: string; * - Accept locale parameter
description: string; * - Check localStorage/sessionStorage for user preference
}>; * - Fall back to browser language
}; * - Return corresponding locale configuration
*/
// Services Section export const getTexts = (_locale?: string): TextConfig => {
services: { // For now, return English texts
title: string; // Future: implement language switching logic here
subtitle: string; // Example:
serviceItems: Array<{ // const userLocale = _locale || localStorage.getItem('locale') || navigator.language.split('-')[0];
title: string; // switch (userLocale) {
description: string; // case 'de': return de;
}>; // case 'en':
}; // default: return en;
// }
// Skills Section
skills: { return en;
title: string;
subtitle: string;
skillCategories: Array<{
title: string;
skills: Array<{
skill: string;
value: number;
}>;
}>;
};
// Projects Section
projects: {
title: string;
subtitle: string;
codeButtonText: string;
liveButtonText: string;
projectItems: Array<{
title: string;
description: string;
}>;
};
// Certifications Section
certifications: {
title: string;
subtitle: string;
certificationItems: Array<{
name: string;
issuer: string;
}>;
};
// Contact Section
contact: {
title: string;
subtitle: string;
connectTitle: string;
connectDescription: string;
buttonText: string;
socialTitle: string;
availabilityText: string;
responseTimeLabel: string;
responseTimeValue: string;
preferredContactLabel: string;
preferredContactValue: string;
locationLabel: string;
locationValue: string;
emailSubject: string;
emailBody: string;
emailAnnouncement: string;
socialAnnouncement: string;
};
// Imprint Page
imprint: {
title: string;
subtitle: string;
companyInfoTitle: string;
contactTitle: string;
responsibilityTitle: string;
disclaimerTitle: string;
contentLiabilityTitle: string;
contentLiabilityText: string[];
copyrightTitle: string;
copyrightText: string[];
privacyTitle: string;
privacyText: string;
detailedPrivacyPolicy: {
title: string;
introduction: string;
responsibleTitle: string;
responsibleText: string;
responsibleContact: string;
dataProtectionOfficerTitle: string;
dataProtectionOfficerText: string;
rightsTitle: string;
rightsIntro: string;
rights: Array<{ title: string; text: string }>;
revocationTitle: string;
revocationText: string;
objectionTitle: string;
objectionText: string;
objectionHighlight: string;
objectionContact: string;
dataDeletionTitle: string;
dataDeletionIntro: string;
dataDeletionReasons: string[];
retentionText: string;
webhostingTitle: string;
webhostingText: string;
webhostingDataTypes: string[];
webhostingPurpose: string;
webhostingDataCategories: {
affectedData: string;
affectedDataList: string[];
affectedPersons: string;
processingPurpose: string;
legalBasis: string;
provider: string;
};
hostingProvider: {
name: string;
address: string;
website: string;
};
contactTitle: string;
contactText: string;
contactDataCategories: {
affectedData: string[];
affectedPersons: string;
processingPurpose: string;
legalBasis: string;
};
securityTitle: string;
securityText: string;
changesTitle: string;
changesText: string;
disclaimer: string;
};
address: {
street: string;
city: string;
country: string;
};
contact: {
phone: string;
};
};
// Privacy Policy Page
privacyPolicy: {
title: string;
lastUpdated: string;
dataCollectionTitle: string;
dataCollectionText: string;
dataCollectionList: string[];
purposeTitle: string;
purposeText: string;
providerTitle: string;
providerText: string;
retentionTitle: string;
retentionText: string;
rightsTitle: string;
rightsText: string;
rightsList: string[];
contactTitle: string;
contactText: string;
contactEmail: string;
legalBasisTitle: string;
legalBasisText: string;
};
// Accessibility and Navigation
accessibility: {
skipLinks: {
skipToHero: string;
skipToAbout: string;
skipToServices: string;
skipToSkills: string;
skipToProjects: string;
skipToCertifications: string;
skipToContact: string;
};
navigation: {
keyboardHelp: string;
keyboardHelpExpanded: string[];
useTabToNavigate: string;
useTabToNavigateCards: string;
useTabToNavigateOptions: string;
closeKeyboardHelp: string;
};
screenReader: {
professionalStatistics: string;
projectCard: string;
viewSourceCode: string;
viewLiveDemo: string;
downloadResume: string;
downloadResumeDescription: string;
keyboardNavigationHelp: string;
themeSwitchButton: string;
switchToTheme: string;
certificationCard: string;
serviceCard: string;
skillBadge: string;
};
ariaLabels: {
mainNavigation: string;
socialLinks: string;
themeToggle: string;
mobileMenuToggle: string;
contactForm: string;
projectsGrid: string;
skillsGrid: string;
certificationsGrid: string;
servicesGrid: string;
};
};
}
// Default text configuration (English/German mix as currently used)
export const defaultTexts: TextConfig = {
navigation: {
menuItems: [
'About',
'Services',
'Skills',
'Certifications',
'Projects',
'Contact',
],
mobileMenuAriaLabel: 'Menü öffnen',
},
themeToggle: {
lightIcon: '🌙',
darkIcon: '☀️',
lightModeText: 'dark',
darkModeText: 'light',
},
footer: {
imprintText: 'Impressum und Datenschutz',
copyrightText: 'All rights reserved.',
githubAriaLabel: 'GitHub Profile',
linkedinAriaLabel: 'LinkedIn Profile',
emailAriaLabel: 'Send Email',
},
hero: {
title: 'Everyone deserves a smooth online experience',
description:
'I help you build accessible websites that make every visitor feel included, in line with the Accessibility Act.',
primaryButtonText: 'View Projects',
secondaryButtonText: 'Download CV',
tertiaryButtonText: 'Get in Touch',
statItems: [
{ label: 'Years of Experience', value: '10+' },
{ label: 'Responsive Design', value: '100%' },
{ label: 'Technologies Used', value: '10+' },
{ label: 'Frameworks', value: '3+' },
],
},
about: {
title: 'About Me',
subtitle:
'Passionate developer with a focus on clean code and innovative solutions.',
name: 'Sascha Bach',
bio: [
'I studied Computer Science at the Technical University of Darmstadt and graduated in 2016 with my bachelor thesis on Motion Sickness in Virtual Reality. Since then I have been working as a Software and Web Developer with a focus on Angular and React.',
'In my work I have seen that every company is different, but many face the same challenge: keeping their digital presence accessible, modern and future proof. A lot of businesses already know that accessibility is required by law, but in practice what matters most is that a website works for all users in a simple and enjoyable way.',
'My specialization is Web Accessibility (WCAG 2.1, BFSG, European Accessibility Act). I help companies update their websites and applications so they are compliant and at the same time easy to use for everyone. What my clients value is that I combine technical expertise in Angular, React and TypeScript with a human centered approach. Because accessibility is not just about code, it is about people.',
'If you are wondering whether your website already meets accessibility standards, or if you feel it is time to make your online presence more inclusive and legally compliant, I would be glad to support you. Often just a few clear steps can make a big difference, helping your website reach more people and stand out from the competition.',
"Let's connect and see how we can make your website barrier free, modern and ready for the future.",
],
featureCards: [
{
title: 'Web Development',
description:
'Building modern web applications with Angular and React frameworks, leveraging TypeScript and JavaScript for robust, scalable solutions.',
},
{
title: 'Responsive Design',
description:
'Creating pixel-perfect, responsive interfaces that work seamlessly across all devices and screen sizes.',
},
{
title: 'Cross-Browser Compatibility',
description:
'Ensuring consistent user experiences across all major browsers with thorough testing and optimization.',
},
{
title: 'Web Accessibility',
description:
'Implementing accessibility best practices to deliver websites that support screen readers, keyboard navigation, and assistive technologies.',
},
],
},
services: {
title: 'Services',
subtitle: 'Comprehensive web development solutions tailored to your needs',
serviceItems: [
{
title: 'Web Accessibility',
description:
'Building inclusive and accessible web applications by implementing Web Content Accessibility Guidelines, semantic HTML, and support for assistive technologies.',
},
{
title: 'Responsive Web Design',
description:
'Creating mobile-first, responsive interfaces that provide optimal viewing experiences across all devices and screen sizes.',
},
{
title: 'Cross-Browser Testing',
description:
'Ensuring consistent functionality and appearance across all major browsers including Chrome, Firefox, Safari, and Edge.',
},
{
title: 'Frontend Architecture',
description:
'Designing and implementing scalable frontend architectures with component-based development and modern tooling.',
},
{
title: 'Performance Optimization',
description:
'Optimizing web applications for speed and efficiency through code splitting, lazy loading, and performance best practices.',
},
{
title: 'Testing & Quality Assurance',
description:
'Implementing comprehensive testing strategies using Cypress for end-to-end testing and ensuring code quality.',
},
],
},
skills: {
title: 'Skills & Technologies',
subtitle: 'Technical expertise across the full development stack',
skillCategories: [
{
title: 'Web Frameworks',
skills: [
{ skill: 'Angular', value: 90 },
{ skill: 'React', value: 70 },
{ skill: 'Next.js', value: 70 },
{ skill: 'TypeScript', value: 95 },
],
},
{
title: 'Styling & Design',
skills: [
{ skill: 'Accessibility (a11y) Standards', value: 90 },
{ skill: 'SCSS/Sass', value: 95 },
{ skill: 'CSS3', value: 95 },
{ skill: 'Bootstrap', value: 80 },
],
},
{
title: 'Backend Development',
skills: [
{ skill: 'Node.js', value: 75 },
{ skill: 'Express.js', value: 70 },
{ skill: 'REST APIs', value: 85 },
{ skill: 'Database Design', value: 70 },
],
},
{
title: 'Development Tools',
skills: [
{ skill: 'Git & GitHub', value: 90 },
{ skill: 'VS Code', value: 95 },
{ skill: 'npm/yarn', value: 85 },
{ skill: 'Webpack/Vite', value: 75 },
],
},
{
title: 'Testing & Quality',
skills: [
{ skill: 'Unit Testing', value: 80 },
{ skill: 'E2E Testing', value: 70 },
{ skill: 'Code Review', value: 85 },
{ skill: 'Performance Optimization', value: 75 },
],
},
{
title: 'AI-Tools',
skills: [
{ skill: 'GitHub Copilot', value: 95 },
{ skill: 'ChatGPT', value: 90 },
{ skill: 'Claude', value: 85 },
{ skill: 'AI-Assisted Development', value: 90 },
],
},
],
},
projects: {
title: 'Featured Projects',
subtitle: 'A showcase of my work and personal projects',
codeButtonText: 'Code',
liveButtonText: 'Live',
projectItems: [
{
title: 'Portfolio Website',
description:
'A personal portfolio website to showcase my skills, projects, and experience.',
},
{
title: 'ErgoVR',
description:
'A virtual reality application for analysis of motion sickness in VR environments from 2015.',
},
{
title: 'Icarace',
description:
'Participated in the development of a web-platform for a fitness racing game for Icaros GmbH until 2018.',
},
],
},
certifications: {
title: 'Certifications & Achievements',
subtitle: 'My professional qualifications and continuous learning',
certificationItems: [
{
name: 'Practical Prompt Engineering Masterclass: Hands-On Learning',
issuer: 'Udemy Lecture - Asif Farooqi, Abdullah Dar',
},
{
name: 'Understanding Typescript',
issuer: 'Udemy Lecture - Maximilian Schwarzmüller',
},
{
name: 'Angular Step by Step',
issuer: 'Udemy Lecture - Shivprasad Koirala',
},
{
name: 'Angular and Typescript',
issuer: 'LinkedIn TestDome',
},
{
name: 'Bachelor of Science in Computer Science',
issuer: 'TU Darmstadt',
},
],
},
contact: {
title: 'Get In Touch',
subtitle: "Let's discuss your project and bring your ideas to life",
connectTitle: "Let's Connect",
connectDescription:
"I'm always interested in new opportunities and exciting projects. Whether you have a question or just want to say hi, feel free to reach out!",
buttonText: 'Contact via Email',
socialTitle: 'Other ways to connect',
availabilityText: 'Available for projects',
responseTimeLabel: 'Response time:',
responseTimeValue: 'Within 24 hours',
preferredContactLabel: 'Preferred contact:',
preferredContactValue: 'Email',
locationLabel: 'Location:',
locationValue: 'Germany',
emailSubject: "Portfolio Inquiry - Let's discuss your project",
emailBody:
'Hello Sascha,\n\nI visited your portfolio and would like to discuss a potential project or collaboration.\n\nBest regards,',
emailAnnouncement: 'Opening email client with pre-filled message',
socialAnnouncement: 'Opening {platform} profile in new tab',
},
imprint: {
title: 'Impressum und Datenschutz',
subtitle: 'Legal Information',
companyInfoTitle: 'Angaben gemäß § 5 DDG',
contactTitle: 'Kontakt',
responsibilityTitle: 'Vertretungsberechtigt',
disclaimerTitle: 'Haftungsausschluss',
contentLiabilityTitle: 'Haftung für Inhalte',
contentLiabilityText: [
'Als Diensteanbieter sind wir gemäß § 7 Abs.1 DDG für eigene Inhalte auf diesen Seiten nach den allgemeinen Gesetzen verantwortlich. Nach §§ 8 bis 10 TMG sind wir als Diensteanbieter jedoch nicht unter der Verpflichtung, übermittelte oder gespeicherte fremde Informationen zu überwachen oder nach Umständen zu forschen, die auf eine rechtswidrige Tätigkeit hinweisen.',
'Verpflichtungen zur Entfernung oder Sperrung der Nutzung von Informationen nach den allgemeinen Gesetzen bleiben hiervon unberührt. Eine diesbezügliche Haftung ist jedoch erst ab dem Zeitpunkt der Kenntnis einer konkreten Rechtsverletzung möglich. Bei Bekanntwerden von entsprechenden Rechtsverletzungen werden wir diese Inhalte umgehend entfernen.',
],
copyrightTitle: 'Urheberrecht',
copyrightText: [
'Die durch die Seitenbetreiber erstellten Inhalte und Werke auf diesen Seiten unterliegen dem deutschen Urheberrecht. Die Vervielfältigung, Bearbeitung, Verbreitung und jede Art der Verwertung außerhalb der Grenzen des Urheberrechtes bedürfen der schriftlichen Zustimmung des jeweiligen Autors bzw. Erstellers.',
'Downloads und Kopien dieser Seite sind nur für den privaten, nicht kommerziellen Gebrauch gestattet. Soweit die Inhalte auf dieser Seite nicht vom Betreiber erstellt wurden, werden die Urheberrechte Dritter beachtet. Insbesondere werden Inhalte Dritter als solche gekennzeichnet. Sollten Sie trotzdem auf eine Urheberrechtsverletzung aufmerksam werden, bitten wir um einen entsprechenden Hinweis. Bei Bekanntwerden von Rechtsverletzungen werden wir derartige Inhalte umgehend entfernen.',
],
privacyTitle: 'Datenschutz',
privacyText:
'Die Nutzung unserer Webseite ist in der Regel ohne Angabe personenbezogener Daten möglich. Soweit auf unseren Seiten personenbezogene Daten (beispielsweise Name, Anschrift oder E-Mail-Adressen) erhoben werden, erfolgt dies, soweit möglich, stets auf freiwilliger Basis. Diese Daten werden ohne Ihre ausdrückliche Zustimmung nicht an Dritte weitergegeben.',
detailedPrivacyPolicy: {
title: 'Datenschutzerklärung',
introduction:
'Mit dieser Datenschutzerklärung möchten wir Sie über Art, Umfang und Zweck der Verarbeitung von personenbezogenen Daten (im Folgenden auch nur als "Daten" bezeichnet) aufklären. Personenbezogene Daten sind alle Daten, die einen persönlichen Bezug zu Ihnen aufweisen, z. B. Name, Adresse, E-Mail-Adresse oder Ihr Nutzerverhalten. Die Datenschutzerklärung gilt für alle von uns vorgenommene Daten-Verarbeitungsvorgänge sowohl im Rahmen unserer Kerntätigkeit als auch für die von uns vorgehaltenen Online-Medien.',
responsibleTitle:
'Wer bei uns für die Datenverarbeitung verantwortlich ist',
responsibleText: 'Verantwortlich für die Datenverarbeitung ist:',
responsibleContact:
'Sascha Bach\nAlt-Tempelhof 15\n12099 Berlin\nDE\n01799364867\ninfo@sascha-bach.de',
dataProtectionOfficerTitle:
'Kontaktdaten unseres Datenschutzbeauftragten',
dataProtectionOfficerText:
'Unseren Datenschutzbeauftragten können Sie per E-Mail unter info [at] sascha-bach.de oder unter unserer Postadresse mit dem Zusatz „an den Datenschutzbeauftragten" erreichen.',
rightsTitle: 'Ihre Rechte nach der DSGVO',
rightsIntro:
'Nach der DSGVO stehen Ihnen die nachfolgend aufgeführten Rechte zu, die Sie jederzeit bei dem in Ziffer 1. dieser Datenschutzerklärung genannten Verantwortlichen geltend machen können:',
rights: [
{
title: 'Recht auf Auskunft',
text: 'Sie haben das Recht, von uns Auskunft darüber zu verlangen, ob und welche Daten wir von Ihnen verarbeiten.',
},
{
title: 'Recht auf Berichtigung',
text: 'Sie haben das Recht, die Berichtigung unrichtiger oder Vervollständigung unvollständiger Daten zu verlangen.',
},
{
title: 'Recht auf Löschung',
text: 'Sie haben das Recht, die Löschung Ihrer Daten zu verlangen.',
},
{
title: 'Recht auf Einschränkung',
text: 'Sie haben in bestimmten Fällen das Recht zu verlangen, dass wir Ihre Daten nur noch eingeschränkt bearbeiten.',
},
{
title: 'Recht auf Datenübertragbarkeit',
text: 'Sie haben das Recht zu verlangen, dass wir Ihnen oder einem anderen Verantwortlichen Ihre Daten in einem strukturierten, gängigen und maschinenlesebaren Format übermitteln.',
},
{
title: 'Beschwerderecht',
text: 'Sie haben das Recht, sich bei einer Aufsichtsbehörde zu beschweren. Zuständig ist die Aufsichtsbehörde Ihres üblichen Aufenthaltsortes, Ihres Arbeitsplatzes oder unseres Firmensitzes.',
},
],
revocationTitle: 'Widerrufsrecht',
revocationText:
'Sie haben das Recht, die von Ihnen erteilte Einwilligung zur Datenverarbeitung jederzeit zu widerrufen.',
objectionTitle: 'Widerspruchsrecht',
objectionText:
'Sie haben das Recht, jederzeit gegen die Verarbeitung Ihrer Daten, die wir auf unser berechtigtes Interesse nach Art. 6 Abs. 1 lit. f DSGVO stützen, Widerspruch einzulegen. Sofern Sie von Ihrem Widerspruchsrecht Gebrauch machen, bitten wir Sie um die Darlegung der Gründe. Wir werden Ihre personenbezogenen Daten dann nicht mehr verarbeiten, es sei denn, wir können Ihnen gegenüber nachweisen, dass zwingende schutzwürdige Gründe an der Datenverarbeitung Ihre Interessen und Rechte überwiegen.',
objectionHighlight:
'Unabhängig vom vorstehend Gesagten, haben Sie das jederzeitige Recht, der Verarbeitung Ihrer personenbezogenen Daten für Zwecke der Werbung und Datenanalyse zu widersprechen.',
objectionContact:
'Ihren Widerspruch richten Sie bitte an die oben angegebene Kontaktadresse des Verantwortlichen.',
dataDeletionTitle: 'Wann löschen wir Ihre Daten?',
dataDeletionIntro:
'Wir löschen Ihre Daten dann, wenn wir diese nicht mehr brauchen oder Sie uns dies vorgeben. Das bedeutet, dass - sofern sich aus den einzelnen Datenschutzhinweisen dieser Datenschutzerklärung nichts anderes ergibt - wir Ihre Daten löschen,',
dataDeletionReasons: [
'wenn der Zweck der Datenverarbeitung weggefallen ist und damit die jeweilige in den einzelnen Datenschutzhinweisen genannte Rechtsgrundlage nicht mehr besteht, also bspw.',
'nach Beendigung der zwischen uns bestehenden vertraglichen oder mitgliedschaftlichen Beziehungen (Art. 6 Abs. 1 lit. a DSGVO) oder',
'nach Wegfall unseres berechtigten Interesses an der weiteren Verarbeitung oder Speicherung Ihrer Daten (Art. 6 Abs. 1 lit. f DSGVO),',
'wenn Sie von Ihrem Widerrufsrecht Gebrauch machen und keine anderweitige gesetzliche Rechtsgrundlage für die Verarbeitung im Sinne von Art. 6 Abs. 1 lit. b-f DSGVO eingreift,',
'wenn Sie vom Ihrem Widerspruchsrecht Gebrauch machen und der Löschung keine zwingenden schutzwürdigen Gründe entgegenstehen.',
],
retentionText:
'Sofern wir (bestimmte Teile) Ihre(r) Daten jedoch noch für andere Zwecke vorhalten müssen, weil dies etwa steuerliche Aufbewahrungsfristen (in der Regel 6 Jahre für Geschäftskorrespondenz bzw. 10 Jahre für Buchungsbelege) oder die Geltendmachung, Ausübung oder Verteidigung von Rechtsansprüchen aus vertraglichen Beziehungen (bis zu vier Jahren) erforderlich machen oder die Daten zum Schutz der Rechte einer anderen natürlichen oder juristischen Person gebraucht werden, löschen wir (den Teil) Ihre(r) Daten erst nach Ablauf dieser Fristen. Bis zum Ablauf dieser Fristen beschränken wir die Verarbeitung dieser Daten jedoch auf diese Zwecke (Erfüllung der Aufbewahrungspflichten).',
webhostingTitle: 'Webhosting',
webhostingText:
'Wir bedienen uns zum Vorhalten unserer Internetseiten eines Anbieters, auf dessen Server unsere Internetseiten gespeichert und für den Abruf im Internet verfügbar gemacht werden (Hosting). Hierbei können von dem Anbieter all diejenigen über den von Ihnen genutzten Browser übertragenen Daten verarbeitet werden, die bei der Nutzung unserer Internetseiten anfallen. Hierzu gehören insbesondere Ihre IP-Adresse, die der Anbieter benötigt, um unser Online-Angebot an den von Ihnen genutzten Browser ausliefern zu können sowie sämtliche von Ihnen über unsere Internetseite getätigten Eingaben. Daneben kann der von uns genutzte Anbieter',
webhostingDataTypes: [
'das Datum und die Uhrzeit des Zugriffs auf unsere Internetseite',
'Zeitzonendifferenz zur Greenwich Mean Time (GMT)',
'Zugriffsstatus (HTTP-Status)',
'die übertragene Datenmenge',
'der Internet-Service-Provider des zugreifenden Systems',
'der von Ihnen verwendete Browsertyp und dessen Version',
'das von Ihnen verwendete Betriebssystem',
'die Internetseite, von welcher Sie gegebenenfalls auf unsere Internetseite gelangt sind',
'die Seiten bzw. Unterseiten, welche Sie auf unserer Internetseite besuchen.',
],
webhostingPurpose:
'erheben. Die vorgenannten Daten werden als Logfiles auf den Servern unseres Anbieters gespeichert. Dies ist erforderlich, um die Stabilität und Sicherheit des Betriebs unserer Internetseite zu gewährleisten.',
webhostingDataCategories: {
affectedData: 'Betroffene Daten:',
affectedDataList: [
'Inhaltsdaten (bspw. Posts, Fotos, Videos)',
'Nutzungsdaten (bspw. Zugriffszeiten, angeklickte Webseiten)',
'Kommunikationsdaten (bspw. Informationen über das genutzte Gerät, IP-Adresse)',
],
affectedPersons: 'Betroffene Personen: Nutzer unserer Internetpräsenz',
processingPurpose:
'Verarbeitungszweck: Ausspielen unserer Internetseiten, Gewährleistung des Betriebs unserer Internetseiten',
legalBasis:
'Rechtsgrundlage: Berechtigtes Interesse, Art. 6 Abs. 1 lit. f DSGVO',
provider: 'Von uns beauftragte(r) Webhoster:',
},
hostingProvider: {
name: 'Bitpalast GmbH',
address: 'Postfach 19 15 64, D-14005 Berlin, Deutschland',
website: 'https://preiswerter-webserver-de.bitpalast.net/',
},
contactTitle: 'Kontaktaufnahme',
contactText:
'Soweit Sie uns über E-Mail, Soziale Medien, Telefon, Fax, Post, unser Kontaktformular oder sonstwie ansprechen und uns hierbei personenbezogene Daten wie Ihren Namen, Ihre Telefonnummer oder Ihre E-Mail-Adresse zur Verfügung stellen oder weitere Angaben zur Ihrer Person oder Ihrem Anliegen machen, verarbeiten wir diese Daten zur Beantwortung Ihrer Anfrage im Rahmen des zwischen uns bestehenden vorvertraglichen oder vertraglichen Beziehungen.',
contactDataCategories: {
affectedData: [
'Bestandsdaten (bspw. Namen, Adressen)',
'Kontakdaten (bspw. E-Mail-Adresse, Telefonnummer, Postanschrift)',
'Inhaltsdaten (Texte, Fotos, Videos)',
'Vertragsdaten (bspw. Vertragsgegenstand, Vertragsdauer)',
],
affectedPersons:
'Interessenten, Kunden, Geschäfts- und Vertragspartner',
processingPurpose:
'Kommunikation sowie Beantwortung von Kontaktanfragen, Büro und Organisationsverfahren',
legalBasis:
'Vertragserfüllung und vorvertragliche Anfragen, Art. 6 Abs. 1 lit. b DSGVO, berechtigtes Interesse, Art. 6 Abs. 1 lit. f DSGVO',
},
securityTitle: 'Sicherheitsmaßnahmen',
securityText:
'Wir treffen im Übrigen technische und organisatorische Sicherheitsmaßnahmen nach dem Stand der Technik, um die Vorschriften der Datenschutzgesetze einzuhalten und Ihre Daten gegen zufällige oder vorsätzliche Manipulationen, teilweisen oder vollständigen Verlust, Zerstörung oder gegen den unbefugten Zugriff Dritter zu schützen.',
changesTitle: 'Aktualität und Änderung dieser Datenschutzerklärung',
changesText:
'Diese Datenschutzerklärung ist aktuell gültig und hat den Stand September 2025. Aufgrund geänderter gesetzlicher bzw. behördlicher Vorgaben kann es notwendig werden, diese Datenschutzerklärung anzupassen.',
disclaimer:
'Diese Datenschutzerklärung wurde mit Hilfe des Datenschutz-Generators von SOS Recht erstellt. SOS Recht ist ein Angebot der Mueller.legal Rechtsanwälte Partnerschaft mit Sitz in Berlin.',
},
address: {
street: 'Alt-Tempelhof 15',
city: '12099 Berlin',
country: 'Deutschland',
},
contact: {
phone: '+49 (0) 179 9364867',
},
},
privacyPolicy: {
title: 'Privacy Policy (GDPR/DSGVO)',
lastUpdated: 'Last updated:',
dataCollectionTitle: 'Data Collection',
dataCollectionText:
'We collect only the information you provide through our contact form:',
dataCollectionList: [
'Name (first and last)',
'Email address',
'Subject and message content',
],
purposeTitle: 'Purpose of Data Processing',
purposeText:
'Your data is used solely to respond to your inquiry and establish contact.',
providerTitle: 'Email Service Provider',
providerText:
'We use Resend (resend.com) to send emails from our contact form. Resend is GDPR compliant and processes data according to their privacy policy.',
retentionTitle: 'Data Retention',
retentionText:
'Contact form data is processed immediately and not stored permanently on our servers. Your message is sent via email and then discarded from our systems.',
rightsTitle: 'Your Rights Under GDPR',
rightsText: 'You have the right to:',
rightsList: [
'Request access to your personal data',
'Request rectification of your personal data',
'Request deletion of your personal data',
'Data portability',
'Object to processing',
],
contactTitle: 'Contact',
contactText:
'For any privacy-related questions or to exercise your rights, contact:',
contactEmail: 'info [at] sascha-bach.de',
legalBasisTitle: 'Legal Basis',
legalBasisText:
'Processing is based on your explicit consent (Art. 6(1)(a) GDPR).',
},
accessibility: {
skipLinks: {
skipToHero: 'Skip to main content',
skipToAbout: 'Skip to about section',
skipToServices: 'Skip to services section',
skipToSkills: 'Skip to skills section',
skipToProjects: 'Skip to projects section',
skipToCertifications: 'Skip to certifications section',
skipToContact: 'Skip to contact section',
},
navigation: {
keyboardHelp: 'Keyboard Navigation Help',
keyboardHelpExpanded: [
'Use Tab to navigate between elements',
'Use Enter or Space to activate buttons and links',
'Use Arrow keys to navigate between project cards',
'Use Escape to close this help dialog',
'Use Ctrl+F to search the page',
],
useTabToNavigate: 'Use Tab to navigate between elements',
useTabToNavigateCards:
'Use Tab to navigate between project cards, or Arrow keys to move within the grid',
useTabToNavigateOptions: 'Use Tab to navigate between options',
closeKeyboardHelp: 'Close keyboard help',
},
screenReader: {
professionalStatistics: 'Professional Statistics',
projectCard: 'Project card',
viewSourceCode: 'View source code',
viewLiveDemo: 'View live demo',
downloadResume: 'Download Resume',
downloadResumeDescription:
'This will download my current resume as a PDF file',
keyboardNavigationHelp: 'Keyboard Navigation Help',
themeSwitchButton: 'Theme switch button',
switchToTheme: 'Switch to theme',
certificationCard: 'Certification card',
serviceCard: 'Service card',
skillBadge: 'Skill badge',
},
ariaLabels: {
mainNavigation: 'Main navigation',
socialLinks: 'Social media links',
themeToggle: 'Toggle theme',
mobileMenuToggle: 'Toggle mobile menu',
contactForm: 'Contact form',
projectsGrid: 'Projects grid',
skillsGrid: 'Skills grid',
certificationsGrid: 'Certifications grid',
servicesGrid: 'Services grid',
},
},
};
// Export a function to get texts (can be extended for internationalization)
export const getTexts = (): TextConfig => {
// For now, return default texts
// In the future, this can be extended to support multiple languages
return defaultTexts;
}; };

View File

@ -0,0 +1,6 @@
export const themeToggle = {
lightIcon: '🌙',
darkIcon: '☀️',
lightModeText: 'dark',
darkModeText: 'light',
};

View File

@ -23,39 +23,41 @@ export default function PrivacyPolicy() {
return ( return (
<div className="privacy-policy" style={{ maxWidth: '800px', margin: '0 auto', padding: '2rem' }}> <div className="privacy-policy" style={{ maxWidth: '800px', margin: '0 auto', padding: '2rem' }}>
<h1>{texts.title}</h1> <h1>{texts.title}</h1>
<p><strong>{texts.lastUpdated}</strong> {new Date().toLocaleDateString()}</p> <p><strong>{texts.lastUpdated}</strong></p>
<h2>{texts.introTitle}</h2>
<p>{texts.introText}</p>
<h2>{texts.dataCollectionTitle}</h2> <h2>{texts.dataCollectionTitle}</h2>
<p>{texts.dataCollectionText}</p> <p>{texts.dataCollectionText}</p>
<ul> <ul>
{texts.dataCollectionList.map((item) => ( {texts.dataCollectionList.map((item, index) => (
<li key={item}>{item}</li> <li key={index}>{item}</li>
))} ))}
</ul> </ul>
<h2>{texts.purposeTitle}</h2> <h2>{texts.webhostingTitle}</h2>
<p>{texts.purposeText}</p> <p>{texts.webhostingText}</p>
<p>
<h2>{texts.providerTitle}</h2> <strong>{texts.webhostingProvider}</strong><br />
<p>{texts.providerText}</p> {texts.webhostingProviderName}<br />
{texts.webhostingProviderAddress}<br />
<h2>{texts.retentionTitle}</h2> <a href={texts.webhostingProviderWebsite} target="_blank" rel="noopener noreferrer">
<p>{texts.retentionText}</p> {texts.webhostingProviderWebsite}
</a>
</p>
<h2>{texts.rightsTitle}</h2> <h2>{texts.rightsTitle}</h2>
<p>{texts.rightsText}</p> <p>{texts.rightsText}</p>
<ul> <ul>
{texts.rightsList.map((right) => ( {texts.rightsList.map((right, index) => (
<li key={right}>{right}</li> <li key={index}>{right}</li>
))} ))}
</ul> </ul>
<h2>{texts.contactTitle}</h2> <h2>{texts.contactTitle}</h2>
<p>{texts.contactText}</p> <p>{texts.contactText}</p>
<p><strong>Email:</strong> {texts.contactEmail}</p> <p><strong>Email:</strong> {texts.contactEmail}</p>
<h2>{texts.legalBasisTitle}</h2>
<p>{texts.legalBasisText}</p>
</div> </div>
); );
} }

View File

@ -1,279 +0,0 @@
@use 'globals';
.contact-section {
background: var(--contact-background);
&__container {
max-width: 80rem;
margin: 0 auto;
padding: 0 1rem;
@include globals.desktop-only {
padding: 0 1.5rem;
}
}
&__header {
text-align: center;
margin-bottom: 4rem;
}
&__title {
font-size: 1.875rem;
font-weight: bold;
margin-bottom: 1rem;
background: var(--gradient-contact-title);
background-clip: text;
-webkit-background-clip: text;
color: transparent;
-webkit-text-fill-color: transparent;
@include globals.desktop-only {
font-size: 2.25rem;
}
}
&__subtitle {
font-size: 1.25rem;
color: var(--color-text-muted);
max-width: 48rem;
margin: 0 auto;
}
&__grid {
display: grid;
grid-template-columns: 1fr;
gap: 3rem;
@include globals.desktop-only {
grid-template-columns: 1fr 1fr;
}
}
// Contact Info Section
&__info {
display: flex;
flex-direction: column;
gap: 2rem;
}
&__connect-title {
font-size: 1.5rem;
font-weight: 600;
color: var(--color-text);
margin-bottom: 1.5rem;
}
&__connect-description {
color: var(--color-text-muted);
line-height: 1.6;
margin-bottom: 2rem;
}
&__details {
display: flex;
flex-direction: column;
gap: 1rem;
}
&__detail-item {
display: flex;
align-items: center;
gap: 1rem;
}
&__detail-icon {
width: 1.25rem;
height: 1.25rem;
color: var(--color-contact-primary);
flex-shrink: 0;
}
&__detail-text {
color: var(--color-text);
font-size: 0.875rem;
}
&__social {
display: flex;
gap: 1rem;
margin-top: 1rem;
}
&__social-button {
display: flex;
align-items: center;
justify-content: center;
width: 2.5rem;
height: 2.5rem;
border: 1px solid var(--contact-social-border);
border-radius: 0.375rem;
background: var(--contact-social-bg);
color: var(--contact-social-text);
text-decoration: none;
transition: all 0.2s ease;
&:hover {
background: var(--contact-social-hover-bg);
border-color: var(--contact-social-hover-border);
transform: translateY(-1px);
}
}
&__social-icon {
width: 1rem;
height: 1rem;
}
// Contact Form Section
&__form-card {
background: var(--contact-form-bg);
border: 1px solid var(--contact-form-border);
border-radius: 1rem;
box-shadow: var(--contact-form-shadow);
overflow: hidden;
}
&__form-header {
padding: 1.5rem 1.5rem 0;
margin-bottom: 1.5rem;
}
&__form-title {
font-size: 1.25rem;
font-weight: 600;
color: var(--color-text);
margin-bottom: 0.5rem;
}
&__form-description {
color: var(--color-text-muted);
font-size: 0.875rem;
}
&__form {
padding: 0 1.5rem 1.5rem;
display: flex;
flex-direction: column;
gap: 1rem;
}
// Honeypot field - hidden from users but visible to bots
&__honeypot {
position: absolute;
left: -9999px;
width: 1px;
height: 1px;
overflow: hidden;
opacity: 0;
}
&__honeypot-label,
&__honeypot-input {
position: absolute;
left: -9999px;
width: 1px;
height: 1px;
overflow: hidden;
opacity: 0;
}
&__form-row {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
@include globals.mobile-only {
grid-template-columns: 1fr 1fr;
}
}
&__form-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
&__form-label {
font-size: 0.875rem;
font-weight: 500;
color: var(--color-text);
}
&__form-input,
&__form-textarea {
padding: 0.75rem;
border: 1px solid var(--contact-input-border);
border-radius: 0.375rem;
background: var(--contact-input-bg);
color: var(--color-text);
font-size: 0.875rem;
transition: all 0.2s ease;
&::placeholder {
color: var(--color-text-muted);
}
&:focus {
outline: none;
border-color: var(--color-contact-primary);
box-shadow: 0 0 0 3px var(--contact-input-focus-ring);
}
}
&__form-textarea {
resize: vertical;
min-height: 5rem;
font-family: inherit;
}
&__form-button {
background: var(--contact-button-bg);
color: var(--contact-button-text);
border: none;
border-radius: 0.375rem;
padding: 0.75rem 1.5rem;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
margin-top: 0.5rem;
&:hover:not(:disabled) {
background: var(--contact-button-hover-bg);
transform: translateY(-1px);
}
&:active:not(:disabled) {
transform: translateY(0);
}
&:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
}
// Responsive adjustments
@include globals.mobile-only {
&__grid {
gap: 2rem;
}
&__form-card {
border-radius: 0.75rem;
}
&__form-header,
&__form {
padding-left: 1rem;
padding-right: 1rem;
}
&__social {
justify-content: center;
margin-top: 1.5rem;
}
}
}

View File

@ -65,13 +65,47 @@
// Responsive breakpoint mixins // Responsive breakpoint mixins
@mixin mobile-only { @mixin mobile-only {
@media (max-width: variables.$mobile-breakpoint) { @media (max-width: $mobile-breakpoint) {
@content; @content;
} }
} }
@mixin desktop-only { @mixin desktop-only {
@media (min-width: variables.$desktop-breakpoint) { @media (min-width: $desktop-breakpoint) {
@content; @content;
} }
}
// Common section patterns
@mixin section-container {
max-width: 80rem;
margin: 0 auto;
padding: 0 1rem;
@include desktop-only {
padding: 0 1.5rem;
}
}
@mixin section-header {
text-align: center;
margin-bottom: 4rem;
}
@mixin section-title {
font-size: 1.875rem;
font-weight: bold;
margin-bottom: 1rem;
color: var(--color-text);
@include desktop-only {
font-size: 2.25rem;
}
}
@mixin section-subtitle {
font-size: 1.25rem;
color: var(--color-text-muted);
max-width: 48rem;
margin: 0 auto;
} }

View File

@ -1,211 +0,0 @@
@use 'globals';
.projects-section {
background: var(--projects-background);
&__container {
max-width: 80rem;
margin: 0 auto;
padding: 0 1rem;
@include globals.desktop-only {
padding: 0 1.5rem;
}
}
&__header {
text-align: center;
margin-bottom: 4rem;
}
&__title {
font-size: 1.875rem;
font-weight: bold;
margin-bottom: 1rem;
background: var(--gradient-projects-title);
background-clip: text;
-webkit-background-clip: text;
color: transparent;
-webkit-text-fill-color: transparent;
@include globals.desktop-only {
font-size: 2.25rem;
}
}
&__subtitle {
font-size: 1.25rem;
color: var(--color-text-muted);
max-width: 48rem;
margin: 0 auto;
}
&__grid {
display: grid;
grid-template-columns: repeat(1, 1fr);
gap: 2rem;
@include globals.mobile-only {
grid-template-columns: repeat(2, 1fr);
}
@include globals.desktop-only {
grid-template-columns: repeat(3, 1fr);
}
}
&__card {
background: var(--projects-card-background);
border-radius: 1rem;
box-shadow: var(--projects-card-shadow);
overflow: hidden;
transition: all 0.3s ease;
border: 1px solid var(--projects-card-border);
&:hover {
transform: translateY(-0.25rem);
box-shadow: var(--projects-card-shadow-hover);
.projects-section__image {
transform: scale(1.05);
}
.projects-section__overlay {
opacity: 1;
}
}
}
&__image-container {
position: relative;
overflow: hidden;
height: 12rem;
}
&__image {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.3s ease;
}
&__overlay {
position: absolute;
inset: 0;
background: var(--projects-overlay-background);
opacity: 0;
transition: opacity 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
}
&__actions {
display: flex;
gap: 1rem;
}
&__action-button {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
border-radius: 0.375rem;
font-size: 0.875rem;
font-weight: 500;
text-decoration: none;
transition: all 0.2s ease;
border: 1px solid transparent;
&--primary {
background: var(--projects-button-primary-bg);
color: var(--projects-button-primary-text);
border-color: var(--projects-button-primary-border);
&:hover {
background: var(--projects-button-primary-hover-bg);
transform: translateY(-1px);
}
}
&--secondary {
background: var(--projects-button-secondary-bg);
color: var(--projects-button-secondary-text);
border-color: var(--projects-button-secondary-border);
&:hover {
background: var(--projects-button-secondary-hover-bg);
transform: translateY(-1px);
}
}
}
&__action-icon {
width: 1rem;
height: 1rem;
}
&__content {
padding: 1.5rem;
}
&__header-content {
margin-bottom: 1.5rem;
}
&__card-title {
font-size: 1.125rem;
font-weight: 600;
color: var(--color-text);
margin-bottom: 0.5rem;
line-height: 1.4;
}
&__card-description {
color: var(--color-text-muted);
font-size: 0.875rem;
line-height: 1.5;
}
&__technologies {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
&__tech-badge {
display: inline-block;
background: var(--projects-tech-badge-bg);
color: var(--projects-tech-badge-text);
padding: 0.25rem 0.75rem;
border-radius: 9999px;
font-size: 0.75rem;
font-weight: 500;
border: 1px solid var(--projects-tech-badge-border);
}
// Responsive adjustments
@include globals.mobile-only {
&__card {
border-radius: 0.75rem;
}
&__content {
padding: 1rem;
}
&__image-container {
height: 10rem;
}
&__actions {
flex-direction: column;
gap: 0.75rem;
}
&__action-button {
justify-content: center;
padding: 0.75rem 1rem;
}
}
}

View File

@ -1,40 +1,24 @@
@use '../globals'; @use '../globals';
@use '../variables' as *; @use '../variables' as *;
@use '../mixins' as *;
.about-section { .about-section {
background: var(--about-background); background: var(--about-background);
&__container { &__container {
max-width: 80rem; @include section-container;
margin: 0 auto;
padding: 0 1rem;
@include globals.desktop-only {
padding: 0 1.5rem;
}
} }
&__header { &__header {
text-align: center; @include section-header;
margin-bottom: 4rem;
} }
&__title { &__title {
font-size: 1.875rem; @include section-title;
font-weight: bold;
margin-bottom: 1rem;
color: var(--color-text);
@include globals.desktop-only {
font-size: 2.25rem;
}
} }
&__subtitle { &__subtitle {
font-size: 1.25rem; @include section-subtitle;
color: var(--color-text-muted);
max-width: 48rem;
margin: 0 auto;
} }
&__content { &__content {

View File

@ -1,40 +1,24 @@
@use '../globals'; @use '../globals';
@use '../variables' as *; @use '../variables' as *;
@use '../mixins' as *;
.certifications-section { .certifications-section {
background: var(--certifications-background); background: var(--certifications-background);
&__container { &__container {
max-width: 80rem; @include section-container;
margin: 0 auto;
padding: 0 1rem;
@include globals.desktop-only {
padding: 0 1.5rem;
}
} }
&__header { &__header {
text-align: center; @include section-header;
margin-bottom: 4rem;
} }
&__title { &__title {
font-size: 1.875rem; @include section-title;
font-weight: bold;
margin-bottom: 1rem;
color: var(--color-text);
@include globals.desktop-only {
font-size: 2.25rem;
}
} }
&__subtitle { &__subtitle {
font-size: 1.25rem; @include section-subtitle;
color: var(--color-text-muted);
max-width: 48rem;
margin: 0 auto;
} }
&__grid { &__grid {

View File

@ -1,40 +1,24 @@
@use '../globals'; @use '../globals';
@use '../variables' as *; @use '../variables' as *;
@use '../mixins' as *;
.contact-section { .contact-section {
background: var(--contact-background); background: var(--contact-background);
&__container { &__container {
max-width: 80rem; @include section-container;
margin: 0 auto;
padding: 0 1rem;
@include globals.desktop-only {
padding: 0 1.5rem;
}
} }
&__header { &__header {
text-align: center; @include section-header;
margin-bottom: 4rem;
} }
&__title { &__title {
font-size: 1.875rem; @include section-title;
font-weight: bold;
margin-bottom: 1rem;
color: var(--color-text);
@include globals.desktop-only {
font-size: 2.25rem;
}
} }
&__subtitle { &__subtitle {
font-size: 1.25rem; @include section-subtitle;
color: var(--color-text-muted);
max-width: 48rem;
margin: 0 auto;
} }
&__grid { &__grid {

View File

@ -19,6 +19,7 @@
.hero-section { .hero-section {
@include flex-center; @include flex-center;
min-height: 100vh;
background: var(--hero-background); background: var(--hero-background);
&__container { &__container {

View File

@ -1,40 +1,24 @@
@use '../globals'; @use '../globals';
@use '../variables' as *; @use '../variables' as *;
@use '../mixins' as *;
.projects-section { .projects-section {
background: var(--projects-background); background: var(--projects-background);
&__container { &__container {
max-width: 80rem; @include section-container;
margin: 0 auto;
padding: 0 1rem;
@include globals.desktop-only {
padding: 0 1.5rem;
}
} }
&__header { &__header {
text-align: center; @include section-header;
margin-bottom: 4rem;
} }
&__title { &__title {
font-size: 1.875rem; @include section-title;
font-weight: bold;
margin-bottom: 1rem;
color: var(--color-text);
@include globals.desktop-only {
font-size: 2.25rem;
}
} }
&__subtitle { &__subtitle {
font-size: 1.25rem; @include section-subtitle;
color: var(--color-text-muted);
max-width: 48rem;
margin: 0 auto;
} }
&__grid { &__grid {

View File

@ -1,5 +1,6 @@
@use '../globals'; @use '../globals';
@use '../variables' as *; @use '../variables' as *;
@use '../mixins' as *;
.services-section { .services-section {
min-height: auto; min-height: auto;
@ -14,36 +15,23 @@
} }
&__container { &__container {
max-width: 80rem; @include section-container;
margin: 0 auto;
padding: 0 1rem;
width: 100%; width: 100%;
@include globals.desktop-only {
padding: 0 1.5rem;
}
} }
&__header { &__header {
text-align: center; @include section-header;
margin-bottom: $services-header-margin; margin-bottom: $services-header-margin;
@include globals.mobile-only { @include mobile-only {
margin-bottom: 1.5rem; margin-bottom: 1.5rem;
} }
} }
&__title { &__title {
font-size: 1.875rem; @include section-title;
font-weight: bold;
margin-bottom: 1rem;
color: var(--color-text);
@include globals.desktop-only { @include mobile-only {
font-size: 2.25rem;
}
@include globals.mobile-only {
font-size: 1.5rem; font-size: 1.5rem;
} }
} }
@ -54,7 +42,7 @@
max-width: 32rem; max-width: 32rem;
margin: 0 auto; margin: 0 auto;
@include globals.mobile-only { @include mobile-only {
font-size: 1rem; font-size: 1rem;
} }
} }

View File

@ -1,5 +1,6 @@
@use '../globals'; @use '../globals';
@use '../variables' as *; @use '../variables' as *;
@use '../mixins' as *;
.skills-section { .skills-section {
padding: $section-padding-y 0; padding: $section-padding-y 0;
@ -7,7 +8,7 @@
position: relative; position: relative;
// Mobile responsiveness // Mobile responsiveness
@include globals.mobile-only { @include mobile-only {
padding: 3rem 0; padding: 3rem 0;
} }
@ -25,37 +26,24 @@
} }
&__container { &__container {
max-width: 80rem; @include section-container;
margin: 0 auto;
padding: 0 1rem;
position: relative; position: relative;
z-index: 1; z-index: 1;
@include globals.desktop-only {
padding: 0 1.5rem;
}
} }
&__header { &__header {
text-align: center; @include section-header;
margin-bottom: $skills-header-margin; margin-bottom: $skills-header-margin;
@include globals.mobile-only { @include mobile-only {
margin-bottom: 2rem; margin-bottom: 2rem;
} }
} }
&__title { &__title {
font-size: 1.875rem; @include section-title;
font-weight: bold;
margin-bottom: 1rem;
color: var(--color-text);
@include globals.desktop-only { @include mobile-only {
font-size: 2.25rem;
}
@include globals.mobile-only {
font-size: 1.5rem; font-size: 1.5rem;
} }
} }