Ein umfassender Leitfaden zur Implementierung der Web Content Accessibility Guidelines mit detaillierten Erklärungen, praktischen Beispielen und Code-Snippets für Entwickler und Designer.
Unsere Experten können Ihnen helfen, Ihre Website vollständig WCAG-konform zu gestalten.
Kostenlose Analyse erhaltenDie Web Content Accessibility Guidelines (WCAG) sind ein international anerkannter Standard für digitale Barrierefreiheit. Sie wurden vom World Wide Web Consortium (W3C) im Rahmen der Web Accessibility Initiative (WAI) entwickelt und bilden die technische Grundlage für die meisten Barrierefreiheitsgesetze weltweit, einschließlich der EU-Richtlinien.
Die aktuelle Version, WCAG 2.1, wurde im Juni 2018 veröffentlicht und erweitert die vorherige Version WCAG 2.0 um zusätzliche Richtlinien, insbesondere für mobile Geräte, Menschen mit kognitiven Einschränkungen und Menschen mit Sehschwäche.
Version | Veröffentlichung | Wichtige Neuerungen | Status |
---|---|---|---|
WCAG 1.0 | 1999 | Erste offizielle Richtlinien | Veraltet |
WCAG 2.0 | 2008 | Technologieunabhängige Prinzipien, testbare Kriterien | W3C-Empfehlung, ISO/IEC 40500 |
WCAG 2.1 | 2018 | Mobile Zugänglichkeit, kognitive Einschränkungen, Sehschwäche | Aktuelle W3C-Empfehlung |
WCAG 2.2 | 2023 | Verbesserte mobile Zugänglichkeit, Drag-and-Drop-Alternativen | Neueste W3C-Empfehlung |
Die WCAG-Richtlinien sind so konzipiert, dass sie:
WCAG 2.1 ist abwärtskompatibel mit WCAG 2.0. Das bedeutet, dass Webseiten, die WCAG 2.1 erfüllen, automatisch auch WCAG 2.0 erfüllen. Die EU-Barrierefreiheitsgesetze verweisen auf WCAG 2.1 Level AA als technischen Standard für die Einhaltung der Barrierefreiheitsanforderungen.
Digitale Barrierefreiheit ist aus mehreren Gründen von entscheidender Bedeutung:
Barrierefreiheit berücksichtigt verschiedene Arten von Behinderungen und Einschränkungen:
Menschen mit Behinderungen nutzen verschiedene Hilfstechnologien, um im Web zu navigieren:
Software, die den Bildschirminhalt vorliest und von blinden und sehbehinderten Menschen verwendet wird.
Beispiele: JAWS, NVDA, VoiceOver, TalkBack
Software, die Teile des Bildschirms vergrößert, um Menschen mit Sehschwäche zu helfen.
Beispiele: ZoomText, Windows-Bildschirmlupe, macOS-Zoom
Geräte, die Menschen mit motorischen Einschränkungen bei der Bedienung von Computern helfen.
Beispiele: Augensteuerung, Mundstab, Einzelschalter, angepasste Tastaturen
Software, die Sprachbefehle in Computeraktionen umwandelt.
Beispiele: Dragon NaturallySpeaking, Windows Speech Recognition, Voice Control
WCAG 2.1 definiert drei Konformitätsstufen, die unterschiedliche Grade der Barrierefreiheit repräsentieren:
Die Mindeststufe der Barrierefreiheit. Websites auf dieser Stufe beseitigen die größten Barrieren für Menschen mit Behinderungen. Level A ist der Ausgangspunkt für Barrierefreiheit, aber in der Regel nicht ausreichend für vollständige Zugänglichkeit.
Die von der EU-Gesetzgebung geforderte Stufe. Websites auf dieser Stufe beseitigen bedeutende Barrieren und verbessern die Zugänglichkeit erheblich. Level AA ist der Standard für die meisten kommerziellen Websites und wird von den meisten Barrierefreiheitsgesetzen weltweit gefordert.
Die höchste Stufe der Barrierefreiheit. Nicht gesetzlich vorgeschrieben, aber empfehlenswert für maximale Zugänglichkeit. Level AAA ist schwer vollständig zu erreichen, aber viele Websites implementieren ausgewählte AAA-Kriterien zusätzlich zu AA.
Die EU-Barrierefreiheitsgesetze erfordern die Einhaltung von WCAG 2.1 Level AA. Es ist jedoch nicht notwendig, alle Level AAA-Kriterien zu erfüllen, um eine barrierefreie Website zu haben. Viele Organisationen streben an, alle Level A und AA-Kriterien zu erfüllen und ausgewählte Level AAA-Kriterien zu implementieren, wo es sinnvoll ist.
Um Konformität mit WCAG 2.1 zu beanspruchen, müssen folgende Bedingungen erfüllt sein:
WCAG 2.1 basiert auf vier Grundprinzipien, die als POUR-Prinzipien bekannt sind. Diese Prinzipien bilden das Fundament für alle Barrierefreiheitsrichtlinien und helfen, die Anforderungen zu strukturieren und zu verstehen.
Informationen und Benutzeroberflächen müssen für alle Benutzer wahrnehmbar sein, unabhängig von ihren Fähigkeiten.
Benutzeroberflächen und Navigation müssen von allen Benutzern bedienbar sein, unabhängig von der Eingabemethode.
Informationen und die Bedienung der Benutzeroberfläche müssen für alle Benutzer verständlich sein.
Inhalte müssen robust genug sein, um von einer Vielzahl von Benutzeragenten, einschließlich Hilfstechnologien, zuverlässig interpretiert werden zu können.
Die WCAG 2.1 sind hierarchisch strukturiert:
Ebene | Beschreibung | Anzahl |
---|---|---|
Prinzipien | Die vier POUR-Grundprinzipien | 4 |
Richtlinien | Grundlegende Ziele, die Autoren anstreben sollten | 13 |
Erfolgskriterien | Testbare Aussagen, die entweder erfüllt oder nicht erfüllt sein können | 78 |
Techniken | Beispiele und Ressourcen zur Erfüllung der Kriterien (informativ) | Hunderte |
Die Web Content Accessibility Guidelines (WCAG) 2.1 sind ein international anerkannter Standard für digitale Barrierefreiheit, der auf vier Grundprinzipien basiert: Wahrnehmbar, Bedienbar, Verständlich und Robust. Die Richtlinien definieren drei Konformitätsstufen (A, AA, AAA), wobei Level AA der Standard für die meisten Websites und die Anforderung der EU-Gesetzgebung ist.
Barrierefreiheit ist wichtig aus sozialen Gründen (Inklusion, Gleichberechtigung) und geschäftlichen Gründen (größere Zielgruppe, bessere SEO, rechtliche Compliance). Die Implementierung der WCAG-Richtlinien hilft, Websites für Menschen mit verschiedenen Behinderungen und Einschränkungen zugänglich zu machen, die unterschiedliche Hilfstechnologien verwenden.
In den folgenden Abschnitten werden die vier POUR-Prinzipien im Detail betrachtet mit praktischen Beispielen und Code-Snippets für die Implementierung der WCAG 2.1 Richtlinien.
Das erste POUR-Prinzip besagt, dass Informationen und Benutzeroberflächen für alle Benutzer wahrnehmbar sein müssen. Dies bedeutet, dass Inhalte so präsentiert werden müssen, dass sie von allen Benutzern wahrgenommen werden können, unabhängig von ihren Fähigkeiten oder der verwendeten Technologie.
Textalternativen sind eine der grundlegendsten Anforderungen der Barrierefreiheit. Sie ermöglichen es Screenreadern, Nicht-Text-Inhalte wie Bilder, Grafiken, Icons, Buttons und andere visuelle Elemente zu interpretieren und an den Benutzer weiterzugeben.
Art des Inhalts | Anforderung an die Textalternative | Beispiel |
---|---|---|
Informative Bilder | Beschreibung des Bildinhalts und seiner Funktion | alt="Diagramm: Umsatzentwicklung 2023 mit 15% Steigerung" |
Dekorative Bilder | Leeres alt-Attribut oder role="presentation" | alt="" oder role="presentation" |
Funktionale Bilder (z.B. Icons in Buttons) | Beschreibung der Funktion, nicht des Aussehens | alt="Suchen" (nicht "Lupe") |
Komplexe Bilder (z.B. Diagramme, Infografiken) | Kurze Beschreibung im alt-Text, ausführliche Beschreibung im Kontext | alt="Verkaufsprozess-Diagramm" + detaillierte Beschreibung |
Bilder von Text | Der exakte Text im Bild | alt="Kontaktieren Sie uns unter 030 12345678" |
CAPTCHAs | Zweck des CAPTCHAs und alternative Methode | alt="CAPTCHA: Geben Sie die Zeichen ein, die Sie sehen, oder nutzen Sie die Audio-Alternative" |
<!-- Fehlendes alt-Attribut -->
<img src="logo.png" />
<!-- Nicht aussagekräftiger alt-Text -->
<img src="chart.png" alt="Bild" />
<!-- Redundante Information -->
<img src="icon-search.png" alt="Bild einer Lupe">
<span>Suchen</span>
<!-- Dateiname als alt-Text -->
<img src="DSC_1234.jpg" alt="DSC_1234.jpg" />
Probleme: Fehlende alt-Attribute, nicht aussagekräftige Beschreibungen, redundante Informationen oder Dateinamen als alt-Text.
<!-- Informatives Bild -->
<img
src="chart.png"
alt="Umsatzsteigerung von 15% im Vergleich zum Vorjahr"
/>
<!-- Dekoratives Bild -->
<img src="decoration.png" alt="" role="presentation" />
<!-- Icon in einem Button -->
<button>
<img src="icon-search.png" alt="">
<span>Suchen</span>
</button>
<!-- SVG mit Titel und Beschreibung -->
<svg role="img" aria-labelledby="chartTitle chartDesc">
<title id="chartTitle">Quartalsumsätze 2023</title>
<desc id="chartDesc">Balkendiagramm zeigt steigende Umsätze in allen Quartalen</desc>
<!-- SVG-Inhalt -->
</svg>
Gute Praxis: Aussagekräftige alt-Texte für informative Bilder, leere alt-Attribute für dekorative Bilder, korrekte Verwendung von ARIA-Attributen für SVGs.
import Image from 'next/image'
// Informatives Bild
export function ProductImage({ product }) {
return (
<Image
src={product.imageUrl || "/placeholder.svg"}
alt={`${product.name} - ${product.description}`}
width={300}
height={200}
/>
)
}
// Dekoratives Bild
export function DecorativeImage() {
return (
<Image
src="/decoration.png"
alt=""
role="presentation"
width={400}
height={100}
/>
)
}
// Icon in einem Button mit verstecktem Text für Screenreader
export function SearchButton() {
return (
<button className="p-2 bg-black text-white rounded-full">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
aria-hidden="true"
>
<circle cx="11" cy="11" r="8"></circle>
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
</svg>
<span className="sr-only">Suchen</span>
</button>
)
}
import Image from 'next/image'
export function ComplexChart() {
return (
<figure>
<Image
src="/sales-chart.png"
alt="Verkaufszahlen nach Regionen 2023"
width={600}
height={400}
aria-describedby="chart-description"
/>
<figcaption id="chart-description" className="mt-2 text-sm text-gray-600">
<h3 className="font-bold">Verkaufszahlen nach Regionen 2023</h3>
<p>
Das Diagramm zeigt die Verkaufszahlen für vier Regionen:
Nord (42%), Süd (28%), Ost (15%) und West (15%).
Die Region Nord hat den größten Anteil, gefolgt von Süd.
Ost und West haben identische Anteile.
</p>
</figcaption>
</figure>
)
}
Hier sind einige Beispielszenarien. Überlegen Sie, welche Alt-Texte Sie für diese Bilder schreiben würden:
Szenario 1: Logo im Header einer Website
Möglicher Alt-Text:
alt="Firmenname Logo"
Szenario 2: Produktfoto in einem Online-Shop
Möglicher Alt-Text:
alt="Schwarzer Ledergürtel mit silberner Schnalle, 3,5 cm breit"
Szenario 3: Dekoratives Hintergrundbild
Möglicher Alt-Text:
alt="" oder role="presentation"
Szenario 4: Infografik zur Produktnutzung
Möglicher Alt-Text:
alt="Infografik: 5 Schritte zur Produktinstallation" (mit zusätzlicher ausführlicher Beschreibung im Kontext)
Zeitbasierte Medien wie Audio und Video müssen Alternativen bereitstellen, damit Menschen mit Hör- oder Sehbehinderungen den Inhalt wahrnehmen können. Dies umfasst Untertitel, Audiodeskriptionen und Transkriptionen.
Alternative | Beschreibung | Für wen? |
---|---|---|
Untertitel | Text-Äquivalent für gesprochene Dialoge und wichtige Soundeffekte | Gehörlose und schwerhörige Menschen |
Transkription | Vollständiger Text aller Dialoge und Beschreibung wichtiger visueller Elemente | Gehörlose, schwerhörige, taubblinde Menschen |
Audiodeskription | Zusätzliche Audiokommentare, die wichtige visuelle Informationen beschreiben | Blinde und sehbehinderte Menschen |
Gebärdensprache | Übersetzung des Audioinhalts in Gebärdensprache | Gehörlose Menschen, die Gebärdensprache bevorzugen |
<!-- Video ohne Untertitel oder Alternativen -->
<video controls>
<source src="video.mp4" type="video/mp4">
Ihr Browser unterstützt das Video-Element nicht.
</video>
<!-- Audio ohne Transkription -->
<audio controls>
<source src="podcast.mp3" type="audio/mpeg">
Ihr Browser unterstützt das Audio-Element nicht.
</audio>
Probleme: Keine Untertitel, keine Transkription, keine Audiodeskription für wichtige visuelle Informationen.
<!-- Video mit Untertiteln und Audiodeskription -->
<video controls>
<source src="video.mp4" type="video/mp4">
<source src="video.webm" type="video/webm">
<track
kind="captions"
src="captions.vtt"
srclang="de"
label="Deutsch"
default
>
<track
kind="descriptions"
src="descriptions.vtt"
srclang="de"
label="Audiodeskription"
>
Ihr Browser unterstützt das Video-Element nicht.
</video>
<!-- Audio mit Transkription -->
<figure>
<audio controls>
<source src="podcast.mp3" type="audio/mpeg">
Ihr Browser unterstützt das Audio-Element nicht.
</audio>
<figcaption>
<details>
<summary>Transkription anzeigen</summary>
<div>
<p>Moderator: Willkommen zu unserem Podcast...</p>
<p>Gast: Danke für die Einladung...</p>
<!-- Vollständige Transkription -->
</div>
</details>
</figcaption>
</figure>
Gute Praxis: Video mit Untertiteln und Audiodeskription, Audio mit vollständiger Transkription.
WEBVTT
00:00:01.000 --> 00:00:04.000
Moderator: Willkommen zu unserem Webinar über digitale Barrierefreiheit.
00:00:04.500 --> 00:00:08.000
Heute sprechen wir über die WCAG 2.1 Richtlinien und ihre Bedeutung.
00:00:08.500 --> 00:00:12.000
Gast: Vielen Dank für die Einladung. Ich freue mich, hier zu sein.
00:00:12.500 --> 00:00:15.000
[Applaus]
WEBVTT
00:00:00.500 --> 00:00:01.000
Eine Frau und ein Mann sitzen an einem Tisch mit Mikrofonen.
00:00:07.000 --> 00:00:08.000
Eine Präsentation mit dem Titel "WCAG 2.1" wird eingeblendet.
00:00:15.500 --> 00:00:16.500
Das Publikum applaudiert.
import { useState } from 'react'
export function AccessibleVideoPlayer() {
const [showTranscript, setShowTranscript] = useState(false)
return (
<figure className="max-w-3xl mx-auto">
<div className="aspect-video bg-black">
<video
controls
className="w-full h-full"
poster="/video-thumbnail.jpg"
>
<source src="/video.mp4" type="video/mp4" />
<source src="/video.webm" type="video/webm" />
{/* Untertitel in verschiedenen Sprachen */}
<track
kind="captions"
src="/captions-de.vtt"
srcLang="de"
label="Deutsch"
default
/>
<track
kind="captions"
src="/captions-en.vtt"
srcLang="en"
label="English"
/>
{/* Audiodeskription */}
<track
kind="descriptions"
src="/descriptions-de.vtt"
srcLang="de"
label="Audiodeskription"
/>
<p>
Ihr Browser unterstützt das Video-Element nicht.
Sie können das Video <a href="/video.mp4">hier herunterladen</a>.
</p>
</video>
</div>
<figcaption className="mt-4">
<button
onClick={() => setShowTranscript(!showTranscript)}
className="flex items-center gap-2 text-sm font-medium"
aria-expanded={showTranscript}
>
<span>{showTranscript ? 'Transkript ausblenden' : 'Transkript anzeigen'}</span>
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className={showTranscript ? 'rotate-180' : ''}
>
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</button>
{showTranscript && (
<div className="mt-4 p-4 bg-gray-50 rounded-md text-sm">
<h3 className="font-bold mb-2">Vollständige Transkription</h3>
<div className="space-y-2">
<p><strong>Moderator (00:01):</strong> Willkommen zu unserem Webinar über digitale Barrierefreiheit.</p>
<p><strong>Moderator (00:04):</strong> Heute sprechen wir über die WCAG 2.1 Richtlinien und ihre Bedeutung.</p>
<p><strong>Gast (00:08):</strong> Vielen Dank für die Einladung. Ich freue mich, hier zu sein.</p>
<p><strong>Moderator (00:12):</strong> [Applaus] Lassen Sie uns mit einer Einführung beginnen...</p>
{/* Weitere Transkription */}
</div>
</div>
)}
</figcaption>
</figure>
)
}
Das Prinzip "Wahrnehmbar" stellt sicher, dass Informationen und Benutzeroberflächen für alle Benutzer wahrnehmbar sind, unabhängig von ihren Fähigkeiten oder der verwendeten Technologie. Die wichtigsten Aspekte sind:
Durch die Implementierung dieser Richtlinien stellen Sie sicher, dass Ihre Inhalte für Menschen mit verschiedenen Behinderungen, insbesondere für blinde, sehbehinderte, gehörlose und schwerhörige Menschen, zugänglich sind.
Das zweite POUR-Prinzip besagt, dass Benutzeroberflächen und Navigation für alle Benutzer bedienbar sein müssen. Dies bedeutet, dass Benutzer mit verschiedenen Fähigkeiten und unter Verwendung verschiedener Eingabemethoden in der Lage sein müssen, mit der Website zu interagieren und zu navigieren.
Tastaturzugänglichkeit ist entscheidend für Menschen mit motorischen Einschränkungen, die keine Maus verwenden können, sowie für blinde Benutzer, die Screenreader verwenden. Alle interaktiven Elemente müssen mit der Tastatur bedienbar sein.
<!-- Klick-Event ohne Tastaturunterstützung -->
<div onclick="toggleMenu()">
Menü öffnen
</div>
<!-- Benutzerdefiniertes Element ohne Tastaturunterstützung -->
<div class="custom-dropdown" onmouseover="showOptions()">
<span>Optionen</span>
<div class="dropdown-content">
<div>Option 1</div>
<div>Option 2</div>
</div>
</div>
Probleme: Keine Tastaturunterstützung, keine semantischen Elemente, keine ARIA-Attribute.
<!-- Button mit Tastaturunterstützung -->
<button onclick="toggleMenu()" aria-expanded="false" aria-controls="menu">
Menü öffnen
</button>
<!-- Zugängliches Dropdown-Menü -->
<div class="dropdown">
<button
aria-haspopup="true"
aria-expanded="false"
aria-controls="dropdown-menu"
onclick="toggleDropdown()"
>
Optionen
</button>
<ul id="dropdown-menu" role="menu" class="dropdown-content" hidden>
<li role="menuitem" tabindex="-1">Option 1</li>
<li role="menuitem" tabindex="-1">Option 2</li>
</ul>
</div>
Gute Praxis: Semantische Elemente, Tastaturunterstützung, ARIA-Attribute für Screenreader.
import { useState, useRef, useEffect } from 'react'
export function AccessibleDropdown() {
const [isOpen, setIsOpen] = useState(false)
const dropdownRef = useRef(null)
// Dropdown öffnen/schließen
const toggleDropdown = () => {
setIsOpen(!isOpen)
}
// Tastaturnavigation
const handleKeyDown = (e) => {
if (e.key === 'Escape' && isOpen) {
setIsOpen(false)
}
}
// Klick außerhalb schließt Dropdown
useEffect(() => {
const handleClickOutside = (event) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
setIsOpen(false)
}
}
document.addEventListener('mousedown', handleClickOutside)
return () => {
document.removeEventListener('mousedown', handleClickOutside)
}
}, [])
return (
<div
className="relative"
ref={dropdownRef}
onKeyDown={handleKeyDown}
>
<button
className="px-4 py-2 bg-gray-200 rounded"
onClick={toggleDropdown}
aria-haspopup="true"
aria-expanded={isOpen}
aria-controls="dropdown-menu"
>
Optionen
</button>
{isOpen && (
<ul
id="dropdown-menu"
role="menu"
className="absolute mt-1 w-40 bg-white border rounded shadow-lg"
>
<li
role="menuitem"
tabIndex={0}
className="px-4 py-2 hover:bg-gray-100 cursor-pointer"
onClick={() => {
console.log('Option 1 selected')
setIsOpen(false)
}}
>
Option 1
</li>
<li
role="menuitem"
tabIndex={0}
className="px-4 py-2 hover:bg-gray-100 cursor-pointer"
onClick={() => {
console.log('Option 2 selected')
setIsOpen(false)
}}
>
Option 2
</li>
</ul>
)}
</div>
)
}
button
, a
, input
Ausreichend Zeit ist wichtig für Menschen mit verschiedenen Behinderungen, die möglicherweise mehr Zeit benötigen, um Inhalte zu lesen oder Aufgaben abzuschließen. Zeitbegrenzungen sollten anpassbar sein oder ganz vermieden werden.
<!-- Automatische Weiterleitung ohne Kontrolle -->
<meta http-equiv="refresh" content="10; URL=next-page.html">
<!-- Automatisch abspielendes Karussell ohne Kontrolle -->
<div class="carousel" data-auto-play="true" data-interval="5000">
<div class="slide">Slide 1</div>
<div class="slide">Slide 2</div>
<div class="slide">Slide 3</div>
</div>
<!-- Session-Timeout ohne Warnung -->
<script>
setTimeout(function() {
window.location = 'logout.php';
}, 900000); // 15 Minuten
</script>
Probleme: Keine Kontrolle über Zeitbegrenzungen, keine Möglichkeit zum Pausieren oder Anpassen.
<!-- Karussell mit Kontrollelementen -->
<div class="carousel" role="region" aria-label="Bildergalerie">
<div class="slides">
<div id="slide1" class="slide" aria-labelledby="slide1-heading">
<h3 id="slide1-heading">Slide 1</h3>
<!-- Inhalt -->
</div>
<!-- Weitere Slides -->
</div>
<div class="controls">
<button class="pause" aria-pressed="false">Pausieren</button>
<button class="prev" aria-label="Vorheriges Bild">Zurück</button>
<button class="next" aria-label="Nächstes Bild">Weiter</button>
</div>
</div>
<!-- Session-Timeout mit Warnung und Verlängerungsoption -->
<script>
let timeoutWarning = 840000; // 14 Minuten
let timeoutNow = 900000; // 15 Minuten
setTimeout(function() {
// Warnung anzeigen
showTimeoutWarning();
}, timeoutWarning);
function showTimeoutWarning() {
// Dialog mit Optionen anzeigen
const dialog = document.createElement('div');
dialog.innerHTML = `
<div role="dialog" aria-labelledby="timeout-title" aria-describedby="timeout-desc">
<h2 id="timeout-title">Sitzungszeitlimit</h2>
<p id="timeout-desc">Ihre Sitzung läuft in 1 Minute ab. Möchten Sie die Sitzung verlängern?</p>
<button onclick="extendSession()">Sitzung verlängern</button>
<button onclick="logout()">Jetzt abmelden</button>
</div>
`;
document.body.appendChild(dialog);
}
function extendSession() {
// Timeout zurücksetzen
clearTimeouts();
// Neue Timeouts setzen
setTimeout(showTimeoutWarning, timeoutWarning);
setTimeout(logout, timeoutNow);
}
</script>
Gute Praxis: Kontrollelemente zum Pausieren und Navigieren, Warnungen vor Zeitlimits mit Verlängerungsoption.
import { useState, useEffect, useRef } from 'react'
export function AccessibleCarousel({ slides, interval = 5000 }) {
const [currentSlide, setCurrentSlide] = useState(0)
const [isPaused, setIsPaused] = useState(false)
const timerRef = useRef(null)
// Zum nächsten Slide wechseln
const nextSlide = () => {
setCurrentSlide((prev) => (prev === slides.length - 1 ? 0 : prev + 1))
}
// Zum vorherigen Slide wechseln
const prevSlide = () => {
setCurrentSlide((prev) => (prev === 0 ? slides.length - 1 : prev - 1))
}
// Karussell pausieren/fortsetzen
const togglePause = () => {
setIsPaused(!isPaused)
}
// Timer für automatischen Wechsel
useEffect(() => {
if (!isPaused) {
timerRef.current = setTimeout(nextSlide, interval)
}
return () => {
if (timerRef.current) {
clearTimeout(timerRef.current)
}
}
}, [currentSlide, isPaused, interval])
return (
<div
className="relative overflow-hidden"
role="region"
aria-roledescription="carousel"
aria-label="Bildergalerie"
>
<div className="flex transition-transform duration-500" style={{ transform: `translateX(-${currentSlide * 100}%)` }}>
{slides.map((slide, index) => (
<div
key={index}
className="w-full flex-shrink-0"
role="group"
aria-roledescription="slide"
aria-label={`${index + 1} von ${slides.length}`}
>
<img
src={slide.image || "/placeholder.svg"}
alt={slide.alt || ''}
className="w-full h-auto"
/>
{slide.caption && (
<div className="p-4 bg-black bg-opacity-50 text-white">
{slide.caption}
</div>
)}
</div>
))}
</div>
<div className="absolute bottom-4 left-0 right-0 flex justify-center gap-4">
<button
onClick={togglePause}
className="px-4 py-2 bg-white bg-opacity-75 rounded"
aria-pressed={isPaused}
>
{isPaused ? 'Fortsetzen' : 'Pausieren'}
</button>
<button
onClick={prevSlide}
className="px-4 py-2 bg-white bg-opacity-75 rounded"
aria-label="Vorheriges Bild"
>
Zurück
</button>
<button
onClick={nextSlide}
className="px-4 py-2 bg-white bg-opacity-75 rounded"
aria-label="Nächstes Bild"
>
Weiter
</button>
</div>
<div className="absolute bottom-16 left-0 right-0 flex justify-center gap-2">
{slides.map((_, index) => (
<button
key={index}
onClick={() => setCurrentSlide(index)}
className={`w-3 h-3 rounded-full ${
currentSlide === index ? 'bg-white' : 'bg-white bg-opacity-50'
}`}
aria-label={`Zu Bild ${index + 1} wechseln`}
aria-current={currentSlide === index ? 'true' : 'false'}
/>
))}
</div>
</div>
)
}
Blinkende oder flackernde Inhalte können bei Menschen mit photosensitiver Epilepsie Anfälle auslösen. Daher ist es wichtig, solche Inhalte zu vermeiden oder sicherzustellen, dass sie unter dem Schwellenwert liegen.
// CSS: Respektieren der Benutzereinstellung für reduzierte Bewegung
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
// React-Komponente mit Animation-Kontrolle
import { useState, useEffect } from 'react'
export function AnimationControl({ children }) {
const [isAnimationPaused, setIsAnimationPaused] = useState(false)
const [prefersReducedMotion, setPrefersReducedMotion] = useState(false)
// Benutzereinstellung für reduzierte Bewegung erkennen
useEffect(() => {
const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)')
setPrefersReducedMotion(mediaQuery.matches)
const handleChange = () => setPrefersReducedMotion(mediaQuery.matches)
mediaQuery.addEventListener('change', handleChange)
return () => mediaQuery.removeEventListener('change', handleChange)
}, [])
// Animation basierend auf Benutzereinstellung oder Kontrolle pausieren
const shouldPauseAnimation = isAnimationPaused || prefersReducedMotion
return (
<div>
<div style={{ animationPlayState: shouldPauseAnimation ? 'paused' : 'running' }}>
{children}
</div>
{!prefersReducedMotion && (
<button
onClick={() => setIsAnimationPaused(!isAnimationPaused)}
aria-pressed={isAnimationPaused}
>
{isAnimationPaused ? 'Animation fortsetzen' : 'Animation pausieren'}
</button>
)}
{prefersReducedMotion && (
<p className="text-sm text-gray-600">
Animationen wurden basierend auf Ihren Systemeinstellungen reduziert.
</p>
)}
</div>
)
}
Verschiedene Eingabemodalitäten ermöglichen es Benutzern, Inhalte auf die für sie am besten geeignete Weise zu nutzen. Dies umfasst Maus, Tastatur, Touch, Stifteingabe und Sprachsteuerung.
<!-- Kleine Klickbereiche -->
<div class="actions">
<button class="icon-button" style="width: 20px; height: 20px;">
<img src="edit.svg" alt="Bearbeiten">
</button>
<button class="icon-button" style="width: 20px; height: 20px;">
<img src="delete.svg" alt="Löschen">
</button>
</div>
<!-- Komplexe Geste ohne Alternative -->
<div class="map"
ontouchmove="handlePinchZoom(event)"
ontouchstart="handleTouchStart(event)">
<!-- Karte, die nur mit Pinch-Zoom-Geste gezoomt werden kann -->
</div>
Probleme: Zu kleine Klickbereiche, komplexe Gesten ohne Alternativen.
<!-- Ausreichend große Klickbereiche -->
<div class="actions">
<button class="icon-button" style="width: 44px; height: 44px; padding: 10px;">
<img src="edit.svg" alt="Bearbeiten" style="width: 24px; height: 24px;">
</button>
<button class="icon-button" style="width: 44px; height: 44px; padding: 10px;">
<img src="delete.svg" alt="Löschen" style="width: 24px; height: 24px;">
</button>
</div>
<!-- Karte mit alternativen Bedienmöglichkeiten -->
<div class="map-container">
<div class="map"
ontouchmove="handlePinchZoom(event)"
ontouchstart="handleTouchStart(event)">
<!-- Karte -->
</div>
<div class="map-controls">
<button aria-label="Vergrößern">+</button>
<button aria-label="Verkleinern">-</button>
<div class="slider">
<label for="zoom-level">Zoom-Level</label>
<input type="range" id="zoom-level" min="1" max="10" value="5">
</div>
</div>
</div>
Gute Praxis: Ausreichend große Klickbereiche, alternative Bedienmöglichkeiten für komplexe Gesten.
import { useState } from 'react'
// Komponente mit ausreichend großen Klickbereichen
export function ActionButtons() {
return (
<div className="flex space-x-2">
<button
className="p-3 min-w-[44px] min-h-[44px] flex items-center justify-center bg-blue-100 rounded hover:bg-blue-200"
aria-label="Bearbeiten"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
aria-hidden="true"
>
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
</svg>
</button>
<button
className="p-3 min-w-[44px] min-h-[44px] flex items-center justify-center bg-red-100 rounded hover:bg-red-200"
aria-label="Löschen"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
aria-hidden="true"
>
<path d="M3 6h18"></path>
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"></path>
<path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
</svg>
</button>
</div>
)
}
// Karte mit alternativen Bedienmöglichkeiten
export function AccessibleMap() {
const [zoomLevel, setZoomLevel] = useState(5)
const handleZoomIn = () => {
setZoomLevel(prev => Math.min(prev + 1, 10))
}
const handleZoomOut = () => {
setZoomLevel(prev => Math.max(prev - 1, 1))
}
const handleZoomChange = (e) => {
setZoomLevel(parseInt(e.target.value))
}
return (
<div className="map-container border rounded overflow-hidden">
<div
className="map bg-gray-200 h-64"
style={{ transform: `scale(${zoomLevel / 5})` }}
aria-label="Interaktive Karte"
role="application"
>
{/* Karteninhalt */}
<div className="flex items-center justify-center h-full text-gray-500">
Karteninhalt (Beispiel)
</div>
</div>
<div className="map-controls bg-white p-2 flex items-center space-x-4">
<button
onClick={handleZoomOut}
className="p-2 min-w-[44px] min-h-[44px] bg-gray-100 rounded hover:bg-gray-200"
aria-label="Verkleinern"
disabled={zoomLevel === 1}
>
-
</button>
<div className="flex-1">
<label htmlFor="zoom-level" className="sr-only">Zoom-Level</label>
<input
type="range"
id="zoom-level"
min="1"
max="10"
value={zoomLevel}
onChange={handleZoomChange}
className="w-full"
/>
</div>
<button
onClick={handleZoomIn}
className="p-2 min-w-[44px] min-h-[44px] bg-gray-100 rounded hover:bg-gray-200"
aria-label="Vergrößern"
disabled={zoomLevel === 10}
>
+
</button>
</div>
</div>
)
}
Das Prinzip "Bedienbar" stellt sicher, dass Benutzeroberflächen und Navigation für alle Benutzer bedienbar sind. Die wichtigsten Aspekte sind:
Durch die Implementierung dieser Richtlinien stellen Sie sicher, dass Ihre Webseite für alle Benutzer, unabhängig von ihren Fähigkeiten oder der verwendeten Technologie, bedienbar ist.
Das dritte POUR-Prinzip besagt, dass Informationen und die Bedienung der Benutzeroberfläche für alle Benutzer verständlich sein müssen. Dies bedeutet, dass Inhalte lesbar und vorhersehbar sein müssen und dass Benutzer bei der Vermeidung und Korrektur von Fehlern unterstützt werden.
Die Angabe der Sprache ist wichtig für Screenreader, damit sie den Inhalt korrekt aussprechen können. Sie hilft auch Browsern, Texte korrekt darzustellen und Übersetzungstools, den Inhalt richtig zu übersetzen.
<!-- Keine Sprachdeklaration -->
<!DOCTYPE html>
<html>
<head>
<title>Meine Website</title>
</head>
<body>
<h1>Willkommen auf meiner Website</h1>
<p>Hier finden Sie Informationen über unsere Dienstleistungen.</p>
<!-- Fremdsprachiger Text ohne Kennzeichnung -->
<blockquote>
The only way to do great work is to love what you do.
</blockquote>
</body>
</html>
Probleme: Fehlende Sprachdeklaration für die Seite, fremdsprachige Textpassagen ohne Kennzeichnung.
<!-- Korrekte Sprachdeklaration -->
<!DOCTYPE html>
<html lang="de">
<head>
<title>Meine Website</title>
</head>
<body>
<h1>Willkommen auf meiner Website</h1>
<p>Hier finden Sie Informationen über unsere Dienstleistungen.</p>
<!-- Fremdsprachiger Text mit Kennzeichnung -->
<blockquote lang="en">
The only way to do great work is to love what you do.
</blockquote>
<!-- Übersetzung für den Kontext -->
<p><small>Übersetzung: Der einzige Weg, großartige Arbeit zu leisten, ist zu lieben, was man tut.</small></p>
</body>
</html>
Gute Praxis: Sprachdeklaration für die gesamte Seite, Kennzeichnung fremdsprachiger Textpassagen, optional Übersetzungen für den Kontext.
// In Next.js: app/layout.tsx
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="de">
<body>{children}</body>
</html>
)
}
// Komponente mit mehrsprachigem Inhalt
export function MultilingualQuote() {
return (
<div className="my-6">
<h2>Inspirierende Zitate</h2>
{/* Deutsches Zitat - keine zusätzliche Sprachkennzeichnung nötig */}
<blockquote className="border-l-4 border-gray-300 pl-4 my-4">
<p>Der Weg ist das Ziel.</p>
<footer>— Konfuzius</footer>
</blockquote>
{/* Englisches Zitat - mit Sprachkennzeichnung */}
<blockquote lang="en" className="border-l-4 border-gray-300 pl-4 my-4">
<p>The journey is the reward.</p>
<footer>— Steve Jobs</footer>
</blockquote>
{/* Französisches Zitat - mit Sprachkennzeichnung und Übersetzung */}
<blockquote lang="fr" className="border-l-4 border-gray-300 pl-4 my-4">
<p>La vie est belle.</p>
<footer>— Französisches Sprichwort</footer>
</blockquote>
<p className="text-sm text-gray-600">Übersetzung: Das Leben ist schön.</p>
</div>
)
}
lang
-Attribut im HTML-Elementlang
-AttributVorhersehbarkeit ist wichtig für alle Benutzer, besonders aber für Menschen mit kognitiven Beeinträchtigungen, Lernbehinderungen und Screenreader-Nutzer. Benutzer sollten verstehen können, wie die Website funktioniert und was passieren wird, wenn sie mit Elementen interagieren.
<!-- Automatische Weiterleitung bei Fokus -->
<select onchange="window.location = this.value">
<option value="">Bitte wählen...</option>
<option value="page1.html">Seite 1</option>
<option value="page2.html">Seite 2</option>
</select>
<!-- Inkonsistente Navigation -->
<nav>
<!-- Auf Seite 1: -->
<ul>
<li><a href="/">Startseite</a></li>
<li><a href="/produkte">Produkte</a></li>
<li><a href="/kontakt">Kontakt</a></li>
</ul>
<!-- Auf Seite 2: -->
<ul>
<li><a href="/kontakt">Kontakt</a></li>
<li><a href="/">Startseite</a></li>
<li><a href="/produkte">Produkte</a></li>
</ul>
</nav>
<!-- Inkonsistente Bezeichnungen -->
<button>Absenden</button> <!-- Auf Seite 1 -->
<button>Senden</button> <!-- Auf Seite 2, gleiche Funktion -->
Probleme: Automatische Weiterleitungen bei Fokus oder Eingabe, inkonsistente Navigation und Bezeichnungen.
<!-- Explizite Aktion erforderlich -->
<form>
<select id="page-select">
<option value="">Bitte wählen...</option>
<option value="page1.html">Seite 1</option>
<option value="page2.html">Seite 2</option>
</select>
<button type="button" onclick="navigateTo()">Gehe zu Seite</button>
</form>
<!-- Konsistente Navigation -->
<nav>
<!-- Auf allen Seiten gleich: -->
<ul>
<li><a href="/">Startseite</a></li>
<li><a href="/produkte">Produkte</a></li>
<li><a href="/kontakt">Kontakt</a></li>
</ul>
</nav>
<!-- Konsistente Bezeichnungen -->
<button>Formular absenden</button> <!-- Auf allen Seiten gleich -->
Gute Praxis: Explizite Benutzeraktionen für Kontextänderungen, konsistente Navigation und Bezeichnungen auf allen Seiten.
// components/layout/Navigation.tsx
// Konsistente Navigation für alle Seiten
export function Navigation() {
return (
<nav className="bg-gray-100 p-4">
<ul className="flex space-x-6">
<li><a href="/" className="hover:underline">Startseite</a></li>
<li><a href="/produkte" className="hover:underline">Produkte</a></li>
<li><a href="/dienstleistungen" className="hover:underline">Dienstleistungen</a></li>
<li><a href="/ueber-uns" className="hover:underline">Über uns</a></li>
<li><a href="/kontakt" className="hover:underline">Kontakt</a></li>
</ul>
</nav>
)
}
// components/forms/LanguageSelector.tsx
// Vorhersehbares Verhalten bei Eingabe
import { useState } from 'react'
export function LanguageSelector() {
const [language, setLanguage] = useState('')
const [showConfirmation, setShowConfirmation] = useState(false)
const handleLanguageChange = (e) => {
setLanguage(e.target.value)
// Keine automatische Weiterleitung oder Kontextänderung
}
const applyLanguageChange = () => {
if (language) {
setShowConfirmation(true)
// Hier könnte die Sprache der Anwendung geändert werden
}
}
return (
<div className="my-4">
<label htmlFor="language-select" className="block mb-2">Sprache auswählen:</label>
<select
id="language-select"
value={language}
onChange={handleLanguageChange}
className="border p-2 rounded mr-2"
>
<option value="">Bitte wählen...</option>
<option value="de">Deutsch</option>
<option value="en">Englisch</option>
<option value="fr">Französisch</option>
</select>
<button
onClick={applyLanguageChange}
className="bg-blue-600 text-white px-4 py-2 rounded"
>
Sprache ändern
</button>
{showConfirmation && (
<div className="mt-2 p-2 bg-green-100 text-green-800 rounded">
Sprache wurde auf {language === 'de' ? 'Deutsch' : language === 'en' ? 'Englisch' : 'Französisch'} geändert.
</div>
)}
</div>
)
}
Eingabehilfen sind wichtig, um Benutzern zu helfen, Fehler zu vermeiden und zu korrigieren. Dies ist besonders wichtig für Menschen mit kognitiven Beeinträchtigungen, aber auch für alle anderen Benutzer.
<!-- Formular ohne Beschriftungen und Fehlermeldungen -->
<form>
<input type="text" name="name" required>
<input type="email" name="email" required>
<input type="password" name="password" required>
<button type="submit">Registrieren</button>
</form>
<!-- Fehlermeldung ohne Identifikation des fehlerhaften Elements -->
<div class="error">
Es ist ein Fehler aufgetreten. Bitte überprüfen Sie Ihre Eingaben.
</div>
Probleme: Fehlende Beschriftungen, keine spezifischen Fehlermeldungen, keine Hilfestellung zur Korrektur.
<!-- Formular mit Beschriftungen und Fehlermeldungen -->
<form>
<div>
<label for="name">Name</label>
<input type="text" id="name" name="name" required aria-describedby="name-error">
<div id="name-error" class="error" role="alert" hidden>
Bitte geben Sie Ihren Namen ein.
</div>
</div>
<div>
<label for="email">E-Mail</label>
<input type="email" id="email" name="email" required aria-describedby="email-error">
<div id="email-error" class="error" role="alert" hidden>
Bitte geben Sie eine gültige E-Mail-Adresse ein.
</div>
</div>
<div>
<label for="password">Passwort</label>
<input type="password" id="password" name="password" required
aria-describedby="password-hint password-error">
<div id="password-hint" class="hint">
Das Passwort muss mindestens 8 Zeichen lang sein und Zahlen enthalten.
</div>
<div id="password-error" class="error" role="alert" hidden>
Das Passwort entspricht nicht den Anforderungen.
</div>
</div>
<button type="submit">Registrieren</button>
</form>
Gute Praxis: Klare Beschriftungen, spezifische Fehlermeldungen, Hinweise zur Korrektur, ARIA-Attribute für Screenreader.
import { useState } from 'react'
export function AccessibleForm() {
const [formData, setFormData] = useState({
name: '',
email: '',
password: ''
})
const [errors, setErrors] = useState({
name: '',
email: '',
password: ''
})
const [submitted, setSubmitted] = useState(false)
const handleChange = (e) => {
const { name, value } = e.target
setFormData({
...formData,
[name]: value
})
// Echtzeit-Validierung
validateField(name, value)
}
const validateField = (name, value) => {
let errorMessage = ''
switch (name) {
case 'name':
errorMessage = value.trim() === '' ? 'Name ist erforderlich' : ''
break
case 'email':
const emailRegex = /^[^s@]+@[^s@]+.[^s@]+$/
errorMessage = !emailRegex.test(value) ? 'Gültige E-Mail-Adresse erforderlich' : ''
break
case 'password':
errorMessage = value.length < 8 ? 'Passwort muss mindestens 8 Zeichen lang sein' :
!/d/.test(value) ? 'Passwort muss mindestens eine Zahl enthalten' : ''
break
default:
break
}
setErrors(prev => ({
...prev,
[name]: errorMessage
}))
return errorMessage === ''
}
const handleSubmit = (e) => {
e.preventDefault()
// Alle Felder validieren
let formIsValid = true
Object.keys(formData).forEach(field => {
const isValid = validateField(field, formData[field])
if (!isValid) formIsValid = false
})
if (formIsValid) {
// Formular absenden
setSubmitted(true)
} else {
// Fokus auf das erste Feld mit Fehler setzen
const firstErrorField = Object.keys(errors).find(field => errors[field] !== '')
if (firstErrorField) {
document.getElementById(firstErrorField)?.focus()
}
}
}
if (submitted) {
return (
<div className="p-4 bg-green-100 text-green-800 rounded" role="alert">
<h2 className="text-xl font-bold mb-2">Registrierung erfolgreich!</h2>
<p>Vielen Dank für Ihre Anmeldung.</p>
</div>
)
}
return (
<form onSubmit={handleSubmit} noValidate className="space-y-6">
<div>
<label htmlFor="name" className="block mb-1 font-medium">
Name
</label>
<input
type="text"
id="name"
name="name"
value={formData.name}
onChange={handleChange}
aria-invalid={errors.name !== ''}
aria-describedby={errors.name ? 'name-error' : undefined}
className={`w-full p-2 border rounded ${errors.name ? 'border-red-500' : 'border-gray-300'}`}
/>
{errors.name && (
<div id="name-error" className="mt-1 text-red-500 text-sm" role="alert">
{errors.name}
</div>
)}
</div>
<div>
<label htmlFor="email" className="block mb-1 font-medium">
E-Mail
</label>
<input
type="email"
id="email"
name="email"
value={formData.email}
onChange={handleChange}
aria-invalid={errors.email !== ''}
aria-describedby={errors.email ? 'email-error' : undefined}
className={`w-full p-2 border rounded ${errors.email ? 'border-red-500' : 'border-gray-300'}`}
/>
{errors.email && (
<div id="email-error" className="mt-1 text-red-500 text-sm" role="alert">
{errors.email}
</div>
)}
</div>
<div>
<label htmlFor="password" className="block mb-1 font-medium">
Passwort
</label>
<input
type="password"
id="password"
name="password"
value={formData.password}
onChange={handleChange}
aria-invalid={errors.password !== ''}
aria-describedby="password-hint password-error"
className={`w-full p-2 border rounded ${errors.password ? 'border-red-500' : 'border-gray-300'}`}
/>
<div id="password-hint" className="mt-1 text-sm text-gray-600">
Das Passwort muss mindestens 8 Zeichen lang sein und Zahlen enthalten.
</div>
{errors.password && (
<div id="password-error" className="mt-1 text-red-500 text-sm" role="alert">
{errors.password}
</div>
)}
</div>
<button
type="submit"
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
>
Registrieren
</button>
</form>
)
}
Das Prinzip "Verständlich" stellt sicher, dass Informationen und die Bedienung der Benutzeroberfläche für alle Benutzer verständlich sind. Die wichtigsten Aspekte sind:
Durch die Implementierung dieser Richtlinien stellen Sie sicher, dass Ihre Webseite für alle Benutzer, insbesondere für Menschen mit kognitiven Beeinträchtigungen und Lernbehinderungen, verständlich und nutzbar ist.
Das vierte POUR-Prinzip besagt, dass Inhalte robust genug sein müssen, um von einer Vielzahl von Benutzeragenten, einschließlich Hilfstechnologien, zuverlässig interpretiert werden zu können. Dies bedeutet, dass Webseiten mit aktuellen und zukünftigen Technologien kompatibel sein müssen.
Bei Inhalten, die mit Auszeichnungssprachen implementiert werden, haben Elemente vollständige Start- und End-Tags, Elemente sind gemäß ihrer Spezifikation verschachtelt, Elemente enthalten keine doppelten Attribute, und alle IDs sind eindeutig, außer wenn die Spezifikationen diese Eigenschaften erlauben.
Für alle Benutzeroberflächen-Komponenten (einschließlich, aber nicht beschränkt auf: Formularelemente, Links und durch Skripte generierte Komponenten) können Name und Rolle programmatisch bestimmt werden; Zustände, Eigenschaften und Werte, die vom Benutzer festgelegt werden können, können programmatisch festgelegt werden; und Benachrichtigungen über Änderungen an diesen Elementen stehen Benutzeragenten, einschließlich Hilfstechnologien, zur Verfügung.
In Inhalten, die mit Auszeichnungssprachen implementiert sind, können Statusmeldungen programmatisch bestimmt werden durch Rolle oder Eigenschaften, so dass sie dem Benutzer durch Hilfstechnologien ohne Fokus präsentiert werden können.
Robustheit bedeutet, dass Webseiten mit verschiedenen Browsern, Geräten und Hilfstechnologien funktionieren müssen. Dies erfordert die Einhaltung von Standards und die Bereitstellung von ausreichenden Informationen für Hilfstechnologien.
Valider HTML-Code ist die Grundlage für robuste Webseiten. Fehler in der HTML-Struktur können dazu führen, dass Hilfstechnologien den Inhalt nicht korrekt interpretieren können.
<!-- Ungültiges HTML mit fehlenden End-Tags und doppelten IDs -->
<div id="content">
<h2>Überschrift
<p id="content">Dieser Absatz hat die gleiche ID wie das div-Element.
<ul>
<li>Erster Punkt
<li>Zweiter Punkt
</div>
Probleme: Fehlende End-Tags, doppelte ID 'content', falsche Verschachtelung.
<!-- Valides HTML mit korrekten Tags und eindeutigen IDs -->
<div id="content">
<h2>Überschrift</h2>
<p id="description">Dieser Absatz hat eine eindeutige ID.</p>
<ul>
<li>Erster Punkt</li>
<li>Zweiter Punkt</li>
</ul>
</div>
Gute Praxis: Vollständige Start- und End-Tags, eindeutige IDs, korrekte Verschachtelung.
ARIA (Accessible Rich Internet Applications) ermöglicht es, komplexe Benutzeroberflächen für Hilfstechnologien zugänglich zu machen, indem es zusätzliche semantische Informationen bereitstellt.
<!-- Benutzerdefiniertes Dropdown ohne ARIA -->
<div class="dropdown">
<div class="dropdown-toggle" onclick="toggleDropdown()">
Menü
</div>
<div class="dropdown-menu">
<div onclick="selectOption('Option 1')">Option 1</div>
<div onclick="selectOption('Option 2')">Option 2</div>
</div>
</div>
Probleme: Keine semantische Struktur, keine Tastaturzugänglichkeit, keine ARIA-Attribute für Screenreader.
<!-- Benutzerdefiniertes Dropdown mit ARIA -->
<div class="dropdown">
<button
id="dropdown-toggle"
class="dropdown-toggle"
aria-haspopup="listbox"
aria-expanded="false"
aria-controls="dropdown-menu"
>
Menü
</button>
<ul
id="dropdown-menu"
role="listbox"
class="dropdown-menu"
aria-labelledby="dropdown-toggle"
hidden
>
<li role="option" tabindex="-1" id="option-1">Option 1</li>
<li role="option" tabindex="-1" id="option-2">Option 2</li>
</ul>
</div>
Gute Praxis: Semantische Elemente, ARIA-Rollen und -Eigenschaften, die Beziehungen und Zustände definieren.
Statusmeldungen informieren Benutzer über Änderungen auf der Webseite, die möglicherweise nicht im Fokus stehen. Diese müssen für Hilfstechnologien zugänglich sein.
// JavaScript-Code, der eine Statusmeldung hinzufügt
function addToCart() {
// Artikel zum Warenkorb hinzufügen
// Statusmeldung anzeigen
const message = document.createElement('div');
message.className = 'status-message';
message.textContent = 'Artikel wurde zum Warenkorb hinzugefügt';
document.body.appendChild(message);
// Nachricht nach 3 Sekunden ausblenden
setTimeout(() => {
message.remove();
}, 3000);
}
Probleme: Die Statusmeldung wird nicht für Screenreader angekündigt, da sie keine ARIA-Rolle oder -Eigenschaften hat.
// JavaScript-Code, der eine zugängliche Statusmeldung hinzufügt
function addToCart() {
// Artikel zum Warenkorb hinzufügen
// Zugängliche Statusmeldung anzeigen
const message = document.createElement('div');
message.className = 'status-message';
message.setAttribute('role', 'status');
message.setAttribute('aria-live', 'polite');
message.textContent = 'Artikel wurde zum Warenkorb hinzugefügt';
document.body.appendChild(message);
// Nachricht nach 3 Sekunden ausblenden
setTimeout(() => {
message.remove();
}, 3000);
}
Gute Praxis: Die Statusmeldung verwendet role='status' und aria-live='polite', damit Screenreader die Änderung ankündigen.
import { useState, useRef, useEffect } from 'react'
// Zugängliche Statusmeldungskomponente
export function StatusMessage({ message, duration = 3000 }) {
const [visible, setVisible] = useState(true)
useEffect(() => {
const timer = setTimeout(() => {
setVisible(false)
}, duration)
return () => clearTimeout(timer)
}, [duration])
if (!visible) return null
return (
<div
role="status"
aria-live="polite"
className="fixed bottom-4 right-4 bg-black text-white p-4 rounded-md shadow-lg"
>
{message}
</div>
)
}
// Zugängliches Dropdown-Menü
export function AccessibleDropdown({ label, options, onSelect }) {
const [isOpen, setIsOpen] = useState(false)
const [selectedOption, setSelectedOption] = useState(null)
const dropdownRef = useRef(null)
const optionsRef = useRef([])
const toggleDropdown = () => setIsOpen(!isOpen)
const handleKeyDown = (e) => {
if (e.key === 'Escape') {
setIsOpen(false)
}
}
const selectOption = (option, index) => {
setSelectedOption(option)
onSelect(option)
setIsOpen(false)
}
const handleOptionKeyDown = (e, option, index) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault()
selectOption(option, index)
}
}
useEffect(() => {
const handleClickOutside = (e) => {
if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
setIsOpen(false)
}
}
document.addEventListener('mousedown', handleClickOutside)
return () => document.removeEventListener('mousedown', handleClickOutside)
}, [])
return (
<div ref={dropdownRef} className="relative" onKeyDown={handleKeyDown}>
<button
id="dropdown-button"
aria-haspopup="listbox"
aria-expanded={isOpen}
aria-controls="dropdown-menu"
aria-label={label}
className="px-4 py-2 border border-gray-300 rounded-md bg-white"
onClick={toggleDropdown}
>
{selectedOption ? selectedOption.label : label}
</button>
{isOpen && (
<ul
id="dropdown-menu"
role="listbox"
aria-labelledby="dropdown-button"
className="absolute z-10 w-full mt-1 bg-white border border-gray-300 rounded-md shadow-lg"
>
{options.map((option, index) => (
<li
key={option.value}
id={`option-${option.value}`}
role="option"
aria-selected={selectedOption?.value === option.value}
tabIndex={0}
className={`px-4 py-2 cursor-pointer hover:bg-gray-100 ${
selectedOption?.value === option.value ? "bg-gray-100" : ""
}`}
onClick={() => selectOption(option, index)}
onKeyDown={(e) => handleOptionKeyDown(e, option, index)}
ref={(el) => (optionsRef.current[index] = el)}
>
{option.label}
</li>
))}
</ul>
)}
</div>
)
}
Obwohl nicht explizit in den WCAG 2.1 Richtlinien enthalten, ist Zukunftskompatibilität ein wichtiger Aspekt der Robustheit. Webseiten sollten so entwickelt werden, dass sie auch mit zukünftigen Technologien und Standards kompatibel sind.
Beginnen Sie mit einer grundlegenden, funktionalen Webseite und fügen Sie fortschrittliche Funktionen hinzu, die nur verwendet werden, wenn der Browser sie unterstützt.
// Beispiel für Feature-Detection
if ('IntersectionObserver' in window) {
// Moderne Browser: Lazy Loading mit IntersectionObserver
const observer = new IntersectionObserver(/* ... */);
} else {
// Ältere Browser: Alternative Implementierung
window.addEventListener('scroll', /* ... */);
}
Verwenden Sie Polyfills, um neue Funktionen in älteren Browsern zu simulieren, die diese noch nicht nativ unterstützen.
// Polyfill für fetch in älteren Browsern
if (!window.fetch) {
window.fetch = function(url, options) {
return new Promise(function(resolve, reject) {
const xhr = new XMLHttpRequest();
xhr.open(options?.method || 'GET', url);
xhr.onload = function() {
resolve({
status: xhr.status,
statusText: xhr.statusText,
text: function() {
return Promise.resolve(xhr.responseText);
}
});
};
xhr.onerror = function() {
reject(new TypeError('Network request failed'));
};
xhr.send(options?.body);
});
};
}
Einige Technologien und Praktiken sind besonders gut geeignet, um die Zukunftskompatibilität zu gewährleisten:
Die Robustheit einer Webseite ist nicht nur für die Barrierefreiheit wichtig, sondern auch für die allgemeine Benutzerfreundlichkeit und Wartbarkeit. Durch die Einhaltung von Standards und die Verwendung von bewährten Praktiken stellen Sie sicher, dass Ihre Webseite für alle Benutzer zugänglich ist, unabhängig von den verwendeten Technologien.
Das Prinzip "Robust" stellt sicher, dass Inhalte robust genug sind, um von einer Vielzahl von Benutzeragenten, einschließlich Hilfstechnologien, zuverlässig interpretiert werden zu können. Die wichtigsten Aspekte sind:
Durch die Implementierung dieser Richtlinien stellen Sie sicher, dass Ihre Webseite mit verschiedenen Browsern, Geräten und Hilfstechnologien kompatibel ist und auch in Zukunft zugänglich bleibt.
Das Testen und Validieren ist ein entscheidender Schritt, um sicherzustellen, dass Ihre Webseite die WCAG-Richtlinien erfüllt. Es gibt verschiedene Methoden und Tools, die Ihnen dabei helfen können, Barrierefreiheitsprobleme zu identifizieren und zu beheben.
Automatisierte Tests können viele Barrierefreiheitsprobleme schnell identifizieren, haben jedoch auch Einschränkungen. Sie sollten als erster Schritt im Testprozess betrachtet werden, gefolgt von manuellen Tests und Benutzertests.
Für eine effektive Barrierefreiheitsstrategie sollten automatisierte Tests in den gesamten Entwicklungsprozess integriert werden:
Phase | Integration | Vorteile |
---|---|---|
Entwicklung | Browser-Erweiterungen, IDE-Plugins | Sofortiges Feedback für Entwickler |
Build-Prozess | Linting-Tools, Pre-commit Hooks | Verhindert, dass nicht barrierefreier Code ins Repository gelangt |
CI/CD-Pipeline | Automatisierte Tests als Teil der Pipeline | Kontinuierliche Überwachung der Barrierefreiheit |
Produktion | Monitoring-Tools, regelmäßige Audits | Identifizierung von Problemen in der Live-Umgebung |
Automatisierte Tests können nur etwa 30-40% der WCAG-Erfolgskriterien überprüfen. Sie können nicht:
Daher sind manuelle Tests und Benutzertests unerlässlich für eine umfassende Barrierefreiheitsprüfung.
Manuelle Tests sind notwendig, um Aspekte der Barrierefreiheit zu überprüfen, die automatisierte Tools nicht erfassen können. Sie erfordern menschliches Urteilsvermögen und Verständnis für den Kontext.
Überprüfen Sie, ob alle Funktionen der Webseite mit der Tastatur zugänglich sind:
Testen Sie Ihre Webseite mit einem Screenreader, um die Erfahrung blinder und sehbehinderter Benutzer zu verstehen:
Aktion | Tastenkombination |
---|---|
NVDA starten/beenden | NVDA + Q |
Alles vorlesen | NVDA + Pfeil nach unten |
Vorlesen stoppen | Strg |
Zur nächsten Überschrift | H |
Zur nächsten Tabelle | T |
Zum nächsten Formularfeld | F |
Zum nächsten Landmark | D |
Überprüfen Sie, ob die Farben und Kontraste Ihrer Webseite den WCAG-Anforderungen entsprechen:
Benutzertests mit Menschen mit Behinderungen sind der effektivste Weg, um die tatsächliche Barrierefreiheit Ihrer Webseite zu bewerten. Sie bieten Einblicke, die durch automatisierte und manuelle Tests allein nicht gewonnen werden können.
Die Rekrutierung von Teilnehmern mit Behinderungen kann eine Herausforderung sein. Hier sind einige Möglichkeiten:
Es gibt eine Vielzahl von Tools, die Ihnen bei der Überprüfung der Barrierefreiheit Ihrer Webseite helfen können. Hier ist eine Übersicht der nützlichsten Tools für verschiedene Aspekte der Barrierefreiheit.
Umfassendes Tool zur Bewertung der Barrierefreiheit mit visueller Feedback-Darstellung direkt auf der Webseite.
Besonderheiten: Visuelle Anzeige von Fehlern, Warnungen und strukturellen Elementen
Leistungsstarkes Tool für Entwickler, das sich in die Browser-Entwicklertools integriert.
Besonderheiten: Genaue Ergebnisse, geringe Falsch-Positiv-Rate, API für automatisierte Tests
Von Microsoft entwickeltes Tool mit automatisierten und manuellen Testfunktionen.
Besonderheiten: FastPass für schnelle Tests, Assessment für umfassende Prüfungen, Tab-Stops-Visualisierung
Moderne Browser bieten integrierte Tools zur Überprüfung der Barrierefreiheit:
Chrome bietet mehrere Funktionen zur Überprüfung der Barrierefreiheit:
Firefox bietet einen speziellen Accessibility Inspector:
Das Testen und Validieren der Barrierefreiheit ist ein kontinuierlicher Prozess, der verschiedene Methoden und Tools erfordert:
Durch die Kombination dieser Methoden und Tools können Sie sicherstellen, dass Ihre Webseite für alle Benutzer zugänglich ist und die WCAG-Richtlinien erfüllt.
Es gibt eine Vielzahl von Ressourcen und Tools, die Ihnen helfen können, barrierefreie Webseiten zu erstellen und zu testen. Hier finden Sie eine Auswahl der nützlichsten Ressourcen für Entwickler, Designer und Content-Ersteller.
Diese Tools helfen Entwicklern, barrierefreien Code zu schreiben und zu testen.
import { useRef, useEffect } from 'react';
function Modal({ isOpen, onClose, title, children }) {
const modalRef = useRef(null);
const previousFocusRef = useRef(null);
useEffect(() => {
if (isOpen) {
// Speichern des aktuellen Fokus
previousFocusRef.current = document.activeElement;
// Fokus auf das Modal setzen
modalRef.current?.focus();
// Tastatur-Event-Listener für Escape-Taste
const handleKeyDown = (e) => {
if (e.key === 'Escape') {
onClose();
}
};
document.addEventListener('keydown', handleKeyDown);
// Scroll des Hintergrunds verhindern
document.body.style.overflow = 'hidden';
return () => {
// Cleanup
document.removeEventListener('keydown', handleKeyDown);
document.body.style.overflow = '';
// Fokus zurücksetzen
previousFocusRef.current?.focus();
};
}
}, [isOpen, onClose]);
if (!isOpen) return null;
return (
<div
className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"
onClick={onClose}
>
<div
ref={modalRef}
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
tabIndex={-1}
className="bg-white rounded-lg p-6 max-w-md w-full"
onClick={(e) => e.stopPropagation()}
>
<h2 id="modal-title" className="text-xl font-bold mb-4">{title}</h2>
<div>{children}</div>
<div className="mt-6 flex justify-end">
<button
onClick={onClose}
className="px-4 py-2 bg-gray-200 rounded hover:bg-gray-300"
>
Schließen
</button>
</div>
</div>
</div>
);
}
import { useState, useRef, useEffect } from 'react';
function Dropdown({ label, options, onSelect }) {
const [isOpen, setIsOpen] = useState(false);
const [activeIndex, setActiveIndex] = useState(-1);
const dropdownRef = useRef(null);
const optionsRef = useRef([]);
const toggleDropdown = () => setIsOpen(!isOpen);
const handleSelect = (option, index) => {
onSelect(option);
setIsOpen(false);
setActiveIndex(-1);
};
const handleKeyDown = (e) => {
if (!isOpen && (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ')) {
e.preventDefault();
setIsOpen(true);
setActiveIndex(0);
} else if (isOpen && e.key === 'Escape') {
setIsOpen(false);
setActiveIndex(-1);
} else if (isOpen && e.key === 'ArrowDown') {
e.preventDefault();
setActiveIndex((prevIndex) =>
prevIndex < options.length - 1 ? prevIndex + 1 : prevIndex
);
} else if (isOpen && e.key === 'ArrowUp') {
e.preventDefault();
setActiveIndex((prevIndex) =>
prevIndex > 0 ? prevIndex - 1 : prevIndex
);
} else if (isOpen && (e.key === 'Enter' || e.key === ' ') && activeIndex >= 0) {
e.preventDefault();
handleSelect(options[activeIndex], activeIndex);
}
};
useEffect(() => {
if (isOpen && activeIndex >= 0) {
optionsRef.current[activeIndex]?.scrollIntoView({
block: 'nearest'
});
optionsRef.current[activeIndex]?.focus();
}
}, [isOpen, activeIndex]);
useEffect(() => {
const handleClickOutside = (e) => {
if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
setIsOpen(false);
setActiveIndex(-1);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);
return (
<div ref={dropdownRef} className="relative">
<button
id="dropdown-button"
aria-haspopup="listbox"
aria-expanded={isOpen}
aria-controls="dropdown-menu"
className="px-4 py-2 border border-gray-300 rounded-md bg-white"
onClick={toggleDropdown}
onKeyDown={handleKeyDown}
>
{label}
</button>
{isOpen && (
<ul
id="dropdown-menu"
role="listbox"
aria-labelledby="dropdown-button"
className="absolute z-10 w-full mt-1 bg-white border border-gray-300 rounded-md shadow-lg max-h-60 overflow-auto"
>
{options.map((option, index) => (
<li
key={option.value}
id={`option-${option.value}`}
role="option"
aria-selected={index === activeIndex}
tabIndex={index === activeIndex ? 0 : -1}
ref={(el) => (optionsRef.current[index] = el)}
className={`px-4 py-2 cursor-pointer hover:bg-gray-100 ${
index === activeIndex ? 'bg-gray-100' : ''
}`}
onClick={() => handleSelect(option, index)}
onKeyDown={handleKeyDown}
>
{option.label}
</li>
))}
</ul>
)}
</div>
);
}
Diese Tools helfen Designern, barrierefreie Designs zu erstellen und zu überprüfen.
Diese Ressourcen helfen Ihnen, mehr über digitale Barrierefreiheit zu lernen und Ihre Kenntnisse zu vertiefen.
Der Austausch mit anderen Fachleuten und die Teilnahme an der Barrierefreiheits-Community können Ihnen helfen, Ihre Kenntnisse zu erweitern und auf dem neuesten Stand zu bleiben.
Folgen Sie diesen Experten und Influencern, um auf dem neuesten Stand der Barrierefreiheit zu bleiben:
Es gibt eine Vielzahl von Ressourcen und Tools, die Ihnen helfen können, barrierefreie Webseiten zu erstellen:
Durch die Nutzung dieser Ressourcen können Sie Ihre Kenntnisse und Fähigkeiten im Bereich der digitalen Barrierefreiheit kontinuierlich verbessern und auf dem neuesten Stand bleiben.
Diese Richtlinien bieten einen Einblick in die umfangreichen WCAG-Anforderungen. Lassen Sie uns gemeinsam Ihre Website analysieren und einen Plan zur Verbesserung der Barrierefreiheit erstellen.
Kostenlose Analyse anfordern