Zum Inhalt springen

Navigation

Eine App ist mehr als eine Seite. Nutzer müssen wissen, wo sie sind und wohin sie können. HTML bringt dafür heute erstaunlich viel mit: Vieles, wofür früher Skriptbibliotheken nötig waren, ist eingebaut.

  • ein Hauptmenü semantisch bauen und den aktuellen Punkt markieren
  • Untermenüs mit details umsetzen
  • Brotkrumen und Reiter gestalten
  • Dialoge mit dialog barrierefrei öffnen

Live-Coding zum Mitmachen

⤓ demo-16-navigation.zip herunterladen

Der Ordner starter ist der Ausgangsstand, fertig der Stand nach dem Live-Coding.

Enthält: index.html, stil.css

Immer dasselbe Muster: nav, eine Liste, Links.

Hauptmenü mit aktuellem Punkt
.menue { display: flex; flex-wrap: wrap; gap: 0.25rem; margin: 0; padding: 0; list-style: none; background: #312e81; }
.menue a { display: block; padding: 0.75rem 1rem; color: #fff; text-decoration: none; }
.menue a:hover { background: #4338ca; }
.menue a:focus-visible { outline: 3px solid #fbbf24; outline-offset: -3px; }
.menue a[aria-current="page"] { box-shadow: inset 0 -3px #fbbf24; font-weight: 600; }
<nav aria-label="Hauptmenü">
<ul class="menue">
<li><a href="#" aria-current="page">Start</a></li>
<li><a href="#">Projekte</a></li>
<li><a href="#">Team</a></li>
</ul>
</nav>
  • aria-label benennt den Bereich, nötig, sobald es mehrere nav auf einer Seite gibt.
  • aria-current="page" kennzeichnet den aktuellen Punkt für Screenreader und ist zugleich dein CSS-Haken.
  • display: block am Link vergrößert die Klickfläche auf den ganzen Eintrag.

details und summary sind ein eingebautes Auf- und Zuklappen: mit Tastatur, ansagbar, ohne eine Zeile JavaScript.

Untermenü, das über dem Inhalt schwebt
.menue { display: flex; gap: 0.5rem; margin: 0; padding: 0.5rem; list-style: none; background: #e4e7f1; }
.menue > li { position: relative; }
.menue a, summary { display: block; padding: 0.5rem 0.75rem; color: inherit; cursor: pointer; }
details ul { position: absolute; z-index: 1; min-width: 10rem; margin: 0; padding: 0.25rem; list-style: none; background: #fff; box-shadow: 0 4px 12px rgb(0 0 0 / 0.25); }
<nav aria-label="Hauptmenü">
<ul class="menue">
<li><a href="#">Start</a></li>
<li>
<details>
<summary>Projekte</summary>
<ul>
<li><a href="#">Umzug</a></li>
<li><a href="#">Sommerfest</a></li>
</ul>
</details>
</li>
</ul>
</nav>

Für Menüs, die ein Klick außerhalb schließen soll, reicht details allein nicht. Dafür gibt es Popover (Ausblick unten) oder ein paar Zeilen JavaScript.

Sie zeigen den Pfad durch eine Hierarchie. Eine geordnete Liste passt genau, die Trenner kommen aus dem CSS.

Brotkrumen
.krumen { display: flex; flex-wrap: wrap; margin: 0; padding: 0; list-style: none; }
.krumen li + li::before { content: "/"; margin-inline: 0.5rem; color: #55606e; }
.krumen a[aria-current="page"] { color: inherit; text-decoration: none; }
<nav aria-label="Brotkrumen">
<ol class="krumen">
<li><a href="#">Start</a></li>
<li><a href="#">Projekte</a></li>
<li><a href="#" aria-current="page">Umzug</a></li>
</ol>
</nav>

Wenn jeder Reiter eine eigene Seite oder Ansicht ist, baue ihn als gewöhnliches Menü aus Links, das nur wie Reiter aussieht. So funktionieren Lesezeichen und der Zurück-Knopf. Das Muster steht unten im Live-Coding.

Echte Reiter, die auf derselben Seite Bereiche umschalten, brauchen JavaScript und ARIA-Rollen (role="tablist"). Das lohnt sich selten.

dialog mit showModal() erledigt alles, was früher schwierig war: Der Dialog liegt über allem, der Rest ist gesperrt, der Fokus bleibt gefangen, und die Escape-Taste schließt.

Modaler Dialog
dialog { border: 0; border-radius: 0.5rem; box-shadow: 0 8px 32px rgb(0 0 0 / 0.3); }
dialog::backdrop { background: rgb(0 0 0 / 0.5); }
<button type="button" onclick="document.getElementById('frage').showModal()">Dialog öffnen</button>
<dialog id="frage" aria-labelledby="frage-titel">
<h2 id="frage-titel">Wirklich löschen?</h2>
<form method="dialog">
<button value="nein">Abbrechen</button>
<button value="ja">Löschen</button>
</form>
</dialog>
  • <form method="dialog"> schließt den Dialog beim Absenden, ohne Server.
  • ::backdrop gestaltet die Abdunklung.
  • Zum Öffnen brauchst du eine Zeile JavaScript. Mehr dazu im Kapitel Ein wenig JavaScript.
Popover (nur in neueren Browsern)
[popover] { padding: 1rem; border: 1px solid #999; border-radius: 0.5rem; }
<button type="button" popovertarget="info">Hinweis ein/aus</button>
<div id="info" popover>Ein Klick außerhalb oder die Escape-Taste schließt mich.</div>

Das Brett bekommt Untermenü, Brotkrumen, Reiter und einen Dialog für neue Aufgaben.

stil.css
/* Untermenü mit details: aufklappen ohne JavaScript */
summary {
padding: var(--abstand-s) var(--abstand-m);
border-radius: var(--radius);
cursor: pointer;
}
summary:hover {
background: var(--farbe-flaeche);
}
summary:focus-visible {
outline: 3px solid var(--farbe-fokus);
}
details ul {
margin-inline-start: var(--abstand-m);
}
/* Brotkrumen: Trenner kommen aus dem CSS, nicht in den vorgelesenen Text */
.krumen {
display: flex;
flex-wrap: wrap;
margin: 0 0 var(--abstand-s);
padding: 0;
list-style: none;
font-size: 0.875rem;
}
.krumen li + li::before {
content: "/";
margin-inline: var(--abstand-s);
color: var(--farbe-text-leise);
}
.krumen a {
color: var(--farbe-akzent);
}
.krumen a[aria-current="page"] {
color: inherit;
text-decoration: none;
}
/* Reiter: Links, die wie Tabs aussehen */
.reiter {
display: flex;
gap: var(--abstand-s);
margin-bottom: var(--abstand-m);
border-bottom: 1px solid var(--farbe-linie);
}
.reiter a {
display: block;
padding: var(--abstand-s) var(--abstand-m);
border-bottom: 3px solid transparent;
color: inherit;
text-decoration: none;
}
.reiter a:hover {
border-bottom-color: var(--farbe-linie);
}
.reiter a[aria-current="page"] {
border-bottom-color: var(--farbe-akzent);
font-weight: 600;
}
/* Knöpfe */
.knopf {
padding: var(--abstand-s) var(--abstand-m);
border: 0;
border-radius: var(--radius);
background: var(--farbe-akzent);
color: #fff;
font: inherit;
cursor: pointer;
}
.knopf.leise {
background: #e4e7f1;
color: var(--farbe-text);
}
.filter {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--abstand-s);
}
.filter .knopf {
margin-inline-start: auto;
}
/* Dialog: Zentrieren und Abdunkeln erledigt der Browser, wir gestalten nur */
dialog {
width: min(28rem, 90vw);
border: 0;
border-radius: var(--radius);
box-shadow: var(--schatten);
}
dialog::backdrop {
background: rgb(0 0 0 / 0.5);
}
dialog input {
display: block;
width: 100%;
padding: var(--abstand-s);
font: inherit;
}
.dialog-knoepfe {
display: flex;
justify-content: flex-end;
gap: var(--abstand-s);
margin-top: var(--abstand-m);
}

Starter für die Übung

⤓ uebung-16-navigation.zip herunterladen

ZIP-Datei entpacken und den Ordner in VS Code öffnen (Datei → Ordner öffnen).

Enthält: hilfe.html, index.html, stil.css, uebung.css

Die neue Seite hilfe.html hat die Struktur schon. Gestalte sie in uebung.css.

.pfad wird eine Zeile, zwischen den Einträgen steht ein ›.

.themen zeigt die Themen als Pillen nebeneinander. Das aktuelle Thema fällt auf.

Gestalte die details in .fragen als Karten. Eine geöffnete Frage hat eine fette Überschrift (Tipp: details[open]).

Der Dialog „Konto löschen“ warnt deutlich: rote Abdunklung, roter Knopf, und die Knöpfe stehen rechts. Probiere die Tastatur aus: Wo steht der Fokus nach dem Öffnen, und wo nach dem Schließen?