Ein- und Ausblenden mit CSS animieren
Ziel
Ein Element wie ein Popover, Dialog, Menü oder Hinweis soll weich einblenden, wenn es erscheint, und weich ausblenden, wenn es verschwindet, obwohl es dazwischen display: none hat. Auf Wunsch soll es auch auf seine natürliche Höhe (height: auto) auf- und zuklappen.
Voraussetzungen
- Grundlagen zu Transitions: CSS-Transitions und Keyframe-Animationen.
- Browser mit
@starting-styleundtransition-behavior(seit 2024-08-06 Baseline newly available).
Getestet mit / Stand
Stand 2026-09-12. Die CSS-Muster stammen von MDN. Am 2026-09-13 in Chrome 152 (macOS) getestet: Das Popover blendet ein und aus, display: none in Keyframes greift am Ende, interpolate-size animiert in beide Richtungen, der Trick mit 0fr/1fr ebenfalls. Nicht getestet: Firefox und Safari, dort gelten die Einschränkungen unten. Vorab das Wichtigste laut web-features 3.38.0:
- Einblenden mit
@starting-stylefunktioniert in allen großen Browsern. - Ausblenden hängt davon ab, dass
displaybeim Schließen verzögert wird. Das kann Firefox nicht (Feature „display animation“: Chrome 117, Safari 18). Dort verschwindet das Element sofort. overlaygibt es nur in Chromium.interpolate-sizegibt es nur in Chromium.
Vorgehen
Popover oder Dialog mit Transition
- Den offenen Zustand festlegen, also das, worauf das Einblenden hinläuft.
- Den geschlossenen Zustand am Element selbst festlegen, also das, worauf das Ausblenden hinläuft.
displayundoverlayin die Transition-Liste aufnehmen, beide mitallow-discrete.@starting-stylehinter die Regel für den offenen Zustand schreiben. Beide sind gleich spezifisch, also gewinnt die spätere.::backdropgesondert behandeln, mit eigenem@starting-style-Block außerhalb, weil&keine Pseudoelemente abbilden kann.
@media (prefers-reduced-motion: no-preference) {
/* 1. offen */
[popover]:popover-open {
opacity: 1;
translate: 0 0;
}
/* 2. geschlossen + 3. Transition-Liste */
[popover] {
opacity: 0;
translate: 0 -0.5rem;
transition:
opacity 200ms ease-out,
translate 200ms ease-out,
overlay 200ms allow-discrete,
display 200ms allow-discrete;
}
/* 4. Startzustand – nach Regel 1 */
@starting-style {
[popover]:popover-open {
opacity: 0;
translate: 0 -0.5rem;
}
}
/* 5. Backdrop */
[popover]::backdrop {
background-color: transparent;
transition:
background-color 200ms,
overlay 200ms allow-discrete,
display 200ms allow-discrete;
}
[popover]:popover-open::backdrop {
background-color: rgb(0 0 0 / 25%);
}
@starting-style {
[popover]:popover-open::backdrop {
background-color: transparent;
}
}
}↗ Beispiel in neuem Tab öffnen
Das ist das MDN-Beispiel, nur mit translate statt transform: scaleX() und in der Media Query.1 Für einen modalen Dialog geht dasselbe mit dialog:open und dialog, oder mit dialog[open], wo :open fehlt.2
Kürzer geht es mit transition: all 200ms allow-discrete;, dann aber für alle Eigenschaften. So steht es als Kommentar im MDN-Beispiel.1
Alternative: Keyframes
Mit Keyframe-Animationen ist @starting-style nicht nötig, und display darf direkt in Keyframes stehen.34
@keyframes fade-out {
to {
opacity: 0;
display: none;
}
}
.toast.is-leaving {
animation: fade-out 300ms forwards;
}↗ Beispiel in neuem Tab öffnen
forwards hält den Endzustand fest.4 Keyframes lohnen sich bei Effekten mit mehreren Stufen, die mit einer Transition nicht gehen. Die Klasse muss dann ein Skript setzen. Wer das Element danach entfernt, wartet das Ende der Animation ab.
Auf height: auto animieren
@supports (interpolate-size: allow-keywords) {
:root {
interpolate-size: allow-keywords;
}
}
.panel {
height: 0;
overflow: clip;
transition: height 300ms ease;
}
.panel.is-open {
height: auto;
}↗ Beispiel in neuem Tab öffnen
interpolate-size wird vererbt. An :root gilt es für die ganze Seite, an einem Teilbaum wie main nur dort.5 Browser, die es nicht können, springen einfach, und das ist so gewollt.5
Wer die Animation in allen Browsern braucht, kann Grid-Zeilen animieren (laut web-features 3.38.0 Baseline widely available seit 2022-10-27).
.panel {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 300ms ease;
}
.panel.is-open {
grid-template-rows: 1fr;
}
.panel > * {
overflow: hidden;
}↗ Beispiel in neuem Tab öffnen
Der Trick mit 0fr/1fr steht in keiner der ausgewerteten Quellen, ist aber weit verbreitet. In Chrome 152 getestet. Ob er in Firefox und Safari genauso läuft, ist offen, auch wenn die Baseline-Daten dafür sprechen.
Warum funktioniert das?
- Transitions starten normalerweise nicht, wenn ein Element zum ersten Mal Styles bekommt oder aus
display: nonekommt.@starting-styleliefert genau für diesen Moment Startwerte.3 - Es gibt drei Zustände: Start (
@starting-style), offen und Standard. Beim Schließen geht es zum Standardzustand, nicht zurück zum Start. Ein- und Ausblenden dürfen also unterschiedlich aussehen.3 displaykennt keine Zwischenwerte. Mitallow-discretespringt es beim Einblenden gleich am Anfang um, beim Ausblenden erst am Ende. So bleibt das Element während der Transition sichtbar.1overlaysorgt dafür, dass das Element erst am Ende der Transition aus dem Top Layer genommen wird.1interpolate-sizemuss man ausdrücklich einschalten, weil viele bestehende Stylesheets davon ausgehen, dassautonicht animiert wird.5
Fallstricke
@starting-stylevor der Regel für den offenen Zustand: Dann wird es überschrieben, und das Einblenden fehlt.3 Verschachtelt man es in die Regel, tritt das Problem nicht auf.displayfehlt in der Transition-Liste: Dann sieht man das Ausblenden nicht, das Element verschwindet sofort.1- Firefox blendet laut Baseline-Daten nicht aus, egal wie korrekt das CSS ist. Das Einblenden funktioniert.
- Firefox und
displayin Keyframes: Auch das gehört zum Feature „display animation“. Beim Keyframe-Beispiel bleibt das Element dort vermutlich unsichtbar, ist aber noch da (opacity: 0, weiter im Layout und klickbar). Das ist abgeleitet, nicht getestet. Wer sichergehen will, entfernt oder versteckt es nach demanimationend-Event per Skript. - Ohne
overlay(Firefox, Safari): Bei einfachen Animationen ist der Unterschied laut MDN „möglicherweise nicht bemerkbar“. Bei aufwendigeren kann das Element vor dem Ende der Transition aus dem Top Layer fallen.1 ::backdropund Nesting: Der Startzustand für::backdropmuss als eigener Block dastehen.1interpolate-sizefür die ganze Seite: Kann Stellen verändern, die bisher bewusst gesprungen sind. Dann besser auf einen Teilbaum beschränken.5- Tippfehler in der Chrome-Quelle: Dort steht einmal
interpolate-size: allow-sizes, richtig istallow-keywords. heightanimiert das Layout: Das ist teurer alstransformoderopacity.6 Bei kurzem Aufklappen meist vertretbar, bei vielen Elementen gleichzeitig sollte man es prüfen.- Bewegung: alles in
@media (prefers-reduced-motion: no-preference)packen, siehe Barrierearme Animationen. Übergänge der Deckkraft sind meist unproblematisch, Verschiebungen eher nicht.
Browsersupport
| Feature | Stand |
|---|---|
@starting-style | Baseline newly available seit 2024-08-06 (Chrome 117, Firefox 129, Safari 17.5) |
transition-behavior | Baseline newly available seit 2024-08-06 (Chrome 117, Firefox 129, Safari 17.4) |
display/content-visibility animieren | limited: Chrome 116/117, Safari 18, Firefox fehlt |
overlay | limited: nur Chromium ab 117 |
:popover-open | Baseline newly available seit 2024-04-16 |
:open | Baseline newly available seit 2026-05-11 |
interpolate-size, calc-size() | limited: nur Chromium ab 129 |
grid-template-rows animieren | Baseline widely available (seit 2022-10-27 newly) |
Quelle: web-features 3.38.0 (Baseline-Daten der W3C WebDX Community Group), lokal abgefragt am 2026-09-12.
Siehe auch
- CSS-Transitions und Keyframe-Animationen
- Top Layer mit popover und dialog
- Mobile-Menü ohne Checkbox-Hack bauen
- Akkordeon mit details bauen
- Barrierearme Animationen
Quellen
- Using the Popover API (MDN) — vollständiges Transition-Muster,
display/overlay, Backdrop - dialog-Element (MDN) — dasselbe Muster für
dialog - starting-style At-Regel (MDN) — Zweck, Reihenfolge, drei Zustände
- Four new CSS features for smooth entry and exit animations (Chrome for Developers) —
displayin Keyframes,transition-behavior - Animate to height auto (Chrome for Developers) —
interpolate-size,calc-size() - How to create high-performance CSS animations (web.dev) — Layout-Animationen sind teurer
Eigene Einordnung bzw. nicht aus den Quellen: der Trick mit 0fr/1fr, die Anpassung mit translate und die Media Query drumherum. Getestet nur in Chrome 152.
Footnotes
-
Using the Popover API (MDN), Abschnitte „Animating popovers“ und „Transitioning a popover“. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7
-
dialog-Element (MDN), Abschnitt „Animating dialogs“. ↩
-
Four new CSS features for smooth entry and exit animations (Chrome for Developers). ↩ ↩2