Wer kennt das: Hero-Section auf min-height: 100vh gesetzt, auf Desktop perfekt — auf Mobile ragt der Inhalt unten raus oder der Button verschwindet hinter der Browser-Leiste.
Das ist kein Bug im eigenen Code. Das ist ein fundamentales Problem mit vh.
Warum 100vh auf Mobile nicht stimmt
vh ist definiert als 1% der Viewport-Höhe. Klingt eindeutig. Ist es aber nicht.
Auf Mobile hat der Browser eine Chrome-Leiste — Adressleiste oben, Navigation unten. Diese Leiste verschwindet beim Scrollen, taucht beim Zurückscrollen wieder auf. Die Höhe des sichtbaren Bereichs ändert sich also ständig.
Browser haben sich entschieden: vh entspricht der Viewport-Höhe ohne die Chrome-Leiste — also dem maximalen sichtbaren Bereich, wenn die Leiste komplett versteckt ist. Beim ersten Laden ist die Leiste aber sichtbar, und der eigentliche sichtbare Bereich ist kleiner als 100vh.
Ergebnis: ein Element mit height: 100vh ist beim ersten Laden zu groß. Der untere Teil ragt unter die Navigation.
┌──────────────────┐
│ Adressleiste │ ← sichtbar beim Laden
├──────────────────┤
│ │
│ 100vh beginnt │
│ hier │
│ │
│ │
│ │
│ 100vh endet │
│ hier │
│ │ ← dieser Teil ist unsichtbar
├──────────────────┤
│ Browser-Nav │ ← sichtbar beim Laden
└──────────────────┘
Die neuen Viewport-Units
CSS hat 2023 drei neue Units eingeführt, die genau dieses Problem lösen:
svh — Small Viewport Height
svh ist 1% der kleinsten möglichen Viewport-Höhe — also mit vollständig sichtbarer Chrome-Leiste. Das ist der sichere Wert: ein Element mit 100svh passt immer in den sichtbaren Bereich, egal ob die Leiste ein- oder ausgeblendet ist.
Nachteil: Es ist der konservativste Wert. Wenn die Chrome-Leiste verschwindet, entsteht unten Leerraum.
lvh — Large Viewport Height
lvh ist 1% der größten möglichen Viewport-Höhe — also ohne Chrome-Leiste. Entspricht dem bisherigen vh-Verhalten. Für die meisten Fälle also kein Fortschritt.
dvh — Dynamic Viewport Height
dvh passt sich dynamisch an den aktuellen sichtbaren Bereich an. Ist die Chrome-Leiste sichtbar, ist 100dvh kleiner. Verschwindet sie beim Scrollen, wächst 100dvh mit.
Das ist der Wert, den man fast immer haben will.
Wann nimmt man was?
| Unit | Wert | Geeignet für |
|---|---|---|
vh | Größter Viewport (legacy) | Nichts Neues — vermeiden |
svh | Kleinster Viewport | Elemente die nie abgeschnitten sein dürfen |
lvh | Größter Viewport | Elemente die beim Scrollen volle Höhe haben sollen |
dvh | Aktueller Viewport | Fast alles — Hero-Sections, Fullscreen-Layouts, Modals |
Konkret:
/* Hero-Section: immer sichtbar, passt sich an */
.hero {
min-height: 100dvh;
}
/* Modal: soll nie größer sein als der sichtbare Bereich */
.modal {
max-height: 100svh;
}
/* Fullscreen-Hintergrund beim Scrollen */
.backdrop {
height: 100lvh;
}
Fallback für ältere Browser
Browser-Support für dvh ist seit 2023 sehr gut — Chrome 108+, Safari 15.4+, Firefox 101+. Für ältere Browser reicht ein einfacher Fallback:
.hero {
min-height: 100vh; /* Fallback */
min-height: 100dvh; /* Modern */
}
Browser die dvh nicht kennen, nehmen die erste Deklaration. Alle anderen nehmen die zweite. Keine JavaScript-Hacks, keine resize-Listener.
In Tailwind
Tailwind hat dvh seit v3.4 eingebaut:
<section class="min-h-dvh">...</section>
<section class="min-h-svh">...</section>
<section class="min-h-lvh">...</section>
Wer Tailwind 4 nutzt, kann außerdem eigene Tokens definieren — oder direkt als Inline-Style setzen wenn man einen spezifischen Breakpoint-Mix braucht:
<!-- Nur auf Mobile dvh, auf Desktop klassisch -->
<section
class="md:min-h-screen"
style="min-height: 100dvh;">
Fazit
vh war jahrelang das Beste was man hatte. Für mobile Fullscreen-Layouts war es aber immer ein Kompromiss mit JavaScript-Workarounds oder übermäßig konservativen Höhen.
dvh macht das obsolet. Es macht genau das, was man von vh immer erwartet hat — verhält sich aber korrekt mit der dynamischen Browser-Chrome-Leiste.
Wer heute eine neue Website baut: dvh als Standard, vh als Fallback. Fertig.
Entdeckt beim Bauen der The Moon Records Website — dort löst 100dvh auf der Hero-Section genau dieses Problem.
Kommentare