text centralization

This commit is contained in:
Sascha 2025-11-03 16:58:40 +01:00
parent c83b6e07cf
commit 34b02f69be
8 changed files with 344 additions and 203 deletions

View File

@ -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>

View File

@ -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>

View File

@ -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');

View File

@ -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}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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)