Eine zugängliche, mehrstufige Navigation, die auf Desktop und Smartphone funktioniert, braucht 2026 weder ein Framework noch eine Zeile JavaScript. Was sie braucht, sind sauberes HTML, ein paar Pseudoklassen und ein Verständnis dafür, warum :hover allein nicht reicht.
Dropdown-Menüs gehören zu den ältesten Frontend-Bauteilen überhaupt und gleichzeitig zu denen, die am häufigsten falsch umgesetzt werden. Wer in den letzten Jahren gelegentlich in den Quelltext großer Websites geschaut hat, findet dort bis heute Konstruktionen, die mit float, display: none und einem Hauch Hoffnung arbeiten. Auf dem Desktop sieht das passabel aus, sobald jemand mit der Tastatur navigiert oder ein Smartphone in die Hand nimmt, fällt das Konstrukt auseinander.
Dabei gibt es längst keinen Grund mehr, JavaScript für ein Aufklappmenü zu bemühen. Flexbox, :focus-within, :has() und das details-Element decken alles ab, was eine moderne Navigation braucht, inklusive Tastaturbedienung und einem sauberen Verhalten auf Touchscreens. Dieser Beitrag zeigt eine Lösung, die sich in jedes Projekt übernehmen lässt, und erklärt, warum sie so aussieht, wie sie aussieht.
Was eine moderne Navigation leisten muss

Bevor wir Code schreiben, lohnt ein Blick auf die Anforderungen. Eine Hauptnavigation ist heute mehr als eine Liste mit Hover-Effekten. Sie muss:
- mit Maus, Tastatur, Touchscreen und Screenreader gleich gut funktionieren,
- auf schmalen Viewports einen anderen Aufbau haben als auf breiten,
- mehrere Ebenen abbilden können, ohne in CSS-Spaghetti zu enden,
- ohne Layout-Sprünge laden, also kein Aufflackern beim ersten Render,
- und, das wird gerne vergessen, auch ohne Pointer funktionieren, wenn jemand per Tab durch die Seite springt.
Die letzten beiden Punkte sind der Grund, warum die meisten älteren Tutorials heute nicht mehr taugen. Eine Navigation, die ausschließlich auf :hover reagiert, ist für Tastaturnutzer schlicht nicht bedienbar. Und eine, die display: none ohne Übergang verwendet, mag funktionieren, wirkt aber abrupt.
Das HTML: semantisch, knapp, vollständig

Die Basis bleibt eine verschachtelte Liste in einem nav-Element. Daran hat sich nichts geändert, und es gibt auch keinen Grund, daran etwas zu ändern. Was hinzukommt, sind ein paar ARIA-Attribute, ein verstecktes Checkbox-Element für die mobile Steuerung und, wichtig, button-Elemente statt verlinkter Eltern-Items.
Zwei Entscheidungen verdienen einen kurzen Kommentar.
Erstens: Eltern-Items, die ein Submenü öffnen, sind hier button-Elemente, keine a-Tags mit href="#". Das ist semantisch korrekt, der Klick verändert keinen Ort, sondern öffnet ein Untermenü, und löst das uralte Touch-Problem, dass ein Tap auf den Eltern-Link die Seite wechselt, bevor der Nutzer das Submenü überhaupt sehen konnte. Wer den Eltern-Eintrag zusätzlich verlinkbar haben möchte, packt Button und Link nebeneinander; das ist sauberer als jeder Trick mit pointer-events.
Zweitens: Die Checkbox #nav-toggle ist visuell versteckt und dient ausschließlich als Schalter für die mobile Ansicht. Das ist der klassische CSS-only-Trick. Wer ihn unelegant findet, kann auf das details-Element ausweichen, dazu unten mehr. Funktional ist die Checkbox in jedem Browser ohne Sonderbehandlung verfügbar.
Das CSS: Flexbox auf Desktop, Stack auf Mobil

Wir gehen Mobile-First vor. Der Standard-Layoutmodus ist also der gestapelte, schmale Viewport; die Desktop-Variante ist die Erweiterung.
Bis hierhin haben wir eine vollständig funktionsfähige mobile Navigation. Der Burger öffnet die Liste, Untermenüs klappen über :focus-within und, wo :has() verfügbar ist, über das aria-expanded-Attribut auf. Drei Pseudoklassen machen die Arbeit, die früher hundert Zeilen JavaScript brauchten.
Für Desktops kommt jetzt der Bruch:
Der Bruch bei 48rem (768 Pixel bei Standard-Schriftgröße) ist Konvention, kein Naturgesetz. Wer Zugang zu echten Nutzungsdaten hat, sollte den Wert daran ausrichten. Für den Zweck dieses Beispiels reicht er.
Das !important beim Listen-Display auf Desktop ist ein bewusstes Zugeständnis an die Spezifität: Auf Mobil schaltet die Checkbox die Liste über display: flex, auf Desktop soll sie unabhängig vom Checkbox-Zustand immer sichtbar sein. Wer das vermeiden möchte, baut eine zusätzliche Klassenhierarchie auf, mein Vorschlag: einmal !important, einmal Kommentar, fertig.
:focus-within und :has() — warum beides?

In den Submenü-Selektoren tauchen drei Bedingungen auf: :hover, :focus-within und :has(> button[aria-expanded="true"]). Das ist kein Versehen.
:hover deckt die Maus ab. :focus-within deckt die Tastatur ab, sobald ein Element innerhalb des li.has-submenu Fokus erhält, gilt der Selektor. Damit lässt sich das Menü mit Tab und Pfeiltasten bedienen, ohne dass JavaScript Fokus-Management übernehmen muss.
Bleibt :has(). Diese relativ neue Pseudoklasse erlaubt es, einen Eltern-Selektor in Abhängigkeit von Kindelementen zu schreiben. Hier prüfen wir, ob der Submenü-Button gerade aria-expanded="true" trägt. Das ist relevant für Touch-Geräte, auf denen weder :hover noch :focus-within zuverlässig auslösen. Die einzige verbleibende Stellschraube wäre dann ein klassischer Klick, und genau den können wir per :has() und einem winzigen Stück JavaScript kombinieren, falls man doch noch ein paar Zeilen schreiben möchte:
Wer ohne JavaScript auskommen will, lässt den :has()-Selektor weg und verlässt sich auf :focus-within, das auch bei Touch-Tap auf den Button greift, solange der Button den Fokus behält. In den meisten Browsern funktioniert das, wenn auch nicht durchgängig sauber. Für produktive Seiten sind die acht Zeilen JavaScript der pragmatische Weg.
Die Alternative: details und summary

Wer das Mobil-Verhalten ohne Checkbox-Hack lösen möchte, kann auf das HTML-Element details zurückgreifen. Browser bringen den Aufklapp-Mechanismus mit, inklusive Tastaturbedienung und Screenreader-Unterstützung. Der Aufbau sieht dann so aus:
Auf Desktop entfernt man die details-Optik mit ein paar CSS-Regeln (summary { display: none }, details > ul { display: flex }) und behandelt das Element wie einen normalen Container. Charmant ist, dass open als Attribut zur Verfügung steht, damit lassen sich CSS-Übergänge sauber schreiben, ohne Zustandsverwaltung im JavaScript:
Für Submenüs innerhalb der Liste lässt sich das gleiche Muster verschachteln. Der einzige Wermutstropfen: Das Default-Dreieck der summary muss browserübergreifend zurückgesetzt werden, was etwas Fingerspitzengefühl verlangt. Dafür spart man sich den Checkbox-Trick.
Worauf bei der Übernahme zu achten ist

Drei Punkte fallen in der Praxis am häufigsten auf die Füße.
Erstens die Stapelreihenfolge. Submenüs liegen absolut positioniert über dem Folge-Inhalt. Wenn die Seite weiter unten Elemente mit eigenem z-index enthält, schiebt sich gerne ein Hero-Bild über das Menü. Ein z-index: 100 auf dem nav-Element räumt das ab.
Zweitens die Kollision mit overflow: hidden. Ein Container, der die Navigation umschließt und gleichzeitig overflow: hidden trägt, schneidet ausgeklappte Submenüs ab. Der Hinweis stammt von einem Kommentator unter der Vorgängerversion dieses Artikels und stimmt bis heute. Lösung: Die Navigation gehört semantisch und strukturell außerhalb solcher Container.
Drittens die Klick-Falle auf Touch. Ein Eltern-Item, das gleichzeitig Link und Submenü-Trigger ist, funktioniert auf Smartphones nicht zuverlässig. Entweder verlinken oder aufklappen, beides geht nur mit zusätzlichem Markup, etwa einem separaten Pfeil-Button neben dem Link.
Performance, Barrierefreiheit, Wartbarkeit

