feat(a11y): add skip-to-content links and ARIA labels to calendar, contacts, todo

- Add skip-to-content link ("Zum Inhalt springen") to all 3 app layouts
- Add id="main-content" to main content areas
- Add ariaLabel prop to shared PillNavigation component
- Set aria-label="Hauptnavigation" on nav elements in all 3 apps
- Add aria-label to icon-only nav toggle button in todo

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-03-19 12:41:23 +01:00
parent 217c48663b
commit 9fc237d9ed
4 changed files with 40 additions and 2 deletions

View file

@ -438,6 +438,13 @@
<SplitPaneContainer>
<div class="layout-container">
<a
href="#main-content"
class="sr-only focus:not-sr-only focus:fixed focus:top-2 focus:left-2 focus:z-[100] focus:rounded-lg focus:bg-primary focus:px-4 focus:py-2 focus:text-white"
>
Zum Inhalt springen
</a>
<!-- UI Elements (hidden in immersive mode) -->
{#if !settingsStore.immersiveModeEnabled}
<PillNavigation
@ -469,6 +476,7 @@
profileHref="/profile"
allAppsHref="/apps"
onOpenInPanel={handleOpenInPanel}
ariaLabel="Hauptnavigation"
/>
<!-- Date strip (only on main calendar page) -->
@ -550,6 +558,7 @@
/>
<main
id="main-content"
class="main-content bg-background"
class:has-toolbar={showCalendarToolbar}
class:immersive={settingsStore.immersiveModeEnabled}

View file

@ -278,6 +278,13 @@
<SplitPaneContainer>
<!-- Navigation Layout -->
<div class="layout-container">
<a
href="#main-content"
class="sr-only focus:not-sr-only focus:fixed focus:top-2 focus:left-2 focus:z-[100] focus:rounded-lg focus:bg-primary focus:px-4 focus:py-2 focus:text-white"
>
Zum Inhalt springen
</a>
<!-- UI Elements (hidden in immersive mode) -->
{#if !contactsSettings.immersiveModeEnabled}
<!-- Floating Pill Navigation (at bottom) -->
@ -310,6 +317,7 @@
profileHref="/profile"
allAppsHref="/apps"
onOpenInPanel={handleOpenInPanel}
ariaLabel="Hauptnavigation"
/>
<!-- Global Quick Input Bar -->
@ -342,6 +350,7 @@
<!-- Main Content -->
<main
id="main-content"
class="main-content bg-background"
class:immersive={contactsSettings.immersiveModeEnabled}
>

View file

@ -310,6 +310,13 @@
<SplitPaneContainer>
<div class="layout-container">
<a
href="#main-content"
class="sr-only focus:not-sr-only focus:fixed focus:top-2 focus:left-2 focus:z-[100] focus:rounded-lg focus:bg-primary focus:px-4 focus:py-2 focus:text-white"
>
Zum Inhalt springen
</a>
<!-- UI Elements (hidden in immersive mode) -->
{#if !todoSettings.immersiveModeEnabled}
<!-- PillNav (shown/hidden via FAB) -->
@ -342,6 +349,7 @@
profileHref="/profile"
allAppsHref="/apps"
onOpenInPanel={handleOpenInPanel}
ariaLabel="Hauptnavigation"
/>
<!-- FilterStrip (shown when Filter pill is active in PillNav) -->
@ -370,6 +378,7 @@
class="pillnav-fab"
onclick={handlePillNavToggle}
title={isPillNavCollapsed ? 'Navigation anzeigen' : 'Navigation ausblenden'}
aria-label={isPillNavCollapsed ? 'Navigation anzeigen' : 'Navigation ausblenden'}
>
{#if isPillNavCollapsed}
<!-- Menu icon -->
@ -401,7 +410,11 @@
onToggle={() => todoSettings.toggleImmersiveMode()}
/>
<main class="main-content bg-background" class:immersive={todoSettings.immersiveModeEnabled}>
<main
id="main-content"
class="main-content bg-background"
class:immersive={todoSettings.immersiveModeEnabled}
>
<div
class="content-wrapper"
class:full-width={$page.url.pathname === '/kanban'}

View file

@ -255,6 +255,8 @@
showA11yQuickToggles?: boolean;
/** Called when an app should be opened in a split panel */
onOpenInPanel?: (appId: string, url: string) => void;
/** Accessible label for the nav element */
ariaLabel?: string;
}
let {
@ -296,6 +298,7 @@
onA11yReduceMotionChange,
showA11yQuickToggles = false,
onOpenInPanel,
ariaLabel,
}: Props = $props();
// Type guards for elements
@ -443,7 +446,11 @@
</script>
{#if !isCollapsed}
<nav class="pill-nav" style={primaryColor ? `--pill-primary-color: ${primaryColor}` : ''}>
<nav
class="pill-nav"
style={primaryColor ? `--pill-primary-color: ${primaryColor}` : ''}
aria-label={ariaLabel}
>
<div class="pill-nav-container">
<!-- Logo pill / App Switcher -->
{#if showAppSwitcher && appItems.length > 0}