Mega-Menü Schritt für Schritt: HTML, CSS und JavaScript mit Barrierefreiheit

Wie Sie ein Mega-Menü bauen: HTML-, CSS- und JavaScript-Code zum Kopieren, Bedienung per Tastatur und Screenreader, Smartphone-Version, häufige Fehler und Demo.

Illustration: Navigationsleiste einer Website mit aufgeklapptem Mega-Menü-Panel und einem Mauszeiger auf einem hervorgehobenen Eintrag
KI-generierte Illustration

Ein Mega-Menü ist ein großes Panel, das sich aus dem oberen Menü aufklappt und mehrere Gruppen von Links auf einmal zeigt, oft mit kurzen Beschreibungen. Sie kennen es aus Onlineshops und von Websites von Firmen mit umfangreichem Angebot. Gut gemacht, verkürzt es den Weg zu einer bestimmten Leistung. Schlecht gemacht, schließt es sich, bevor der Mauszeiger es erreicht, funktioniert auf dem Smartphone nicht und lässt sich nicht per Tastatur bedienen.

In diesem Ratgeber bauen wir ein Mega-Menü von Grund auf: HTML, CSS und etwa 30 Zeilen JavaScript, ganz ohne Bibliothek. Den Code habe ich im Browser geprüft, und das funktionierende Demo öffnen Sie hier. Dasselbe Muster läuft in meinem eigenen Menü: dem Button „Kostenlose Tools“ oben auf dieser Seite.

Wann ein Mega-Menü sinnvoll ist

Ein Mega-Menü ist nützlich, wenn:

  • Sie in einem Teil des Angebots mehr als 7–8 Unterseiten haben, zum Beispiel mehrere Arten von Leistungen in mehreren Gruppen,
  • sich das Angebot natürlich in Kategorien gliedert (Onlineshop, Baufirma, Buchhaltungsbüro mit Leistungen für verschiedene Kundengruppen),
  • Sie neben den Links eine kurze Beschreibung oder einen hervorgehobenen Call-to-Action zeigen möchten.

Hat die ganze Website nur 5–6 Unterseiten, genügt ein gewöhnliches Menü. Ein Mega-Menü mit drei Links wirkt leer und macht den Code nur länger.

Wie ein gutes Mega-Menü funktionieren soll

Legen Sie die Regeln fest, bevor Sie Code schreiben. Jede davon hat ihren Grund:

  1. Es öffnet sich per Klick, und per Tastatur mit Enter oder der Leertaste. Das bloße Überfahren mit der Maus funktioniert auf dem Smartphone nicht.
  2. Es schließt sich bei einem Klick daneben, mit der Escape-Taste und dann, wenn Sie mit der Tab-Taste weitergehen.
  3. Der Screenreader weiß, ob das Panel aufgeklappt ist, dank des Attributs aria-expanded.
  4. Die Links sind gewöhnliche <a>-Tags im HTML, damit Google alle Unterseiten sieht.
  5. Zwischen Button und Panel gibt es keine Lücke, in der der Mauszeiger das Menü „verliert“.
  6. Auf dem Smartphone wird das Panel zu einer Dropdown-Liste unter dem Button „Menu“.
  7. Die Gruppen haben Überschriften, und das ganze Panel hat höchstens einen hervorgehobenen Call-to-Action.
Mega-Menü am Computer: drei Spalten mit Links und kurzen Beschreibungen sowie eine blaue Karte „Bezpłatny pomiar“ (kostenloses Aufmaß)
Demo am Computer: drei Gruppen von Links mit Beschreibungen und ein Call-to-Action.

Schritt 1: HTML

Jeder aufklappbare Eintrag besteht aus einem Button und einem Panel. Ein Button und kein Link, weil er nichts unter einer neuen Adresse öffnet, sondern nur das Panel zeigt. Das Attribut aria-controls verweist auf das Panel, und hidden blendet es am Anfang aus:

