text centralization
This commit is contained in:
parent
c83b6e07cf
commit
34b02f69be
|
|
@ -14,6 +14,7 @@ interface AboutSectionProps {
|
|||
|
||||
export default function AboutSection(props: AboutSectionProps = {}) {
|
||||
const texts = getTexts().about;
|
||||
const accessibilityTexts = getTexts().accessibility;
|
||||
|
||||
// Use centralized texts as defaults, allow props to override
|
||||
const {
|
||||
|
|
@ -22,32 +23,12 @@ export default function AboutSection(props: AboutSectionProps = {}) {
|
|||
subtitle = texts.subtitle,
|
||||
bio = texts.bio,
|
||||
profileImage = saschaImage,
|
||||
featureCards = [
|
||||
{
|
||||
icon: Code,
|
||||
title: "Web Development",
|
||||
description: "Building modern web applications with Angular and React frameworks, leveraging TypeScript and JavaScript for robust, scalable solutions.",
|
||||
colorClass: "primary"
|
||||
},
|
||||
{
|
||||
icon: Palette,
|
||||
title: "Responsive Design",
|
||||
description: "Creating pixel-perfect, responsive interfaces that work seamlessly across all devices and screen sizes.",
|
||||
colorClass: "secondary"
|
||||
},
|
||||
{
|
||||
icon: ShieldCheck,
|
||||
title: "Cross-Browser Compatibility",
|
||||
description: "Ensuring consistent user experiences across all major browsers with thorough testing and optimization.",
|
||||
colorClass: "tertiary"
|
||||
},
|
||||
{
|
||||
icon: Eye,
|
||||
title: "Web Accessibility",
|
||||
description: "Implementing accessibility best practices to deliver websites that support screen readers, keyboard navigation, and assistive technologies.",
|
||||
colorClass: "quaternary"
|
||||
}
|
||||
]
|
||||
featureCards = texts.featureCards.map((card, index) => ({
|
||||
icon: [Code, Palette, ShieldCheck, Eye][index] || Code,
|
||||
title: card.title,
|
||||
description: card.description,
|
||||
colorClass: ["primary", "secondary", "tertiary", "quaternary"][index] || "primary"
|
||||
}))
|
||||
} = props;
|
||||
|
||||
return (
|
||||
|
|
@ -65,8 +46,8 @@ export default function AboutSection(props: AboutSectionProps = {}) {
|
|||
|
||||
{/* Skip link for keyboard navigation */}
|
||||
<div className="about-section__skip">
|
||||
<a href="#features" className="sr-only sr-only-focusable">
|
||||
Skip to features section
|
||||
<a href="#services" className="sr-only sr-only-focusable">
|
||||
{accessibilityTexts.skipLinks.skipToServices}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -10,43 +10,18 @@ interface CertificationsSectionProps {
|
|||
|
||||
export default function CertificationsSection(props: CertificationsSectionProps = {}) {
|
||||
const texts = getTexts().certifications;
|
||||
const accessibilityTexts = getTexts().accessibility;
|
||||
|
||||
// Use centralized texts as defaults, allow props to override
|
||||
const {
|
||||
title = texts.title,
|
||||
subtitle = texts.subtitle,
|
||||
certifications = [
|
||||
{
|
||||
name: "Practical Prompt Engineering Masterclass: Hands-On Learning",
|
||||
issuer: "Udemy Lecture - Asif Farooqi, Abdullah Dar",
|
||||
year: "2025",
|
||||
certifications = texts.certificationItems.map((cert, index) => ({
|
||||
name: cert.name,
|
||||
issuer: cert.issuer,
|
||||
year: ["2025", "2025", "2020", "2020", "2015"][index] || "2025",
|
||||
icon: ""
|
||||
},
|
||||
{
|
||||
name: "Understanding Typescript",
|
||||
issuer: "Udemy Lecture - Maximilian Schwarzmüller",
|
||||
year: "2025",
|
||||
icon: ""
|
||||
},
|
||||
{
|
||||
name: "Angular Step by Step",
|
||||
issuer: "Udemy Lecture - Shivprasad Koirala",
|
||||
year: "2020",
|
||||
icon: ""
|
||||
},
|
||||
{
|
||||
name: "Angular and Typescript",
|
||||
issuer: "LinkedIn TestDome",
|
||||
year: "2020",
|
||||
icon: ""
|
||||
},
|
||||
{
|
||||
name: "Bachelor of Science in Computer Science",
|
||||
issuer: "TU Darmstadt",
|
||||
year: "2015",
|
||||
icon: ""
|
||||
}
|
||||
]
|
||||
}))
|
||||
} = props;
|
||||
|
||||
// Sort certifications by year in descending order (newest first)
|
||||
|
|
@ -70,7 +45,7 @@ export default function CertificationsSection(props: CertificationsSectionProps
|
|||
{/* Skip link for keyboard navigation */}
|
||||
<div className="certifications-section__skip">
|
||||
<a href="#contact" className="sr-only sr-only-focusable">
|
||||
Skip to contact section
|
||||
{accessibilityTexts.skipLinks.skipToContact}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -40,14 +40,14 @@ export default function ContactSection(props: ContactSectionProps = {}) {
|
|||
// Enhanced email handler with better accessibility
|
||||
const handleEmailClick = () => {
|
||||
const email = getEmailAddress();
|
||||
const subject = encodeURIComponent("Portfolio Inquiry - Let's discuss your project");
|
||||
const body = encodeURIComponent("Hello Sascha,\n\nI visited your portfolio and would like to discuss a potential project or collaboration.\n\nBest regards,");
|
||||
const subject = encodeURIComponent(texts.emailSubject);
|
||||
const body = encodeURIComponent(texts.emailBody);
|
||||
|
||||
// Announce action to screen readers
|
||||
const announcement = document.createElement('div');
|
||||
announcement.setAttribute('aria-live', 'polite');
|
||||
announcement.setAttribute('class', 'sr-only');
|
||||
announcement.textContent = 'Opening email client with pre-filled message';
|
||||
announcement.textContent = texts.emailAnnouncement;
|
||||
document.body.appendChild(announcement);
|
||||
|
||||
globalThis.location.href = `mailto:${email}?subject=${subject}&body=${body}`;
|
||||
|
|
@ -67,7 +67,7 @@ export default function ContactSection(props: ContactSectionProps = {}) {
|
|||
const announcement = document.createElement('div');
|
||||
announcement.setAttribute('aria-live', 'polite');
|
||||
announcement.setAttribute('class', 'sr-only');
|
||||
announcement.textContent = `Opening ${platform} profile in new tab`;
|
||||
announcement.textContent = texts.socialAnnouncement.replace('{platform}', platform);
|
||||
document.body.appendChild(announcement);
|
||||
|
||||
globalThis.open(url, '_blank', 'noopener,noreferrer');
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ interface HeroSectionProps {
|
|||
|
||||
export default function HeroSection(props: HeroSectionProps = {}) {
|
||||
const texts = getTexts().hero;
|
||||
const accessibilityTexts = getTexts().accessibility;
|
||||
|
||||
// Use centralized texts as defaults, allow props to override
|
||||
const {
|
||||
|
|
@ -98,7 +99,7 @@ export default function HeroSection(props: HeroSectionProps = {}) {
|
|||
{/* Skip link for keyboard navigation */}
|
||||
<div className="hero-section__skip">
|
||||
<a href="#about" className="sr-only sr-only-focusable">
|
||||
Skip to about section
|
||||
{accessibilityTexts.skipLinks.skipToAbout}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
|
@ -140,11 +141,11 @@ export default function HeroSection(props: HeroSectionProps = {}) {
|
|||
|
||||
{/* Hidden descriptions for screen readers */}
|
||||
<div id="resume-description" className="sr-only">
|
||||
This will download my current resume in PDF format
|
||||
{accessibilityTexts.screenReader.downloadResumeDescription}
|
||||
</div>
|
||||
|
||||
<aside className="hero-section__stats" aria-labelledby="stats-heading">
|
||||
<h2 id="stats-heading" className="sr-only">Professional Statistics</h2>
|
||||
<h2 id="stats-heading" className="sr-only">{accessibilityTexts.screenReader.professionalStatistics}</h2>
|
||||
{statItems.map((item, index) => (
|
||||
<div
|
||||
key={item.label}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ interface ProjectsSectionProps {
|
|||
|
||||
export default function ProjectsSection(props: ProjectsSectionProps = {}) {
|
||||
const texts = getTexts().projects;
|
||||
const accessibilityTexts = getTexts().accessibility;
|
||||
const [activeCardIndex, setActiveCardIndex] = useState<number | null>(null);
|
||||
const [focusedActionIndex, setFocusedActionIndex] = useState<number>(0);
|
||||
const cardRefs = useRef<(HTMLElement | null)[]>([]);
|
||||
|
|
@ -25,36 +26,24 @@ export default function ProjectsSection(props: ProjectsSectionProps = {}) {
|
|||
const {
|
||||
title = texts.title,
|
||||
subtitle = texts.subtitle,
|
||||
projects = [
|
||||
{
|
||||
id: 1,
|
||||
title: "Portfolio Website",
|
||||
description: "A personal portfolio website to showcase my skills, projects, and experience.",
|
||||
image: portfolioImage,
|
||||
technologies: ["React", "TypeScript", "SCSS", "Vercel", "Github Copilot"],
|
||||
year: "2025",
|
||||
github: personalConfig.gitProjects.portfolio,
|
||||
live: personalConfig.projectsUrls.portfolio
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "ErgoVR",
|
||||
description: "A virtual reality application for analysis of motion sickness in VR environments from 2015.",
|
||||
image: ergoVRImage,
|
||||
technologies: ["Unity3D", "C#", "Oculus SDK"],
|
||||
year: "2015",
|
||||
github: personalConfig.gitProjects.ergoVR
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "Icarace",
|
||||
description: "Participated in the development of a web-platform for a fitness racing game for Icaros GmbH until 2018.",
|
||||
image: icaraceImage,
|
||||
technologies: ["Angular 4", "Typescript", "HTML", "CSS"],
|
||||
year: "2018",
|
||||
github: personalConfig.gitProjects.ergoVR
|
||||
}
|
||||
]
|
||||
projects = texts.projectItems.map((project, index) => ({
|
||||
id: index + 1,
|
||||
title: project.title,
|
||||
description: project.description,
|
||||
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;
|
||||
|
||||
// Get available actions for a project
|
||||
|
|
@ -73,7 +62,7 @@ export default function ProjectsSection(props: ProjectsSectionProps = {}) {
|
|||
// Announce to screen readers
|
||||
const project = projects[index];
|
||||
const actions = getProjectActions(project);
|
||||
const announcement = `${project.title} card selected. ${actions.length} actions available. Use Tab to navigate actions, Escape to close.`;
|
||||
const announcement = `${project.title} card selected. ${actions.length} actions available. ${accessibilityTexts.navigation.useTabToNavigate}, Escape to close.`;
|
||||
announceToScreenReader(announcement);
|
||||
};
|
||||
|
||||
|
|
@ -196,7 +185,7 @@ export default function ProjectsSection(props: ProjectsSectionProps = {}) {
|
|||
|
||||
{/* Keyboard navigation instructions */}
|
||||
<div className="projects-section__instructions sr-only" aria-live="polite">
|
||||
<p>Use Tab to navigate between project cards. Press Enter or Space to select a card and view available actions. Use Tab to navigate between actions within a selected card. Press Escape to close the selected card. Use Arrow keys to move between cards when one is selected.</p>
|
||||
<p>{accessibilityTexts.navigation.useTabToNavigateCards}</p>
|
||||
</div>
|
||||
|
||||
{/* Global keyboard shortcuts info - MOVED BEFORE CARDS */}
|
||||
|
|
@ -217,7 +206,7 @@ export default function ProjectsSection(props: ProjectsSectionProps = {}) {
|
|||
{/* Skip link for keyboard navigation help */}
|
||||
<div className="projects-section__skip">
|
||||
<a href="#contact" className="sr-only sr-only-focusable">
|
||||
Skip to contact section
|
||||
{accessibilityTexts.skipLinks.skipToContact}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
|
@ -233,7 +222,7 @@ export default function ProjectsSection(props: ProjectsSectionProps = {}) {
|
|||
className={`projects-section__card ${isActive ? 'projects-section__card--active' : ''}`}
|
||||
ref={(el) => { cardRefs.current[cardIndex] = el; }}
|
||||
aria-describedby={`project-${project.id}-description project-${project.id}-metadata`}
|
||||
aria-label={`Project card: ${project.title}. ${cardIndex + 1} of ${projects.length}`}
|
||||
aria-label={`${accessibilityTexts.screenReader.projectCard}: ${project.title}. ${cardIndex + 1} of ${projects.length}`}
|
||||
onKeyDown={(e) => handleCardKeyDown(e, cardIndex)}
|
||||
onClick={() => handleCardSelect(cardIndex)}
|
||||
>
|
||||
|
|
@ -260,7 +249,7 @@ export default function ProjectsSection(props: ProjectsSectionProps = {}) {
|
|||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="projects-section__action-button projects-section__action-button--secondary"
|
||||
aria-label={`View ${project.title} source code on GitHub (opens in new tab)`}
|
||||
aria-label={`${accessibilityTexts.screenReader.viewSourceCode} ${project.title} source code on GitHub (opens in new tab)`}
|
||||
tabIndex={isActive ? 0 : -1}
|
||||
onKeyDown={(e) => handleActionKeyDown(e, cardIndex, 0)}
|
||||
>
|
||||
|
|
@ -275,7 +264,7 @@ export default function ProjectsSection(props: ProjectsSectionProps = {}) {
|
|||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="projects-section__action-button projects-section__action-button--primary"
|
||||
aria-label={`View ${project.title} live demo (opens in new tab)`}
|
||||
aria-label={`${accessibilityTexts.screenReader.viewLiveDemo} ${project.title} live demo (opens in new tab)`}
|
||||
tabIndex={isActive ? 0 : -1}
|
||||
onKeyDown={(e) => handleActionKeyDown(e, cardIndex, project.github ? 1 : 0)}
|
||||
>
|
||||
|
|
@ -321,7 +310,7 @@ export default function ProjectsSection(props: ProjectsSectionProps = {}) {
|
|||
{isActive && (
|
||||
<div className="projects-section__status sr-only" aria-live="polite" aria-atomic="true">
|
||||
{project.title} card selected. {actions.length} actions available.
|
||||
Use Tab to navigate actions, Escape to close, Arrow keys to move between cards.
|
||||
{accessibilityTexts.navigation.useTabToNavigate}, Escape to close, Arrow keys to move between cards.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -10,49 +10,18 @@ interface ServicesSectionProps {
|
|||
|
||||
export default function ServicesSection(props: ServicesSectionProps = {}) {
|
||||
const texts = getTexts().services;
|
||||
const accessibilityTexts = getTexts().accessibility;
|
||||
|
||||
// Use centralized texts as defaults, allow props to override
|
||||
const {
|
||||
title = texts.title,
|
||||
subtitle = texts.subtitle,
|
||||
services = [
|
||||
{
|
||||
icon: Eye,
|
||||
title: "Web Accessibility",
|
||||
description: "Building inclusive and accessible web applications by implementing Web Content Accessibility Guidelines, semantic HTML, and support for assistive technologies.",
|
||||
colorClass: "primary"
|
||||
},
|
||||
{
|
||||
icon: Palette,
|
||||
title: "Responsive Web Design",
|
||||
description: "Creating mobile-first, responsive interfaces that provide optimal viewing experiences across all devices and screen sizes.",
|
||||
colorClass: "secondary"
|
||||
},
|
||||
{
|
||||
icon: ShieldCheck,
|
||||
title: "Cross-Browser Testing",
|
||||
description: "Ensuring consistent functionality and appearance across all major browsers including Chrome, Firefox, Safari, and Edge.",
|
||||
colorClass: "tertiary"
|
||||
},
|
||||
{
|
||||
icon: Laptop,
|
||||
title: "Frontend Architecture",
|
||||
description: "Designing and implementing scalable frontend architectures with component-based development and modern tooling.",
|
||||
colorClass: "quaternary"
|
||||
},
|
||||
{
|
||||
icon: Lightbulb,
|
||||
title: "Performance Optimization",
|
||||
description: "Optimizing web applications for speed and efficiency through code splitting, lazy loading, and performance best practices.",
|
||||
colorClass: "quinary"
|
||||
},
|
||||
{
|
||||
icon: Rocket,
|
||||
title: "Testing & Quality Assurance",
|
||||
description: "Implementing comprehensive testing strategies using Cypress for end-to-end testing and ensuring code quality.",
|
||||
colorClass: "senary"
|
||||
}
|
||||
]
|
||||
services = texts.serviceItems.map((service, index) => ({
|
||||
icon: [Eye, Palette, ShieldCheck, Laptop, Lightbulb, Rocket][index] || Eye,
|
||||
title: service.title,
|
||||
description: service.description,
|
||||
colorClass: ["primary", "secondary", "tertiary", "quaternary", "quinary", "senary"][index] || "primary"
|
||||
}))
|
||||
} = props;
|
||||
|
||||
return (
|
||||
|
|
@ -70,8 +39,8 @@ export default function ServicesSection(props: ServicesSectionProps = {}) {
|
|||
|
||||
{/* Skip link for keyboard navigation */}
|
||||
<div className="services-section__skip">
|
||||
<a href="#certifications" className="sr-only sr-only-focusable">
|
||||
Skip to certifications section
|
||||
<a href="#skills" className="sr-only sr-only-focusable">
|
||||
{accessibilityTexts.skipLinks.skipToSkills}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -28,67 +28,13 @@ const getColorByPosition = (index: number): string => {
|
|||
|
||||
export default function SkillsSection(props: SkillsSectionProps = {}) {
|
||||
const texts = getTexts().skills;
|
||||
const accessibilityTexts = getTexts().accessibility;
|
||||
|
||||
// Use centralized texts as defaults, allow props to override
|
||||
const {
|
||||
title = texts.title,
|
||||
subtitle = texts.subtitle,
|
||||
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 }
|
||||
]
|
||||
}
|
||||
]
|
||||
skillCategories = texts.skillCategories
|
||||
} = props;
|
||||
|
||||
return (
|
||||
|
|
@ -106,8 +52,8 @@ export default function SkillsSection(props: SkillsSectionProps = {}) {
|
|||
|
||||
{/* Skip link for keyboard navigation */}
|
||||
<div className="skills-section__skip">
|
||||
<a href="#services" className="sr-only sr-only-focusable">
|
||||
Skip to services section
|
||||
<a href="#certifications" className="sr-only sr-only-focusable">
|
||||
{accessibilityTexts.skipLinks.skipToCertifications}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -41,18 +41,33 @@ export interface TextConfig {
|
|||
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
|
||||
|
|
@ -61,12 +76,20 @@ export interface TextConfig {
|
|||
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
|
||||
|
|
@ -84,6 +107,10 @@ export interface TextConfig {
|
|||
preferredContactValue: string;
|
||||
locationLabel: string;
|
||||
locationValue: string;
|
||||
emailSubject: string;
|
||||
emailBody: string;
|
||||
emailAnnouncement: string;
|
||||
socialAnnouncement: string;
|
||||
};
|
||||
|
||||
// Imprint Page
|
||||
|
|
@ -184,6 +211,52 @@ export interface TextConfig {
|
|||
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)
|
||||
|
|
@ -242,16 +315,126 @@ export const defaultTexts: TextConfig = {
|
|||
'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: {
|
||||
|
|
@ -259,11 +442,50 @@ export const defaultTexts: TextConfig = {
|
|||
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: {
|
||||
|
|
@ -281,6 +503,11 @@ export const defaultTexts: TextConfig = {
|
|||
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: {
|
||||
|
|
@ -475,6 +702,59 @@ export const defaultTexts: TextConfig = {
|
|||
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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue