Improve accessibility: keyboard-operable mobile nav, skip link, focus styles
- Replace the checkbox-hack mobile menu (display:none checkbox was unreachable by keyboard/screen readers) with a real button using aria-expanded/aria-controls - Add a skip-to-content link - Add a consistent :focus-visible outline across interactive elements - Underline prose links by default instead of only on hover (WCAG 1.4.1 use-of-color), keep nav/button links unstyled since they're already visually distinct - Respect prefers-reduced-motion for smooth scrolling Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
cf8d6a65ea
commit
807022a62a
4 changed files with 97 additions and 16 deletions
|
|
@ -51,6 +51,7 @@ const footer = site.footer;
|
|||
.footer-nav a {
|
||||
color: var(--color-text-muted);
|
||||
font-size: var(--font-size-sm);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.footer-nav a:hover {
|
||||
|
|
|
|||
|
|
@ -10,14 +10,20 @@ const nav = site.nav;
|
|||
<img src={nav.logo} alt={nav.logoAlt} width="320" height="60" class="nav-logo-img" />
|
||||
</a>
|
||||
|
||||
<input type="checkbox" id="nav-toggle" class="nav-toggle-checkbox" />
|
||||
<label for="nav-toggle" class="nav-toggle" aria-label="Menü öffnen/schließen">
|
||||
<button
|
||||
type="button"
|
||||
class="nav-toggle"
|
||||
id="nav-toggle"
|
||||
aria-expanded="false"
|
||||
aria-controls="nav-links"
|
||||
aria-label="Menü öffnen"
|
||||
>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</label>
|
||||
</button>
|
||||
|
||||
<nav class="nav-links" aria-label="Hauptnavigation">
|
||||
<nav class="nav-links" id="nav-links" aria-label="Hauptnavigation">
|
||||
<ul>
|
||||
{nav.links.map((item) => (
|
||||
<li>
|
||||
|
|
@ -50,9 +56,6 @@ const nav = site.nav;
|
|||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
.nav-logo:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
|
@ -78,11 +81,11 @@ const nav = site.nav;
|
|||
color: var(--color-text-muted);
|
||||
font-weight: var(--font-weight-medium);
|
||||
font-size: var(--font-size-sm);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.nav-links ul a:hover {
|
||||
color: var(--color-primary);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.nav-cta {
|
||||
|
|
@ -92,10 +95,6 @@ const nav = site.nav;
|
|||
padding: var(--space-3) var(--space-5);
|
||||
}
|
||||
|
||||
.nav-toggle-checkbox {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nav-toggle {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
|
|
@ -103,6 +102,9 @@ const nav = site.nav;
|
|||
gap: 5px;
|
||||
width: 28px;
|
||||
height: 22px;
|
||||
padding: 0;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
|
@ -149,8 +151,34 @@ const nav = site.nav;
|
|||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.nav-toggle-checkbox:checked ~ .nav-links {
|
||||
.nav-links.is-open {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
const toggle = document.getElementById('nav-toggle');
|
||||
const links = document.getElementById('nav-links');
|
||||
|
||||
function setOpen(open) {
|
||||
links?.classList.toggle('is-open', open);
|
||||
toggle?.setAttribute('aria-expanded', String(open));
|
||||
toggle?.setAttribute('aria-label', open ? 'Menü schließen' : 'Menü öffnen');
|
||||
}
|
||||
|
||||
toggle?.addEventListener('click', () => {
|
||||
setOpen(!links?.classList.contains('is-open'));
|
||||
});
|
||||
|
||||
links?.addEventListener('click', (event) => {
|
||||
const target = event.target;
|
||||
if (target instanceof HTMLElement && target.tagName === 'A') {
|
||||
setOpen(false);
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('keydown', (event) => {
|
||||
if (event.key === 'Escape') setOpen(false);
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -32,8 +32,9 @@ const fontsHref = googleFontsHref(theme);
|
|||
<style set:html={themeCss(theme)} />
|
||||
</head>
|
||||
<body>
|
||||
<a href="#main-content" class="skip-link">Zum Inhalt springen</a>
|
||||
<Nav />
|
||||
<main>
|
||||
<main id="main-content">
|
||||
<slot />
|
||||
</main>
|
||||
<Footer />
|
||||
|
|
|
|||
|
|
@ -123,11 +123,23 @@ p {
|
|||
|
||||
a {
|
||||
color: var(--color-primary);
|
||||
text-decoration: none;
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 0.15em;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
color: var(--color-primary-dark);
|
||||
}
|
||||
|
||||
a:focus-visible,
|
||||
button:focus-visible,
|
||||
input:focus-visible,
|
||||
textarea:focus-visible,
|
||||
summary:focus-visible,
|
||||
[tabindex]:focus-visible {
|
||||
outline: 2px solid var(--color-primary);
|
||||
outline-offset: 2px;
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
|
||||
ul {
|
||||
|
|
@ -147,6 +159,25 @@ textarea {
|
|||
font-size: var(--font-size-base);
|
||||
}
|
||||
|
||||
.skip-link {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: var(--space-4);
|
||||
transform: translateY(-150%);
|
||||
z-index: 100;
|
||||
background: var(--color-primary);
|
||||
color: var(--color-primary-contrast);
|
||||
padding: var(--space-3) var(--space-5);
|
||||
border-radius: 0 0 var(--radius-sm) var(--radius-sm);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
text-decoration: none;
|
||||
transition: transform var(--transition-fast);
|
||||
}
|
||||
|
||||
.skip-link:focus {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Layout-Hilfsklassen
|
||||
========================================================================== */
|
||||
|
|
@ -249,3 +280,23 @@ textarea {
|
|||
border-bottom: 1px dashed var(--color-flag);
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Bewegungsreduktion
|
||||
Respektiert die Systemeinstellung fuer Nutzer:innen mit vestibulaeren
|
||||
Beeintraechtigungen (WCAG 2.3.3).
|
||||
========================================================================== */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
html {
|
||||
scroll-behavior: auto;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue