Über diese Website

Dieser Kasten steht auf jeder Seite in der linken Spalte. Was darin steht, änderst du in der Konfiguration unter Plugins — oder du schaltest ihn dort ab.

Über dieses Handbuch

Dieses Handbuch gehört zur Vorlage Example für QuartzControl. Es erklärt die Vorlage und führt zugleich alles vor, was sie kann.

Der Text hier kommt aus einer Markdown-Datei, nicht aus der Konfiguration.

Dropdown-Navigation mit Disclosure-Buttons bauen

Dropdown-Navigation mit Disclosure-Buttons bauen

Ziel

Eine Navigation mit Untermenüs, die mit Maus, Tastatur, Touch und Screenreader funktioniert. Die Untermenüs öffnen sich per Button, nicht nur beim Hover und nicht schon beim Durchtabben.

Grundstruktur der Navigation: Hauptnavigation semantisch aufbauen.

Voraussetzungen

  • Variante A (Popover): kein JavaScript nötig. Die Popover API ist seit 2025-01-27 Baseline newly available.
  • Variante B (klassisch): JavaScript für den Zustand, Esc und den Fokus.

Getestet mit / Stand

Stand 2026-09-12. Variante A am 2026-09-13 in Chrome 152 (macOS) getestet: Ein echter Klick öffnet, ein zweites Untermenü schließt das erste, Tab springt ins Untermenü, Esc schließt und setzt den Fokus zurück auf den Button, ein Klick daneben schließt. Beide Platzierungen landen wie gewollt. Nicht getestet: Variante B, Firefox, Safari, Screenreader. Den automatischen aria-expanded-Zustand konnte das verwendete Werkzeug nicht anzeigen. Das APG betont selbst, dass man die Beispiele vor dem Produktiveinsatz mit assistiven Technologien prüfen muss.1

Vorgehen

Gemeinsame Regeln

  1. Kein role="menu". Die Rolle verspricht ein Verhalten wie in Desktop-Programmen, das Navigationen auf Websites nicht bieten.12
  2. Button statt Hover. Ein Untermenü öffnet sich erst, wenn man den Button betätigt. Beim Tabben durch die Navigation bleiben die Untermenüs zu, sonst müsste man sich mit der Tastatur durch alle Unterpunkte arbeiten.3
  3. Oberpunkt, der selbst eine Seite ist: ein Link und daneben ein eigener Button für das Untermenü.3
  4. Struktur: Das Untermenü ist eine verschachtelte Liste im selben Listenpunkt wie sein Button.1
  5. Esc schließt und setzt den Fokus zurück auf den Button. Das ist für WCAG 1.4.13 „Content on Hover or Focus“ nötig.1

Variante A: Popover

<nav aria-label="Hauptmenü">
  <ul class="nav-list">
    <li><a href="/">Start</a></li>
    <li>
      <button type="button" popovertarget="sub-leistungen">Leistungen</button>
      <ul id="sub-leistungen" popover class="submenu">
        <li><a href="/leistungen/beratung/">Beratung</a></li>
        <li><a href="/leistungen/umsetzung/">Umsetzung</a></li>
      </ul>
    </li>
    <li><a href="/kontakt/">Kontakt</a></li>
  </ul>
</nav>

Das Popover steht im DOM direkt nach seinem Button.4

Platzierung unter dem Header in voller Breite. Das lehnt sich an Lehner an, der top, margin: 0 und width: 100% setzt. inset: auto und left: 0 habe ich ergänzt:2

.submenu {
  inset: auto;
  top: var(--header-height, 4rem);
  left: 0;
  margin: 0;
  width: 100%;
}

↗ Beispiel in neuem Tab öffnen

Oder direkt unter dem Button, als Zusatz für Browser mit Anchor Positioning. Popover und Button sind über popovertarget bzw. commandfor automatisch verankert, anchor-name und position-anchor braucht man nicht:56

@supports (position-area: bottom) {
  .submenu {
    inset: auto;
    margin: 0;
    width: auto;
    position-area: bottom span-right;
  }
}

↗ Beispiel in neuem Tab öffnen

Variante B: klassisch mit aria-expanded

<li>
  <button type="button" aria-expanded="false" aria-controls="sub-leistungen">Leistungen</button>
  <ul id="sub-leistungen" class="submenu" hidden>
    …
  </ul>
</li>

JavaScript muss dann selbst:1

  • beim Klick aria-expanded umschalten und hidden entfernen oder setzen,
  • bei Esc schließen und den Fokus auf den Button setzen,
  • schließen, wenn der Fokus die Navigation verlässt.

Das Aussehen an den Zustand koppeln statt an eine Klasse:

button[aria-expanded="true"]::after { rotate: 180deg; }

↗ Beispiel in neuem Tab öffnen

Das APG zeichnet den Pfeil mit einem Rahmen in ::after, damit er im Hochkontrastmodus sichtbar bleibt.1

Warum funktioniert das?

