Der Weg dorthin war holprig: ein wenig Herumprobieren, einen Hinweis aus der einen Doku aufnehmen, ein Detail aus dem Suchergebnis zum Thema mitnehmen. Am Ende lief alles auf ein altes Prinzip hinaus – Progressive Enhancement: erst eine Basis, die ohne CSS-Style und JavaScript funktioniert, dann optionale Verbesserungen obendrauf.
Wie meine Vorschau klickbar wurde – mit zu viel JavaScript
In der Beitragsübersicht zeigt mein Theme jeden Beitrag als Vorschau-Kachel: Beitragsbild, Meta-Infos, Titel, ein Textauszug, ein Weiterlesen-Link. Im Code heisst so eine Kachel .post – im Webdesign nennt man sie meist «Card». Ich wollte, dass sich nicht nur der Weiterlesen-Link anklicken oder antippen lässt, sondern die ganze Card. Das holt alle ab, die intuitiv das Bild als Ziel wählen statt des Links – oder ihn mit Maus oder Finger nicht ganz genau treffen. Für die Tastatur bleibt es bei einem einzigen, klar fokussierten Tab-Stopp auf dem Link – unabhängig von der Klickfläche.
Hinweis: Die Code-Beispiele in diesem Beitrag sind zur Erklärung vereinfacht – sie sind nicht direkt einsatzbereit oder vollständig.
Vorweg ein Detail, das unabhängig von der Klickfläche zählt: Der Weiterlesen-Link sollte nicht einfach den Text «Weiterlesen» enthalten. Denn damit stehen auf einer Übersichtsseite schnell zehn identische «Weiterlesen»-Links – für einen Screenreader sagen sie alle dasselbe, ohne Ziel. Der Link braucht den Bezug zum Beitrag, am besten den Titel. Damit das die sichtbare Gestaltung nicht stört, kommt der Titel-Teil in ein visually-hidden-Element: für das Auge unsichtbar, für Screenreader und andere Tools aber vorhanden (in Bootstrap ist dies als Utility-Klasse mit dabei).
<a class="btn btn-primary read-more" href="/beitrag">Weiterlesen<span class="visually-hidden">: Beitragstitel</span></a>
Bleibt die eigentliche Frage: Wie wird daraus eine Card, die sich komplett anklicken lässt?
Meine erste Lösung zum Problem war ein kleines JavaScript (read-more.js). Es machte den ganzen Container der Card per Tastatur fokussierbar und fing Klicks oder das Tippen darauf ab, um sie an den Weiterlesen-Link weiterzuleiten – indem es sich das Link-Ziel des Weiterlesen-Links holte und es auf der ganzen Fläche der Card anwandte.
const href = link.getAttribute( 'href' );
container.addEventListener( 'click', function() {
window.location.href = href;
} );
Damit dabei keine doppelten Tab-Stopps entstehen (durch Links, die sich in der Card befanden, z.B. auf die Kategorie), schob das Skript die Fokus-Reihenfolge hin und her – ein Muster, das sich «roving tabindex» nennt: Der Container bekommt einen Tab-Stopp, die Links darin verlieren ihren, bis der Container den Fokus erhält.
// Container fokussierbar machen ...
container.setAttribute( 'tabindex', '0' );
// ... und die Links darin aus der Tab-Reihenfolge nehmen,
// bis der Container fokussiert wird.
container.querySelectorAll( 'a' ).forEach( function( link ) {
link.setAttribute( 'tabindex', '-1' );
} );
Funktioniert hat das, aber eigentlich war es schlechter als vorher, auch wenn es sich mit Tastatur, Finger und Maus bedienen liess. Denn ein <article> mit tabindex="0", aber ohne Rolle und ohne Namen, ist für einen Screenreader ein fokussierbares Nichts: Der Cursor landet darauf und es gibt nichts vorzulesen. Gleichzeitig hatte ich die echten Links – die, welche ein Screenreader sauber ansagen würde – per tabindex="-1" aus der Reihenfolge genommen und per focusin/focusout wieder zurückgeholt. Das Ergebnis: eine wacklige Tab-Reihenfolge, die je nach Fokus umsprang.
Und das alles widersprach einer Regel, die ich mir selbst gesetzt hatte: Was der Browser von sich aus kann, sollte im Markup und CSS stehen und nicht in einem JavaScript, das es zur Laufzeit nachbaut.
Der Denkfehler: Verhalten nachbauen statt Vorhandenes nutzen
Der Fehler steckte schon in der Frage, die ich mir gestellt hatte: Wie mache ich diesen Container tipp-, klick- und fokussierbar? Dabei gab es ein vorhandenes Element, das beides von Natur aus kann – den Weiterlesen-Link. Ein echter <a href> ist fokussierbar, mit Enter bedienbar und wird vom Screenreader als Link mit Ziel angesagt – deswegen ist der Beitragstitel zusätzlich im Text, wie zuvor erklärt.
Ich hatte mit JavaScript genau das gemacht, was ich vermeiden wollte.
Es wäre besser gewesen, etwas anderes zu fragen: Wie kann ich die Klickfläche des Links vergrössern, ohne dass ich Browser-Verhalten nachbauen muss?
Eine Möglichkeit: den vorhandenen Weiterlesen-Link per CSS vergrössern.
Und damit fällt der ganze JavaScript-Apparat weg: kein tabindex, keine focusin-Logik, keine Klick-Weiterleitung. Tastatur und Screenreader sehen weiterhin genau einen normalen Link. Nur für die Bedienung mit Maus oder Finger bekommt man mehr Fläche.
Merke: Manchmal macht es mehr Sinn, eine Klickfläche mit CSS zu vergrössern, statt zu versuchen, das Verhalten nachzubauen.
Das Bittere daran: Die Basis war von Anfang an richtig – ein echter, semantischer Link, der schon alles konnte, was für die Barrierefreiheit und die normale Nutzung notwendig war. Ich hatte ihn mit JavaScript entkräftet, statt ihn einfach im HTML zu lassen, wie er war.
Die schlanke Lösung: ein CSS-Pseudo-Element statt eines Skripts
Übrig bleiben zwei kurze CSS-Regeln und, in meinem Fall, ein wenig PHP.
Die Klickfläche dehnen
Ein Element kann mit einem Pseudo-Element eine unsichtbare Fläche aufspannen. Dafür bekommt die Card position: relative und wird so zum Bezugsrahmen, und der Weiterlesen-Link erhält ein :after ohne Inhalt, das per inset: 0 über die ganze Card gezogen wird.
&:has( .read-more ) {
position: relative;
cursor: pointer; // bringt der Link-Overlay selbst mit – hier nur zur Veranschaulichung
}
.read-more {
&:after {
content: '';
position: absolute;
inset: 0;
}
}
inset ist die Kurzschreibweise für top, right, bottom und left in einem – inset: 0 heisst also: an allen vier Seiten bündig mit der Card. Das :has() davor wählt nur Cards, die überhaupt einen Weiterlesen-Link enthalten.
Wichtig ist, wem das Pseudo-Element gehört: dem Weiterlesen-Link, nicht der Card. Dadurch umfasst die aktive Fläche dieses einen Links die ganze Card – ein Klick oder Tipp irgendwo auf der Card löst den Link aus. Für die Tastatur ändert sich nichts: Der Link bleibt das fokussierte Element, Enter löst ihn aus – unabhängig vom Overlay. Sichtbar wird der Fokus aber nur am Button, der seinen eigenen Fokus-Ring mitbringt (im Theme ein Bootstrap-Button). Eine zusätzliche Outline um die Card braucht es darum in meinem Fall nicht.
Konkurrenz entfernen
Eine ganzflächig bedienbare Card mit einem zweiten Link darin erzeugt immer Reibung: Man tabbt daran vorbei, oder ein Tipp bzw. Klick auf den zweiten Link wird vom Overlay verschluckt. Daher muss eine Regel gesetzt werden: so wenig interaktive Elemente wie möglich in einer klickbaren Card. Am besten nur der Weiterlesen-Link.
Titel und Beitragsbild verlinke ich auf einer Übersicht ohnehin nicht – blieb nur die Kategorie. Sie ist dort jetzt nicht mehr verlinkt, sondern nur noch ein <span>:
foreach ( $categories as $category ) {
?><span class="category"><?php echo esc_html( $category->name ); ?></span><?php
}
Verlinkt bleibt die Kategorie im Einzelbeitrag, wo es kein Overlay gibt. So hat jede Card genau ein Ziel – klare Tab-Reihenfolge, kein verschluckter Klick.
Der Preis: Das Overlay liegt über dem Text, also lässt sich der Textauszug in der Card nicht mehr markieren. Bei einer klickbaren Card ist das gewollt – und beim vorherigen JavaScript-Workaround war es genauso.
Der umgekehrte Fehler: zu wenig statt zu viel
Mein Fehler war zu viel künstliche Semantik. Der häufigere Fehler ist das Gegenteil: zu wenig. Man sieht ihn oft bei genau solchen Vorschau-Karten – ein einziger Link, der Bild, Titel und ganzen Auszug umschliesst:
<a href="/beitrag">
<img src="..." alt="">
<h2>Beitragstitel</h2>
<p>Ein langer Auszug, der den Beitrag anteasert ...</p>
</a>
Technisch klickbar und sogar nicht falsch (siehe a-Tag) – aber der Screenreader macht aus dem gesamten Inhalt den Namen des Links und liest beim Durchtabben den ganzen Block am Stück vor: Titel, Auszug, alles. Statt eines knappen «Beitragstitel, Link» bekommt man einen Absatz. Hat das Bild dann noch ein leeres oder fehlendes alt, wird es noch unklarer.
Die Korrektur ist klein: den Link auf das beschränken, was das Ziel wirklich benennt, und der klickbaren Fläche über aria-label oder, wie zuvor gezeigt, einen kurzen, klaren Namen (Weiterlesen: + Beitragstitel) geben.
<a href="/beitrag" aria-label="Beitragstitel lesen">…</a>
Optimal ist das Markup auch mit dieser Korrektur nicht: Ein eigener, sichtbarer Weiterlesen-Button fehlt hier ganz – und er lässt sich auch nicht nachrüsten, denn ein zweites interaktives Element wie ein Link oder Button darf nicht innerhalb des umschliessenden <a> stehen.
Beide Fehler – mein zu viel und dieses zu wenig – haben dieselbe Wurzel: Es wird am nativen Verhalten des Browsers vorbeientwickelt, statt es zu nutzen.
Merke: Mit dem Browser arbeiten statt gegen ihn. Und nur weil etwas technisch nicht falsch ist, ist es noch nicht die richtige Lösung – gerade dort, wo hinter dem ersten optischen Eindruck mehr steckt.
Warum Barrierefreiheit gerade jetzt relevanter wird
Bisher wurde Barrierefreiheit oft als Kür behandelt. Das ändert sich. In der Schweiz soll die Teilrevision des Behindertengleichstellungsgesetzes (BehiG) die Pflicht zu barrierefreien digitalen Angeboten frühestens ab 2027 auf private Anbieter ausweiten; Massstab sind die WCAG auf Stufe AA. Wer den EU-Markt bedient, fällt ohnehin schon unter den European Accessibility Act, der seit Juni 2025 gilt.
Den rechtlichen Rahmen mit Quellen habe ich im Abschnitt Barrierefreiheit meines Beitrags zum Like-Button ausführlicher beschrieben. Wichtig ist der folgende Punkt: Was lange optional war, wird zur Grundanforderung. Und genau dann hilft es, wenn die zugängliche Lösung zugleich die einfachere ist.
Mehr dazu gibt’s auch hier:
- Teilrevision des BehiG
- Barrierefreie Webseiten und Apps – neue Pflichten nach dem BehiG-Entwurf (Härting)
- European Accessibility Act
- The A11Y Project
Die Erkenntnis und was bleibt
Es wurde weniger Code als am Anfang verwendet – letztlich nur HTML und CSS als Progressive Enhancement. Für die Barrierefreiheit war gar kein weiteres Enhancement mit JavaScript notwendig. Das ganze read-more.js wurde gelöscht, der Import ist weggefallen, an dessen Stelle traten zwei CSS-Regeln und ein <span> statt eines überflüssigen Links. Weniger Zeilen, weniger Zustände, die schiefgehen können – und zugänglicher als die aufwändige Variante davor und um Längen besser als der «zu wenig Markup»-Fall.
Das ist der Teil, der mich überrascht hat: Barrierefreiheit kostet nicht immer extra. Manchmal ist die zugängliche Lösung schlicht die schlankere, weil sie nutzt, was der Browser schon kann, statt es nachzubauen. Schwer war die eigentliche Lösung nicht, sondern die richtige Frage zu stellen und zu beantworten.
Und diese Erkenntnis lässt sich mitnehmen. Bevor man das nächste Mal ein Element per JavaScript klick- und fokussierbar macht, lohnt eine Frage: Gibt es nicht ein echtes, semantisches Element – einen Link, einen Button –, den man vielleicht nur anders stylen muss? Meistens gibt es das. Und dann ist der barrierefreie Weg auch der kürzere.

Kommentare