Komponenten · Navigation & Bedienelemente
Pagination & „Mehr laden“
Eine barrierefreie Seitennavigation ist ein <nav> mit echten Links, bei dem die aktuelle Seite aria-current="page" trägt und sichtbar mehr als nur farblich markiert ist. „Mehr laden“ braucht darüber hinaus zwei Dinge, die man leicht vergisst: eine Ansage über eine Live-Region und einen Fokus, der zum neuen Inhalt wandert.
Lange Trefferlisten müssen aufgeteilt werden – die Frage ist nur, wie. Alle drei üblichen Muster lassen sich zugänglich bauen, aber sie starten von sehr unterschiedlichen Ausgangslagen, und eines davon hat eine Schattenseite, die man nicht wegprogrammieren kann.
Das Wichtigste in Kürze
-
Echte
<a href>in der Pagination – keinhref="#", keinjavascript:void(0), keine Klick-<div>s. - Die aktuelle Seite bleibt ein Link mit echtem Ziel und bekommt
aria-current="page". Genau ein Element pro Set. - Farbe allein reicht nicht: Die Markierung braucht ein zweites, nicht-farbliches Merkmal und mindestens 3:1 Kontrast – zum Hintergrund und zu den übrigen Seitenzahlen.
- 24 × 24 CSS-Pixel Zielgröße als Untergrenze (2.5.8, Stufe AA) – Seitenzahlen sind hier notorische Sünder.
- „Mehr laden“: erst ansagen, dann den Fokus verlagern. Ohne beides sind zehn neue Einträge da, von denen niemand erfährt.
- SEO: eigene URL pro Seite, selbstreferenzierendes Canonical, keine
#-Fragmente – und crawlbare Links hinter jedem Nachlade-Button.
Welches Muster wann?
Die Entscheidung hängt weniger am Geschmack als an der Frage, was Nutzende vorhaben:
| Situation | Passendes Muster |
|---|---|
| Gezielt etwas suchen und finden | Pagination |
| Treffer untereinander vergleichen | Pagination |
| Eine bestimmte Position wiederfinden oder teilen | Pagination |
| Ohne festes Ziel stöbern (Feed, Galerie) | „Mehr laden“ |
| Homogener Strom ohne Ende und ohne Ziel | Infinite Scroll (mit Vorbehalt) |
| Suchmaschinen sollen alle Einträge finden | Pagination oder crawlbare Links |
Die Nielsen Norman Group rät ausdrücklich von Infinite Scroll ab, wenn Nutzende etwas Bestimmtes suchen, Einträge in einer langen Liste vergleichen wollen, mit geringer Bandbreite unterwegs sind – oder wenn viele Menschen mit Behinderungen zur Zielgruppe gehören.
Klassische Pagination: das Grundgerüst
Eine Seitennavigation ist eine Navigation mit einer Liste echter Links:
<nav aria-label="Seitennavigation">
<ul>
<li><span class="pg-aus">Zurück</span></li>
<li><a href="?seite=1">Seite 1</a></li>
<li><a href="?seite=2" aria-current="page">Seite 2</a></li>
<li><a href="?seite=3">Seite 3</a></li>
<li><a href="?seite=3">Weiter</a></li>
</ul>
</nav>
Vier Entscheidungen stecken darin:
Das aria-label an der <nav>. Sobald es mehr als eine Navigation auf der Seite gibt, braucht jede einen eigenen Namen (2.4.6). „Seitennavigation“ ist gut; „Navigation Seitennavigation“ wäre doppelt gemoppelt, weil die Rolle ohnehin angesagt wird.
Die aktuelle Seite bleibt ein Link. Das ist kontraintuitiv – warum auf die Seite verlinken, auf der man schon ist? Die Begründung des US Web Design System ist pragmatisch: „Use a link for the current page for robustness.“ Wer per Screenreader durch die Elementliste springt, findet die aktuelle Seite dann zuverlässig als Link mit dem Zustand „aktuelle Seite“. Ein href="#" oder ein <span> an dieser Stelle nimmt diese Information weg.
<ul> oder <ol>? Beides ist vertretbar. Für reines Durchblättern spricht etwas für <ol>; sobald „Zurück“ und „Weiter“ mit in der Liste stehen, ist die Nummerierung eher irreführend. USWDS und das BBC-Designsystem nehmen <ul> – eine Pflicht zur Reihenfolge-Semantik gibt es nicht.
Wie viele Zahlen? USWDS legt maximal sieben Slots fest und hält diese Anzahl über alle Seiten des Sets konstant. Das ist keine Norm, aber eine gute Regel: Ein Layout, das je nach Seite zwischen fünf und elf Elementen springt, verschiebt bei jedem Klick die Klickziele.
aria-current="page" richtig einsetzen
aria-current markiert das Element innerhalb einer Gruppe, das gerade „dran“ ist. Es kennt genau sieben gültige Werte:
| Wert | Bedeutung |
|---|---|
page |
aktuelle Seite in einer Seitennavigation |
step |
aktueller Schritt in einem Prozess |
location |
aktueller Ort, z. B. in einem Fortschrittsbalken |
date |
aktuelles Datum in einem Kalender |
time |
aktuelle Uhrzeit |
true |
aktuelles Element ohne genauere Angabe |
false |
nicht aktuell (wird nicht exponiert) |
Zwei Fallstricke: Jeder andere nicht-leere Wert wird wie true behandelt – ein Tippfehler wie aria-current="pages" fällt also nicht auf false zurück, sondern erzeugt eine unpräzise Ansage. Und: Nur ein Element pro Gruppe darf markiert sein.
aria-current="page" ist dabei kein Ersatz für die sichtbare Kennzeichnung – sondern deren Gegenstück für Hilfsmittel.
Sichtbar machen: 3:1 statt nur Farbe
Die Handreichung der Überwachungsstelle des Bundes für Barrierefreiheit von Informationstechnik (BFIT-Bund) ist hier ungewöhnlich konkret. Anforderung 817 (Muss): „Die Markierung für die aktuelle Seite muss zum Hintergrund sowie den sonstigen Seiten ein Kontrastverhältnis von mindestens 3:1 aufweisen.“
Das sind zwei Messungen, nicht eine: Die Markierung muss sich vom Hintergrund abheben und von den anderen Seitenzahlen. Dazu kommt 1.4.1 Benutzung von Farbe: Farbe allein darf nicht das einzige Unterscheidungsmerkmal sein.
/* Nicht ausreichend: nur Farbe */
.pg [aria-current="page"] { color: #2563eb; }
/* Ausreichend: Fläche, Form und Schriftschnitt kommen dazu */
.pg [aria-current="page"] {
background: #2563eb;
color: #fff;
border-radius: 0.5rem;
font-weight: 700;
text-decoration: none;
}
Nachmessen kannst du das mit dem Kontrast-Check; die Hintergründe stehen unter Farbkontraste.
Zugängliche Namen für Zahlen, Pfeile und Icons
Eine „3“ allein ist kein guter Linkname. Zwei Wege führen laut BFIT-Bund (Anforderung 821) zum Ziel: Entweder der zugängliche Name wird ergänzt – oder die Schalter liegen in einer Gruppe, die als „Paginierung“ beschriftet ist.
Wenn du ergänzt, dann mit visuell verstecktem Text statt mit aria-label:
<li>
<a href="?seite=3">
<span class="visually-hidden">Seite </span>3
</a>
</li>
<li>
<a href="?seite=3">
<svg aria-hidden="true" focusable="false" viewBox="0 0 24 24" width="16" height="16">
<path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2"/>
</svg>
Weiter<span class="visually-hidden">, Seite 3</span>
</a>
</li>
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0 0 0 0);
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}
Warum nicht einfach aria-label="Seite 3"? Der spezifikationsgestützte Grund ist 2.5.3 Beschriftung im Namen: Wer per Sprache bedient, sagt „Klick 3“ – und dafür muss der sichtbare Text im zugänglichen Namen enthalten sein. aria-label ersetzt den sichtbaren Text dagegen komplett. Dazu kommt eine praktische Beobachtung aus dem HTMHell-Adventskalender (getestet im Dezember 2024 mit NVDA 2024.4 und Chrome 130): Englischer Text in einem aria-label wurde mit deutscher Stimme vorgelesen, während Text aus einer .sr-only-Klasse korrekt ausgesprochen wurde. Ein Einzeltest, kein Spezifikationsverhalten – aber ein weiterer Grund, dem sichtbaren Text den Vorzug zu geben.
Und ein handfester Fehler zum Schluss: aria-label wirkt nicht auf Elementen ohne Rolle. <span aria-label="Seite 3">3</span> ist wirkungslos – das ist eine ARIA-Regel, kein Browser-Bug.
Auslassungspunkte: „…“ wird nicht vorgelesen
Bei langen Sets stehen zwischen den Zahlen Auslassungspunkte. Screenreader sagen „…“ in der Regel nicht an – die Information, dass Seiten übersprungen wurden, geht damit verloren.
<li>
<span aria-hidden="true">…</span>
<span class="visually-hidden">Seiten 4 bis 9 übersprungen</span>
</li>
Wichtig: Das Overflow-Element ist kein Link und kein Button. Es ist ein Hinweis, kein Bedienelement.
Deaktivierte „Zurück“- und „Weiter“-Elemente
Auf der ersten Seite gibt es kein „Zurück“. Die verbreitete Lösung – ein <a>, das per CSS ausgegraut wird, aber weiter verlinkt – ist die schlechteste: Sie sieht deaktiviert aus und funktioniert trotzdem.
<!-- Falsch: sieht deaktiviert aus, ist es aber nicht -->
<li><a href="?seite=0" class="deaktiviert">Zurück</a></li>
<!-- Richtig: gar kein Bedienelement -->
<li><span class="pg-aus">Zurück</span></li>
Ein <span> ist hier die sauberste Lösung: kein Link, keine Rolle, kein Fokusziel, kein ARIA. Die manchmal empfohlene Variante <a role="link" aria-disabled="true"> ohne href hat einen Haken – ein <a> ohne href ist nicht fokussierbar, die Ansage „deaktiviert“ erreicht Tastaturnutzende also gar nicht erst. Und aria-disabled ist ohnehin rein semantisch: Es verhindert nichts, es sagt nur etwas an.
Klickfläche: 24 × 24 CSS-Pixel
2.5.8 Zielgröße (Minimum) verlangt seit WCAG 2.2 auf Stufe AA mindestens 24 × 24 CSS-Pixel für Klickziele. Gemeint ist die klickbare Fläche – Padding zählt mit, die Schriftgröße allein tut es nicht.
.pg a,
.pg .pg-aus {
display: inline-flex;
align-items: center;
justify-content: center;
min-inline-size: 2.75rem;
min-block-size: 2.75rem;
padding-inline: 0.5rem;
}
Fünf Ausnahmen gibt es (Spacing, Equivalent, Inline, User Agent Control, Essential). Für Paginierungen greift praktisch nur die erste: Sie erlaubt kleinere Ziele, wenn ein Kreis von 24 CSS-Pixeln Durchmesser, zentriert auf dem Ziel, kein anderes Ziel und keinen anderen solchen Kreis schneidet – man erkauft die kleine Fläche also mit Abstand. Die BFIT-Bund-Handreichung führt die 24 px als Soll; über WCAG 2.5.8 und die EN 301 549 sind sie im BITV-Kontext trotzdem verbindlich.
Tastatur und Fokus
Drei Muss-Anforderungen der BFIT-Bund-Handreichung, die im Alltag zählen: Die Seitennavigation muss mit der Tastatur erreicht, bedient und wieder verlassen werden können (818), sie darf beim Fokussieren keine unerwarteten Kontextwechsel auslösen (819a), und der Fokus darf beim Blättern nicht verloren gehen (819b).
Der letzte Punkt ist der, der in der Praxis kippt: Wer auf „Weiter“ klickt und die Seite lädt neu, landet am Seitenanfang – das ist in Ordnung. Wer aber per JavaScript nachlädt und den Fokus nicht mitführt, verliert ihn an <body>, und die Tastatur beginnt wieder ganz oben.
Dazu kommt der sichtbare Fokus (2.4.7) und – bei klebenden Kopf- oder Fußleisten – 2.4.11 Fokus nicht verdeckt:
.pg a:focus-visible {
outline: 3px solid #0b3d91;
outline-offset: 2px;
scroll-margin-block: 6rem; /* damit ein Sticky-Header ihn nicht verdeckt */
}
Optional, aber freundlich: Bild auf / Bild ab für vorherige und nächste Seite, Pos1 / Ende für die erste und letzte. Die BFIT-Bund-Handreichung empfiehlt das, verlangt es aber nicht – und ungewohnte Tastenbelegungen muss man dokumentieren, sonst findet sie niemand.
„Mehr laden“: das vollständige Beispiel
Der solideste Weg ist Progressive Enhancement: Serverseitig ausgelieferte Pagination als Fundament, per JavaScript zum Button aufgewertet. Ohne JavaScript bleibt die Pagination stehen – so macht es auch das BBC-Designsystem.
<ul id="treffer">
<li>…</li>
</ul>
<p id="stand" role="status"></p>
<nav aria-label="Seitennavigation" id="pagination">
<ul>
<li><a href="?seite=2">Seite 2</a></li>
</ul>
</nav>
<button type="button" id="mehr" hidden data-naechste="2">Weitere 10 laden</button>
Die Live-Region (<p id="stand" role="status">) steht leer und von Anfang an im DOM. Eine Live-Region, die erst zur Laufzeit eingefügt wird, sagt typischerweise gar nichts an.
const liste = document.getElementById('treffer');
const button = document.getElementById('mehr');
const stand = document.getElementById('stand');
const pagination = document.getElementById('pagination');
// Progressive Enhancement: erst jetzt tauschen wir Pagination gegen Button
pagination.hidden = true;
button.hidden = false;
button.addEventListener('click', async () => {
const seite = Number(button.dataset.naechste);
button.setAttribute('aria-disabled', 'true');
liste.setAttribute('aria-busy', 'true');
stand.textContent = 'Wird geladen, bitte warten';
const antwort = await fetch(`/api/treffer?seite=${seite}`);
const daten = await antwort.json();
// Trenner vor der neuen Charge – er wird gleich das Fokusziel
const trenner = document.createElement('li');
trenner.tabIndex = -1;
trenner.className = 'charge-trenner';
trenner.textContent = `Ergebnisse ${daten.von} bis ${daten.bis}`;
liste.append(trenner);
for (const eintrag of daten.eintraege) {
liste.insertAdjacentHTML('beforeend', eintrag.html);
}
liste.setAttribute('aria-busy', 'false');
button.removeAttribute('aria-disabled');
stand.textContent = `${daten.eintraege.length} weitere geladen. ${daten.bis} von ${daten.gesamt}.`;
history.replaceState(null, '', `?seite=${seite}`);
trenner.focus();
if (daten.bis >= daten.gesamt) {
button.remove();
stand.textContent = `Alle ${daten.gesamt} Ergebnisse geladen.`;
}
});
Ein Detail mit Folgen: aria-disabled="true" statt disabled. Ein natives disabled nimmt dem Button den Fokus weg – und der Fokus landet dann irgendwo. aria-disabled sagt den Zustand an und lässt den Fokus, wo er ist; die Wirkung muss man im Handler selbst unterbinden.
Statusmeldungen: role="status", aria-live und aria-busy
Drei Begriffe, die gern verwechselt werden:
-
role="status"ist eine Live-Region mit implizitemaria-live="polite"undaria-atomic="true". Für Ladezustände und Ergebnismeldungen ist das die richtige Wahl. -
aria-live="assertive"oderrole="alert"unterbricht die laufende Ausgabe. Für „10 weitere geladen“ ist das übergriffig – nimm es für Fehler, nicht für Fortschritt. -
aria-busy="true"am Container sagt: „Ich verändere mich gerade, lies mich noch nicht vor.“ Danach zwingend zurück auffalse.
Das ist die praktische Umsetzung von 4.1.3 Statusmeldungen (Stufe AA). Mehr dazu unter Live-Regionen.
Und der Mythos gleich mit: Screenreader lesen nicht automatisch vor, was ins DOM kommt. Ohne Live-Region oder Fokusverlagerung passiert schlicht nichts.
Wohin gehört der Fokus?
Drei Strategien sind im Umlauf:
| Strategie | Stärke | Schwäche |
|---|---|---|
(a) Erster neuer Eintrag mit tabindex="-1" |
einfach, führt direkt weiter | kein Kontext: „Woher weiß ich, dass hier Neues beginnt?“ |
| (b) Trenner vor der Charge, der „Ergebnisse 11 bis 20“ ansagt | Kontext kommt mit, robust | ein zusätzliches Element im Markup |
| (c) Fokus bleibt am Button, nur Ansage | kein Fokussprung | man muss rückwärts tabben, um zum Neuen zu kommen |
Ich halte (b) für die beste Wahl – das ist auch das Muster des BBC-Designsystems. Der Trenner bekommt tabindex="-1" (nicht 0: Er soll nicht dauerhaft in der Tab-Reihenfolge liegen) und ein eigenes Fokus-Styling, das ihn nicht wie ein Bedienelement aussehen lässt – aber ganz ohne sichtbare Rückmeldung darf er auch nicht bleiben.
URL, History und der Zurück-Button
Das am häufigsten übersehene Problem: Wer dreimal „Mehr laden“ drückt, einen Treffer öffnet und zurückgeht, steht wieder bei den ersten zehn. Ein Baymard-Benchmark der 50 größten US-Shops stellte schon 2016 fest, dass über 90 % der Sites mit „Mehr laden“-Button genau das falsch machten. Die Zahl ist alt – das Problem begegnet mir bis heute in fast jedem zweiten Shop.
-
history.replaceState()beim Nachladen weiterer Chargen – sie sollen keine eigenen History-Einträge erzeugen. -
history.pushState()nur bei echter Seitennavigation. - Immer echte Query-Parameter (
?seite=3), niemals Fragmente (#seite=3): Google ignoriert Fragment-Identifier und folgt Links, die sich nur nach dem#unterscheiden, unter Umständen gar nicht. - Beim Zurückkommen die Position wiederherstellen – oder wenigstens die bereits geladene Anzahl.
Infinite Scroll: die Probleme im Detail
Automatisches Nachladen beim Scrollen ist kein WCAG-Verstoß an sich – es gibt kein Erfolgskriterium „kein Infinite Scroll“. Die Verstöße entstehen an ganz bestimmten Stellen, und sie treffen unterschiedliche Gruppen unterschiedlich hart. Deque hat neun betroffene Gruppen benannt; besonders eindeutig ist der Befund zur Spracheingabe: Nutzende von Dragon oder anderer Spracherkennungssoftware seien „completely left out of the infinite scroll experience“, weil es schlicht keinen Befehl gibt, mit dem sich das Nachladen auslösen ließe.
| Betroffen | Was konkret passiert |
|---|---|
| Spracheingabe | Kein auslösbarer Befehl – Nachladen ist nicht bedienbar |
| Tastatur | Der Tab-Pfad wächst unkontrolliert, der Fußbereich rückt weg |
| Screenreader | Neue Inhalte werden ohne Live-Region gar nicht angesagt |
| Switch-Bedienung | Jeder zusätzliche Eintrag verlängert die Scanzeit |
| Bildschirmvergrößerung | Orientierung geht verloren, weil der Kontext springt |
| Kognition | „Illusion of Completeness“ – man weiß nie, ob man fertig ist |
| Alle | Strg+F findet nur, was bereits geladen ist |
Dazu die harten Nebenwirkungen: Der Fußbereich wird unerreichbar, weil ständig Neues nachrückt, und die Position geht verloren, sobald man einen Eintrag öffnet und zurückkommt.
Sauber zugeordnet verletzt schlecht gebauter Infinite Scroll typischerweise 4.1.3 (keine Ansage), 2.4.3 (Fokus geht verloren) und 2.1.1 (nicht per Tastatur auslösbar) – bei automatischem Nachladen ohne Anforderung zusätzlich 3.2.5 (Stufe AAA).
role="feed": das ARIA-Muster, ehrlich eingeordnet
Für endlose Listen gibt es ein eigenes ARIA-Muster. Es ist gut gemeint, und man sollte es kennen – aber ich empfehle es nur mit Vorbehalt.
<div role="feed" aria-labelledby="feed-titel" aria-busy="false">
<h2 id="feed-titel">Neueste Beiträge</h2>
<article tabindex="0" aria-labelledby="p1-titel" aria-describedby="p1-text"
aria-posinset="1" aria-setsize="-1">
<h3 id="p1-titel">Barrierefreie Formulare</h3>
<p id="p1-text">…</p>
</article>
</div>
-
aria-setsize="-1"bedeutet „Gesamtzahl unbekannt“ – bei endlosen Listen der Normalfall. -
aria-busyvor dem Einfügen auftrue, danach auffalse. - Tastatur: Bild ab = nächster Artikel, Bild auf = vorheriger, Strg+Ende = erstes fokussierbares Element nach dem Feed, Strg+Pos1 = erstes davor.
Und jetzt die Einschränkungen, die man dazusagen muss: Diese Tastenbelegung hat kein Vorbild aus Desktop-Oberflächen – niemand erwartet sie, also muss man sie auf der Seite dokumentieren. Sie kollidiert mit Widgets innerhalb der Artikel, die dieselben Tasten belegen. Und belastbare, aktuelle Aussagen zur Screenreader-Unterstützung sind rar: Die meistzitierte Supportmatrix von Deque stammt aus dem Jahr 2019 und listet noch Internet Explorer. Wer heute role="feed" einsetzt, sollte selbst testen.
Zwei Regeln aus der Praxis kommen dazu: keine Artikel mitten im Feed einfügen oder entfernen, und neue Artikel laden, bevor das Ende erreicht ist.
Den Fußbereich erreichbar halten
Wenn Infinite Scroll gesetzt ist, muss der Fußbereich trotzdem erreichbar bleiben. Drei Bausteine helfen:
<a class="skip" href="#fuss">Zum Fußbereich</a>
<button type="button" id="autoload" aria-pressed="true" class="autoload-schalter">
Automatisches Nachladen: an
</button>
<div role="feed" aria-labelledby="feed-titel">…</div>
<footer id="fuss" tabindex="-1">…</footer>
Der Schalter ist ein Vorschlag von Deque: Er steht unmittelbar vor der Feed-Region, ist normalerweise visuell versteckt und wird bei Tastaturfokus sichtbar. Im Aus-Zustand erscheinen stattdessen „Mehr laden“- oder Pagination-Links. Mehr zum Sprunglink-Muster unter Skip-Links.
SEO: was Google dokumentiert
Der SEO-Teil ist bemerkenswert eindeutig – Google hat dazu eine eigene Dokumentationsseite, und fast alles darin widerspricht dem, was man in Foren liest.
- Jede Seite braucht eine eigene URL (
?seite=2). Ohne eigene URL gibt es nichts zu indexieren. - Selbstreferenzierendes Canonical je Seite – Seite 2 kanonisiert auf Seite 2, nicht auf Seite 1. Der verbreitete Rat, alles auf Seite 1 zu kanonisieren, ist falsch; mehr dazu unter Canonical & Duplicate Content.
-
Kein
noindexauf paginierten Seiten. Sonst verwaisen die Detailseiten, die nur von dort verlinkt sind.noindexist etwas für Filter- und Sortiervarianten. -
rel="next"/rel="prev"wertet Google nicht mehr aus. Sie sind weiterhin gültige HTML-Linktypen und andere Suchmaschinen können sie nutzen – als Google-SEO-Maßnahme bringen sie nichts. -
Keine
#-Fragmente als Seitenzähler. -
Crawler klicken keine Buttons. Googlebot rendert zwar JavaScript, löst aber keine Funktionen aus, die eine Nutzeraktion erfordern. Hinter „Mehr laden“ und Infinite Scroll gehören deshalb zusätzlich crawlbare
<a href>-Links auf die paginierten URLs – im einfachsten Fall die Pagination selbst, die per JavaScript nur versteckt statt entfernt wird. - Titles und Descriptions dürfen auf paginierten Seiten ausnahmsweise identisch sein; die Empfehlung eindeutiger Titel gilt hier nicht.
Welche Kriterien hier greifen
| Kriterium | Stufe | Bezug zur Pagination |
|---|---|---|
| 1.4.11 Nicht-Text-Kontrast | AA | Markierung der aktuellen Seite, Fokusindikator |
| 2.1.1 Tastatur | A | Blättern und Nachladen ohne Maus |
| 2.4.3 Fokus-Reihenfolge | A | Fokus nach dem Nachladen |
| 2.4.6 Überschriften und Beschriftungen | AA | Name der <nav>, Namen der Links |
| 2.4.7 Fokus sichtbar | AA | sichtbarer Fokusindikator |
| 2.4.11 Fokus nicht verdeckt | AA | Sticky-Leisten über der Pagination |
| 2.5.8 Zielgröße | AA | 24 × 24 CSS-Pixel je Seitenzahl |
| 4.1.3 Statusmeldungen | AA | Ansage nach „Mehr laden“ |
Die BFIT-Bund-Handreichung ordnet dieselben Punkte den EN-301-549-Klauseln zu – 9.1.4.11 (Kontrast), 9.2.1.1/9.2.1.2 (Tastatur), 9.2.4.3 (Fokusreihenfolge), 9.2.4.6 (Namen) und 9.4.1.2 (Rollen und Zustände).
Testen
- Nur mit Tab durch die Pagination – auch auf der ersten und letzten Seite.
- Sprachsteuerung: „Klick Seite 3“ – prüft 2.5.3 in einem Schritt.
- Screenreader (NVDA mit Firefox oder Chrome, VoiceOver auf macOS und iOS): Wird die aktuelle Seite angesagt? Kommt nach „Mehr laden“ eine Meldung?
- Zoom auf 400 % (1.4.10 Reflow): Bleibt die Pagination bedienbar oder wird sie zur Wurstkette?
- Zurück-Button nach dem Nachladen.
- JavaScript aus: Steht die Pagination noch?
Häufige Fehler
- Klick-
<div>s statt echter Links – nicht fokussierbar, nicht teilbar. - Aktuelle Seite nur farblich markiert, ohne zweites Merkmal und ohne
aria-current="page". -
aria-label, das den sichtbaren Text nicht enthält – sabotiert die Sprachsteuerung. -
Deaktivierte Blätter-Links mit
href, die trotz Ausgrauen funktionieren. - „…“ ohne Erläuterung – wird nicht vorgelesen.
- „Mehr laden“ ohne Ansage oder ohne Fokusverlagerung.
- Live-Region erst zur Laufzeit eingefügt – sie sagt dann nichts.
-
disabledstattaria-disabledam Ladebutton: Der Fokus geht verloren. -
#-Fragmente als Seitenzähler oder Canonical aller Seiten auf Seite 1. - Infinite Scroll ohne Abschaltmöglichkeit und ohne erreichbaren Fußbereich.
Häufige Fragen
Braucht die aktuelle Seite wirklich noch ein href?
Ja, das ist die robustere Variante. Screenreader-Nutzende springen häufig über die Linkliste; ein <span> an dieser Stelle fällt dort heraus. Der Link zeigt auf die eigene URL und trägt aria-current="page".
Soll ich rel="next" und rel="prev" noch setzen?
Es schadet nicht, bringt bei Google aber nachweislich nichts. Wenn dein CMS sie ohnehin ausgibt, lass sie stehen; extra einbauen musst du sie nicht.
Ist role="feed" produktionsreif?
Ich würde es heute nur mit eigenem Test einsetzen. Die Tastaturkonventionen sind ungewohnt und dokumentationspflichtig, sie kollidieren mit Bedienelementen in den Artikeln, und aktuelle Aussagen zum Screenreader-Support fehlen. Für die meisten Listen ist „Mehr laden“ die verlässlichere Wahl.
Wie viele Seitenzahlen zeige ich?
Sieben Slots sind ein guter Richtwert – und vor allem: immer gleich viele, damit die Klickziele beim Blättern nicht wandern.
Darf „Mehr laden“ automatisch beim Scrollen auslösen?
Dann ist es kein Button mehr, sondern Infinite Scroll – mit allen Folgen. Wer beides kombinieren will, sollte das automatische Nachladen abschaltbar machen.
Pagination oder „Mehr laden“?
Pagination, wenn man gezielt springen, vergleichen und URLs teilen können soll – das ist auch fürs SEO der ruhigere Weg. „Mehr laden“ für fortlaufendes Stöbern. Beide sind zugänglich zu bauen.
Fazit
Pagination baust du als <nav> mit echten Links, aria-current="page" und einer Markierung, die mehr kann als Farbe. „Mehr laden“ ist nur so gut wie sein Nachspiel: role="status"-Ansage, Fokus auf einen Trenner vor der neuen Charge, replaceState für die URL – und crawlbare Links dahinter, damit Suchmaschinen überhaupt weiterkommen. Infinite Scroll bleibt der heikelste Weg; wenn er sein muss, dann mit Abschalter, erreichbarem Fußbereich und der ehrlichen Erkenntnis, dass ein Teil der Nutzenden ihn gar nicht bedienen kann. Im Zweifel: der bewusst ausgelöste Button.
Quellen
- Pagination, incremental page loading, and their impact on Google Search (Google Search Central – URLs, Canonical,
rel=next/prev, Crawlbarkeit) - Paginierung (Überwachungsstelle des Bundes, BFIT-Bund, Version 0.5 vom 24.03.2025 – Anforderungen 817–823 mit EN-301-549-Zuordnung)