Bei der Popover-Variante erledigt der Browser, was man bei Variante B von Hand bauen muss:54

AufgabePopoverklassisch
aria-expanded am Buttonautomatisch (nur deklarativ)Skript
Esc schließtautomatischSkript
Fokus zurück an den Buttonautomatisch, wenn der Fokus im Menü lagSkript
Klick daneben schließtautomatischSkript
nur ein Untermenü offenautomatisch (auto-Popovers)Skript
Über anderem Inhalt, nicht abgeschnittenTop Layerz-index, overflow beachten
Schließen, wenn der Fokus die Navigation verlässtnein, in Chrome 152 bleibt das Untermenü beim Wegtabben offenSkript

Die letzte Zeile habe ich getestet, in den Quellen steht sie nicht: Weder MDN noch de Vries/O’Hara beschreiben ein Schließen beim Wegtabben, und Chrome 152 tut es auch nicht. Das APG verlangt es für seine Variante. Wer das will, ergänzt einen kleinen focusout-Handler.

Fallstricke

  • Popover per Skript öffnen: Dann fällt das automatische aria-expanded weg.4
  • Untermenü per CSS mit display: block dauerhaft anzeigen (z. B. auf dem Desktop): Der Button meldet trotzdem „zugeklappt“.4
  • width: 100% mit Innenabstand oder Rahmen: Das Untermenü wird dann breiter als der Viewport, und es entsteht horizontales Scrollen. box-sizing: border-box am Untermenü verhindert das. Im Beispiel mit Chrome 152 nachgestellt (ohne ragte es 34 Pixel über).
  • Standardposition des Popovers: mittig im Viewport mit Rahmen. Ohne eigene Platzierung wirkt das Untermenü „verloren“.5
  • Popover per showPopover() ohne Button geöffnet: Dann fehlt die automatische Ankerbeziehung, und das Popover landet mit inset: auto; margin: 0 oben links im Viewport (in Chrome 152 getestet). Mit showPopover({ source: button }) stimmt die Position wieder.
  • position-area lässt sich nicht mischen: Physische und logische Werte in einer Deklaration machen sie ungültig. bottom span-right geht, block-end span-right nicht.6
  • Ohne feste Breite verhält sich das verankerte Untermenü wie width: max-content, höchstens so breit wie der Viewport.6
  • Eigene Ankernamen in wiederholten Komponenten: Wer statt der automatischen Verknüpfung anchor-name benutzt, bekommt bei gleichem Namen immer den letzten Anker im Dokument. anchor-scope begrenzt den Namen auf die Komponente.6
  • Anchor Positioning ohne Fallback: Browser, die das nicht können, zeigen das Popover dann an der Standardposition. Die @supports-Abfrage im Beispiel verhindert das. Ob die automatische Verankerung in Firefox und Safari greift, habe ich nicht geprüft.
  • Zusätzlich beim Hover öffnen: Wer das will, braucht JavaScript und eine Verzögerung beim Schließen. WAI nennt eine Sekunde als Beispiel.3 Öffnen beim Hover ohne JavaScript („interest invokers“) gibt es laut web-features 3.38.0 nur in Chromium (ab 142).
  • Safari und aria-details: Laut de Vries/O’Hara setzte Safari zum Zeitpunkt ihres Artikels nur aria-expanded, nicht aria-details und keine group-Rolle.4
  • Pfeiltasten: Im APG sind sie optional und ersetzen Tab nicht. Screenreader im Lesemodus fangen sie ohnehin ab.1

Siehe auch

Quellen

Das Anchor-Beispiel mit @supports und position-area: bottom span-right, die Vergleichstabelle und der Pfeil per rotate habe ich selbst umgesetzt. MDN zeigt span-right nur nach demselben Muster (top span-left). Laut web-features 3.38.0 gibt es den Wert in Chrome 129, Firefox 147 und Safari 26.

Footnotes

  1. Example Disclosure Navigation Menu (W3C APG). ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7

  2. How to build Accessible Navigation Menus with the Popover API, Abschnitte „Avoid the menu role“ und „Visual Placement“. ↩ ↩2

  3. Menus Tutorial (W3C WAI), Seite „Fly-out Menus“. ↩ ↩2 ↩3

  4. On popover accessibility – what the browser does and doesn’t do. ↩ ↩2 ↩3 ↩4 ↩5

  5. Using the Popover API (MDN), Abschnitte „Popover accessibility features“, „Positioning popovers“ und „Popover anchor positioning“. ↩ ↩2 ↩3

  6. Using CSS anchor positioning (MDN), Abschnitte „Implicit anchor association“, „Anchor scoping“ und „Setting a position-area“. ↩ ↩2 ↩3 ↩4

Weiterlesen

Diese Seite gehört zu gui-test — der Beispielvorlage für QuartzControl. Ein Überblick über alle Bereiche steht auf der Startseite.

Auf einem schmalen Bildschirm ist die Navigation oben eingeklappt.