Notch und Home-Indikator umschiffen – env(safe-area-inset-*) in CSS
Dein Button klebt am unteren Rand und liegt auf dem iPhone genau unter dem Home-Indikator? Das löst CSS von sich aus – mit env(safe-area-inset-*) und einem Meta-Tag, das fast immer vergessen wird. Ich zeige das Muster, den häufigsten Fehler und wie du den Wert sogar im Canvas nutzt.
Es gibt einen Fehler, den ich in fast jeder mobilen Web-App sehe: Ein Element klebt per position: fixed am unteren Rand – und auf dem iPhone liegt es dann zur Hälfte hinter dem Home-Indikator. Oben dasselbe Spiel mit Notch und Dynamic Island. Dafür brauchst du keine Geräteabfrage, das Betriebssystem sagt dir die Maße selbst.
Zwei Dinge, sonst passiert nichts
Erstens braucht dein Viewport viewport-fit=cover. Ohne das legt der Browser die Seite gar nicht erst unter die Notch – die Insets sind dann alle 0, und du wunderst dich, warum dein CSS nichts tut:
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">Zweitens liest du die Werte mit env() aus. Immer mit Fallback, denn auf dem Desktop existieren sie nicht:
.bottom-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
/* 12px eigener Abstand PLUS das, was das Gerät beansprucht */
padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 12px);
padding-left: calc(env(safe-area-inset-left, 0px) + 16px);
padding-right: calc(env(safe-area-inset-right, 0px) + 16px);
}
.top-bar {
position: fixed;
top: 0;
padding-top: calc(env(safe-area-inset-top, 0px) + 10px);
}Die vier Werte heißen safe-area-inset-top, -right, -bottom und -left. Im Hochformat ist meist nur oben und unten etwas gesetzt, im Querformat wandert der Notch an die Seite – deshalb gehören links und rechts genauso dazu. Genau das wird am häufigsten vergessen.
Der häufigste Fehler: Padding statt Margin – und umgekehrt
Faustregel, die mir viel Ärger erspart hat:
- Hat das Element einen Hintergrund, der bis zum Rand gehen soll (Leiste, Sheet, Banner) →
padding. Die Farbe läuft dann sauber unter den Indikator, nur der Inhalt rückt nach oben. - Hat es keinen durchgehenden Hintergrund (schwebender Button, Toast) →
marginoder direktbottom: calc(env(safe-area-inset-bottom, 0px) + 12px).
Setzt du bei einer Leiste margin-bottom, entsteht ein hässlicher Streifen in der Hintergrundfarbe der Seite.
Bonus: den Wert in JavaScript oder im Canvas nutzen
env() gibt es nur in CSS – ein <canvas>, das ein HUD an den oberen Rand zeichnet, kann es nicht lesen. Der Trick: den Wert einmal in eine CSS-Variable schreiben und von dort abholen.
:root { --sat: env(safe-area-inset-top, 0px); }const sat = parseFloat(
getComputedStyle(document.documentElement).getPropertyValue('--sat')
) || 0;
ctx.fillText('Punkte: 42', 16, sat + 28); // liegt jetzt unter der NotchWichtig: Beim Drehen des Geräts ändern sich die Werte. Cache den Wert also nicht für die Ewigkeit, sondern setz ihn im resize-Handler zurück – genau da bin ich in einem eigenen Spiel schon einmal reingelaufen.
Testen ohne iPhone
Du musst nicht raten. Überschreib die Variable kurz im DevTools-Konsolenfenster und schau dir das Ergebnis an:
document.documentElement.style.setProperty('--sat', '59px');Für env() selbst geht das nicht – deshalb lohnt es sich, die Insets von Anfang an über eigene Variablen zu führen. Dann ist jedes Layout in zwei Sekunden prüfbar.
Das Ganze kostet fünf Minuten und ist der Unterschied zwischen „läuft auf dem Handy" und „fühlt sich nach App an". Mehr HTML- & CSS-Snippets aus meiner täglichen Arbeit findest du auf code.bymw.de – und wenn du eine App oder Website brauchst, die auf echten Geräten sauber sitzt, meld dich bei mir.
Quellen
Du brauchst mehr als ein Snippet?
Ich entwickle Android-Apps in Kotlin und moderne Websites für Selbstständige und kleine Unternehmen — von der ersten Idee bis zum Release.
Projekt anfragen →Verwandte Snippets
:is() und :where() – kürzere Selektoren, und einer davon wiegt nichts
Drei fast gleiche Selektoren untereinander, nur weil die Überschrift mal h2 und mal h3 heißt? :is() fasst das zusammen. Und :where() macht dasselbe, ohne die Spezifität hochzutreiben – der Unterschied ist der ganze Trick.
content-visibility – lange Seiten rendern, ohne dass der Browser alles auf einmal baut
Bei sehr langen Seiten rechnet der Browser Layout und Malen für jeden Abschnitt – auch für die, die 8.000 Pixel weiter unten liegen. Eine CSS-Eigenschaft verschiebt diese Arbeit auf den Moment, in dem sie gebraucht wird.
color-mix() – Hover- und Dark-Mode-Farben ausrechnen lassen, statt sie zu pflegen
Für jede Markenfarbe eine hellere, eine dunklere und eine transparente Variante von Hand zu pflegen, ist Fleißarbeit mit Fehlerquote. color-mix() mischt sie im Browser – aus genau einer Quellfarbe.