<header class="top">
  <div class="top__in">
    <a class="logo" href="/">Pomost</a>
    <button class="burger" type="button" aria-expanded="false" aria-controls="menu">Menu</button>

    <nav class="nav" id="menu" aria-label="Menu główne">
      <ul class="nav__list">
        <li>
          <button class="nav__toggle" type="button" aria-expanded="false" aria-controls="mm-oferta">Oferta</button>
          <div class="mega" id="mm-oferta" hidden>
            <div class="mega__in">
              <div>
                <p class="mega__title" id="mm-tarasy">Tarasy</p>
                <ul aria-labelledby="mm-tarasy">
                  <li><a href="/tarasy-kompozytowe">Tarasy kompozytowe<small>Bez malowania, na lata</small></a></li>
                  <li><a href="/tarasy-drewniane">Tarasy drewniane<small>Modrzew, sosna, egzotyki</small></a></li>
                </ul>
              </div>
              <!-- weitere Spalten: Gärten, Zubehör -->
              <div class="mega__promo">
                <b>Bezpłatny pomiar</b>
                <p>Przyjedziemy, zmierzymy i w 2 dni prześlemy wycenę.</p>
                <a href="/kontakt">Umów pomiar</a>
              </div>
            </div>
          </div>
        </li>
        <li><a href="/realizacje">Realizacje</a></li>
        <li><a href="/kontakt">Kontakt</a></li>
      </ul>
    </nav>
  </div>
</header>

Die Überschrift einer Gruppe ist ein gewöhnlicher Absatz, und die Liste ist über aria-labelledby mit ihr verbunden. Der Screenreader liest dann „Tarasy, Liste, 3 Einträge“ vor, und in der Überschriftenstruktur der Seite tauchen nicht plötzlich mehr als ein Dutzend zusätzliche H2 auf.

Häufig sehe ich in solchen Menüs role="menu" und role="menuitem". Das ist ein Fehler: Diese Rollen sind für Anwendungsmenüs gedacht (wie „Datei, Bearbeiten“ in einem Editor) und versprechen eine Bedienung mit den Pfeiltasten, die eine gewöhnliche Website nicht bietet. Für die Navigation empfehlen die WAI-ARIA Authoring Practices genau das Muster „Disclosure“, also einen Button, der Inhalte ein- und ausblendet.

Schritt 2: CSS

Am wichtigsten sind drei Dinge: Das Panel schließt ohne Lücke an den Header an, das Aussehen des Buttons hängt von aria-expanded ab, und der Tastaturfokus ist gut sichtbar:

.top { position: relative; background: #fff; }
.nav__list { display: flex; gap: 4px; margin: 0; padding: 0; list-style: none; }
.nav__toggle { display: inline-flex; align-items: center; gap: 8px; padding: 10px 14px;
  border: 0; border-radius: 10px; background: none; font: inherit; cursor: pointer; }

/* Der Pfeil dreht sich, wenn das Panel geöffnet ist: Der Zustand wird direkt aus aria-expanded gelesen */
.nav__toggle::after { content: ""; width: 7px; height: 7px; border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor; transform: translateY(-2px) rotate(45deg); transition: transform .2s; }
.nav__toggle[aria-expanded="true"]::after { transform: translateY(1px) rotate(-135deg); }

/* Panel über die volle Breite, beginnt genau unter dem Header (top: 100%, ohne Außenabstand) */
.mega { position: absolute; left: 0; right: 0; top: 100%; z-index: 10; background: #fff;
  box-shadow: 0 24px 40px -24px rgba(17, 21, 28, .25); }
.mega__in { max-width: 1120px; margin: 0 auto; padding: 28px 20px 32px;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)) minmax(0, 1.2fr); gap: 32px; }
.mega ul { margin: 0; padding: 0; list-style: none; }
.mega ul a { display: block; padding: 8px 10px; border-radius: 10px; text-decoration: none; }
.mega ul small { display: block; color: #5b6472; font-size: 13.5px; }

:focus-visible { outline: 3px solid #2f5bff; outline-offset: 2px; }

/* kurzes Einblenden des Panels, aber nicht für Personen, die Animationen im System abgeschaltet haben */
@media (prefers-reduced-motion: no-preference) {
  .mega:not([hidden]) { animation: wjazd .18s ease-out; }
}
@keyframes wjazd { from { opacity: 0; transform: translateY(-6px); } }

Beachten Sie minmax(0, 1fr) im Raster. Ohne das kann ein langes Wort in einer Spalte das Panel breiter machen als den Bildschirm.

Schritt 3: JavaScript

Das Skript tut vier Dinge: Es öffnet und schließt das Panel per Klick, schließt andere Panels, reagiert auf Escape und schließt das Menü, wenn Sie daneben klicken oder mit der Tab-Taste aus dem Menü hinausgehen:

const nav = document.querySelector('.nav');
const toggles = [...nav.querySelectorAll('.nav__toggle')];

function setOpen(btn, open) {
  btn.setAttribute('aria-expanded', String(open));
  document.getElementById(btn.getAttribute('aria-controls')).hidden = !open;
}
function closeAll(except) {
  toggles.forEach((b) => { if (b !== except) setOpen(b, false); });
}

toggles.forEach((btn) => btn.addEventListener('click', () => {
  const open = btn.getAttribute('aria-expanded') !== 'true';
  closeAll(btn);
  setOpen(btn, open);
}));

// Escape schließt das geöffnete Panel und springt zurück zu seinem Button
nav.addEventListener('keydown', (e) => {
  const btn = toggles.find((b) => b.getAttribute('aria-expanded') === 'true');
  if (e.key === 'Escape' && btn) { setOpen(btn, false); btn.focus(); }
});

// Ein Klick außerhalb des Menüs und ein Fokuswechsel auf ein Element außerhalb des Menüs schließen das Panel
document.addEventListener('click', (e) => { if (!nav.contains(e.target)) closeAll(); });
document.addEventListener('focusin', (e) => { if (!nav.contains(e.target)) closeAll(); });

Der Zustand des Menüs ist an einer einzigen Stelle gespeichert: in den Attributen aria-expanded und hidden. Das CSS liest ihn, um den Pfeil zu drehen, der Screenreader, um „aufgeklappt“ und „eingeklappt“ anzusagen, und das Skript, um zu entscheiden, was geschlossen wird. Es gibt keine separaten Klassen wie is-open, die von dem abweichen könnten, was eine blinde Person hört.

Eine Beobachtung aus den Tests: Die erste Version schloss das Menü im Ereignis focusout. Das funktionierte per Tastatur, aber das Panel verschwand beim Klick auf seinen Hintergrund, weil der Browser dann den Fokus vom Button nimmt. Das Ereignis focusin auf dem ganzen Dokument reagiert nur, wenn der Fokus tatsächlich auf ein anderes Element wechselt, und hat dieses Problem nicht.

Schritt 4: Version für das Smartphone

Auf dem Smartphone ist kein Platz für vier Spalten. Denselben HTML-Code verwandeln wir in eine Dropdown-Liste: Die Navigation verschwindet hinter dem Button „Menu“, und die Panels ordnen sich untereinander an:

.burger { display: none; }
@media (max-width: 860px) {
  .burger { display: inline-flex; margin-left: auto; padding: 10px 14px;
    border: 1px solid #e3e6eb; border-radius: 10px; background: none; font: inherit; }
  .nav { display: none; position: absolute; left: 0; right: 0; top: 100%; z-index: 10;
    max-height: calc(100dvh - 68px); overflow-y: auto; background: #fff; }
  .nav.is-open { display: block; }
  .nav__list { flex-direction: column; padding: 8px 12px 16px; }
  .nav__toggle { width: 100%; justify-content: space-between; padding: 14px 12px; }
  .mega { position: static; box-shadow: none; }
  .mega__in { grid-template-columns: minmax(0, 1fr); gap: 20px; padding: 8px 12px 16px; }
}
const burger = document.querySelector('.burger');
burger.addEventListener('click', () => {
  const open = burger.getAttribute('aria-expanded') !== 'true';
  burger.setAttribute('aria-expanded', String(open));
  nav.classList.toggle('is-open', open);
});

max-height mit der Einheit dvh und overflow-y: auto sorgen dafür, dass sich ein langes Menü auf einem kleinen Bildschirm scrollen lässt und die Adressleiste des Browsers auf dem Smartphone es nicht verdeckt.

Mega-Menü auf dem Smartphone: Button „Menu“, aufgeklappter Eintrag „Oferta“ (Angebot) und Gruppen von Links untereinander
Auf dem Smartphone funktioniert derselbe Code wie eine Dropdown-Liste unter dem Button „Menu“.

So testen Sie das Menü in 5 Minuten

Bevor Sie das Menü veröffentlichen, prüfen Sie es so, wie Menschen es nutzen werden:

  • Tastatur: Gehen Sie mit der Tab-Taste zu „Oferta“ (Angebot), Enter öffnet das Panel, weitere Tabs führen durch die Links, Escape schließt das Panel und kehrt zum Button zurück. Ein Tab nach dem letzten Link schließt das Panel.
  • Maus: Fahren Sie mit dem Mauszeiger diagonal vom Button zum Panel und klicken Sie auf eine leere Stelle im Panel. Das Panel soll geöffnet bleiben.
  • Screenreader: In NVDA (Windows) oder VoiceOver (Mac, iPhone) sollte der Button „Oferta, Schaltfläche, eingeklappt“ lauten und nach dem Öffnen „ausgeklappt“.
  • Smartphone und Vergrößerung: Bei 320 px Breite und 200 % Vergrößerung darf nichts über den Bildschirm hinausragen oder seitliches Scrollen erzwingen.
  • Lighthouse in Chrome (Registerkarte „Barrierefreiheit“) deckt fehlende Namen von Buttons und zu schwachen Kontrast auf.

Den allgemeinen Zustand Ihrer Website, einschließlich Barrierefreiheit, SEO und Geschwindigkeit, prüfen Sie in 30 Sekunden mit meinem Tool Website prüfen.

Die häufigsten Fehler beim Mega-Menü

  • Menü nur beim Überfahren. Auf dem Smartphone lässt es sich nicht öffnen, und am Computer öffnet es sich versehentlich.
  • Lücke zwischen Button und Panel. Schon margin-top: 8px genügt, damit der Mauszeiger auf dem Weg zum Panel das Menü verlässt und sich alles schließt.
  • <a href="#"> statt eines Buttons. Die Seite springt nach oben, und der Screenreader kündigt einen Link an, der nirgendwohin führt.
  • role="menu" ohne Bedienung mit den Pfeiltasten. Ein Versprechen, das die Seite nicht einhält, ist schlimmer als gar kein ARIA.
  • Links, die erst beim Überfahren per Skript nachgeladen werden. Google sieht sie womöglich nicht.
  • Zu viele Links. Ein Panel mit 120 Einträgen ist ein Katalog, keine Navigation. Die Suche dauert 2–3 Sekunden, und wer nichts findet, geht zurück zu Google.
  • Panel breiter als der Bildschirm auf dem Smartphone. Das klassische Symptom: Die Seite lässt sich um einige Pixel zur Seite verschieben.

Und WordPress?

In WordPress stammt das Mega-Menü meist vom Theme oder von einem Plugin. Bevor Sie es behalten, gehen Sie dieselbe Testliste durch: Viele Plugins öffnen das Menü nur beim Überfahren, setzen kein aria-expanded und laden auf jeder Unterseite einige Dutzend Kilobyte Skripte zusätzlich. Manchmal ist es einfacher und schneller, ein eigenes Menü auf Basis des Codes aus diesem Ratgeber zu ergänzen.

Zusammenfassung

Ein gutes Mega-Menü besteht aus einem Button mit aria-expanded, einem Panel mit hidden, gewöhnlichen Links im HTML und drei Wegen zum Schließen: Klick daneben, Escape und Tab. Auf dem Smartphone funktioniert derselbe Code als Dropdown-Liste. Den vollständigen Code aus diesem Artikel finden Sie im Demo: Öffnen Sie es, probieren Sie es mit Maus und Tastatur aus und sehen Sie sich dann den Quelltext der Seite an.

Wenn Ihre Website ein umfangreiches Angebot hat und das Menü nicht mithält, schreiben Sie mir. Ich baue die Navigation Ihrer bestehenden Website um oder entwerfe eine neue Firmenwebsite. Ich antworte in 24 Stunden, und Neukunden erhalten 20 % Rabatt auf den ersten Auftrag.

Häufige Fragen

Passende Leistungen

Haben Sie ein ähnliches Projekt?

Beschreiben Sie, was Sie brauchen, und Sie erhalten innerhalb von 24 Stunden ein Angebot. Neukunden erhalten 20 % Rabatt auf den ersten Auftrag.

Angebot anfragenGespräch buchen (20 Min.)

Weiterlesen

Websites30. Oktober 20266 Min. Lesezeit

Website-Briefing: 20 Fragen vor der Beauftragung

Website-Briefing in 20 Fragen: Ziel, Zielgruppe, Wettbewerber, Unterseiten, Texte, Integrationen, Domain und E-Mail. Ein schnelleres Angebot und weniger Korrekturen.