Die hier gezeigte Lösung kommt mit etwa 120 Zeilen CSS und, optional, acht Zeilen JavaScript aus. Kein Framework, keine externe Datei, keine Render-Verzögerung. Die kritische Renderpfad-Last bleibt minimal, was bei Lighthouse-Audits direkt in den Largest-Contentful-Paint einfließt.
In Sachen Barrierefreiheit erfüllt das Markup die WCAG-2.2-Anforderungen für Navigationen: tastaturbedienbar (Tab, Shift+Tab, Enter), korrekt beschriftet (aria-label, aria-expanded, aria-haspopup), und mit sichtbarem Fokus-Indikator dank :focus-visible. Wer es noch konsequenter mag, ergänzt eine Skip-Link am Seitenanfang und Pfeiltasten-Navigation per JavaScript, beides ist optional, beides ist kein Hexenwerk.
Wartbarkeit kommt über die strikte BEM-ähnliche Klassenstruktur und CSS-Custom-Properties. Wer die Farben anpassen will, ändert vier Werte am :root. Wer den Breakpoint verschiebt, ändert eine Media Query. Es gibt keine versteckten Abhängigkeiten, keine !important-Kaskaden außer der einen erklärten, keine generierten Klassennamen.
Fazit

Eine reine CSS-Navigation ist 2026 keine Spielerei mehr, sondern der Standardweg. Flexbox sorgt für das Layout, :focus-within und :has() für die Interaktion, ARIA-Attribute für die Barrierefreiheit. Wer noch JavaScript-Plug-ins für Dropdown-Menüs einbindet, schleppt Ballast mit, der schon vor Jahren überflüssig wurde.
Der hier gezeigte Code lässt sich direkt in jedes Projekt kopieren. Anpassungen an Farben, Schriften und Breakpoints sind über Custom Properties in Sekunden erledigt. Die einzige strategische Entscheidung, die offenbleibt, ist die zwischen Checkbox-Hack und details-Element, und die ist eine Geschmacksfrage, keine technische.
8 Kommentare
Netter Beitrag, allerdings wäre – grade in Hinblick auf responsive Best practices – ein „Mobile first“-Ansatz angebracht.
Man könnte ein paar Zeilen Code sparen, wenn statt dem „
display:none“ für das ul der zweiten Menüebene und dem ganzen Code aus dem Bereich „Zweite und dritte Menüebene gestalten“ einfach nur folgendes verwendet wird:Das sollte genauso funktionieren. Wie es sich dann mit der Performance verhält, hab‘ ich nicht getestet, allerdings sollte das nicht sonderlich ins Gewicht fallen 😉
Super Menu !!! Aber sobald sich das Menü in einem Container befindet der nicht der komplette Höhe des Menüs (also im ausgeklappten Zustand) entspricht, dann klappen sich die Untermenu Punkte wieder ein beim Drüberfahren. Ebenso wenn unter dem Menü sich direkt ein JS Anwendung befindet.
VG Mike
Des weiteren ist es schwierig das komplette Menü rechts zu positionieren.
VG Mike
Bei mir funktioniert das nicht, fehlt was? Es gibt mir kein Button nur die ganze Codierung ist sichtbar.
Eine „hübsche“ Version von Menü mit einem modernen Look & Feel wäre noch chicker! ))
Aber trozdem Danke für den Artikel!
Seit den späten 90ern frage ich mich nun schon, warum überall immer diese doppelte Verschachtelung in Ungeordnete Listen und deren Listenelemente und konnte mich bisher auch nicht dazu durchringen. Gut, vor Erscheinen des „nav“ Tags hätte man es ja noch mit Zugänglichkeit für Screen-Reader und mit SEO Relevanz begründen können, aber jetzt mit HTML5? Mit dem nav Tag als umfassenden Container der „a“ Tags (Links), sollte der Zugänglichkeit und der Semantic doch eigentlich Genüge getan sein. Und auch der dümmste Suchmaschinenrobot wird wohl längst den Sinn des nav Tags, nämlich einen Bereich semantisch als Linkliste auszuweisen, verinnerlicht haben und nicht dran vorbeischliddern. Der container und darin alle Link Tags des Menüs in simpler Form von *** und gut ist, odda?? Waren die Listen und deren Listenelemente nicht auch ursprünglich nur als Aufzählungszeichen und zur visuellen Ordnung im eigentlichen Textbereich angedacht?
Guten Abend alle. Ich finde die „Fluide Navigation“ für mich sehr interssant und wende das auch an. Ich versuche jedoch ein Untermenu einzubinden, leider mit wenig Erfolg. Die zweite Ebene ist nicht ersichtlich bzw mit Hover wird da nichts als „Submenu“ dargestellt. Eine Zweite Menüebene, wie oben im Beispiel beschrieben, zu gestalten, bewirkt bei der Fluide Navigation keinen Effekt. Da sind andere/zusätzliche CSS Befehle zu vergeben, ich werde aber leider nicht schlau oder mir fehlen sicher auch die nötigen Kenntnisse.
Hallo Reto, soweit ich weiss, bekommst Du bei der Fluiden Navigation keine Menü-Unterpunkte eingebaut. Habe ich im Beitrag ergänzt.