@starting-style: Entry-Animationen ohne JavaScript mit Tailwind
AI generated
tw
Tailwind CSS · CSS Animationen · @starting-style · Modal
@starting-style
Entry-Animationen ganz ohne JavaScript

Ein Modal, das beim Öffnen einfach da ist, wirkt abrupt. Bisher war ein sanftes Einblenden beim ersten Rendern eine Domäne von JavaScript-Bibliotheken, die den Mount-Zeitpunkt abpassen mussten. Mit der CSS-Regel @starting-style übernimmt der Browser diese Aufgabe nativ, kombiniert mit den gewohnten Tailwind-Transition-Utilities.

15 Min. Lesezeit @starting-style · transition CSS Entry-Animationen ohne JS

1. Das Problem mit Animationen beim ersten Rendern

CSS-Transitions animieren zuverlässig den Wechsel zwischen zwei Zuständen, etwa wenn eine Klasse per Klick hinzugefügt oder entfernt wird und sich dabei Opazität oder Transformation ändern. Genau das versagt aber beim allerersten Erscheinen eines Elements, weil eine Transition per Definition einen Ausgangswert und einen Zielwert braucht, das Element beim ersten Rendern aber direkt mit seinem finalen Zustand im DOM erscheint, ohne dass der Browser einen vorherigen Wert kennt, von dem aus animiert werden könnte.

Bisher wurde dieses Problem mit JavaScript umgangen, indem ein Element zunächst mit dem Startzustand ins DOM eingefügt wird, im nächsten Animationsframe die Zielklasse hinzugefügt wird und der Browser dazwischen die Transition auslöst. Diese Technik funktioniert, erfordert aber sorgfältiges Timing mit requestAnimationFrame, um Race Conditions zu vermeiden, bei denen der Browser beide Zustände im selben Frame zusammenfasst und die Animation komplett überspringt.

2. @starting-style Grundlagen

Die CSS-Regel @starting-style löst genau dieses Problem, indem sie explizit definiert, von welchem Zustand aus ein Element animieren soll, wenn es zum ersten Mal einen animierbaren Wert erhält, sei es beim ersten Einfügen ins DOM oder beim Wechsel von display: none zu einem sichtbaren Display-Wert. Innerhalb des Blocks werden dieselben Eigenschaften wie im Zielzustand deklariert, nur mit den Ausgangswerten, etwa opacity: 0 und einer verschobenen Transformation, während die eigentliche Zielregel die Endwerte trägt.

Der Browser erkennt automatisch, dass eine Transition definiert ist, entnimmt der starting-style-Regel den Ausgangswert und animiert von dort zum in der normalen Regel definierten Zielwert, sobald das Element sichtbar wird. Diese Logik funktioniert vollständig deklarativ und ohne jedes JavaScript, weil der Browser den kritischen ersten Frame selbst verwaltet, statt dass ein Skript den Timing-Trick mit zwei aufeinanderfolgenden Klassenzuweisungen nachbauen muss.

Ein Modal-Dialog ist der klassische Anwendungsfall für @starting-style, weil es typischerweise mit dem HTML-dialog-Element oder über bedingtes Rendering in den DOM eingefügt wird und beim Öffnen sanft einblenden statt abrupt erscheinen soll. In Tailwind lässt sich dafür eine kleine, wiederverwendbare Utility-Kombination definieren, die opacity und scale über eine Transition animiert und die Ausgangswerte über @starting-style festlegt.

Im folgenden Beispiel öffnet sich ein Modal aus einer leicht verkleinerten, transparenten Ausgangsposition heraus zur vollen Größe und Deckkraft, gesteuert allein über CSS. Wichtig ist die Kombination mit transition-discrete beziehungsweise dem CSS-Wert allow-discrete, damit auch der Sprung von display: none zu einem sichtbaren Wert sauber animiert wird, statt dass der Browser die Sichtbarkeitsänderung sofort ohne Übergang vollzieht.


<dialog class="m-auto rounded-xl bg-white p-6 shadow-xl backdrop:bg-black/50
               opacity-100 scale-100 transition-all duration-300
               starting:opacity-0 starting:scale-95
               open:opacity-100 open:scale-100
               [transition-behavior:allow-discrete]">
  <h2 class="text-lg font-semibold text-gray-900">Bestellung bestätigen</h2>
  <p class="mt-2 text-sm text-gray-600">
    Möchtest du die Bestellung wirklich abschicken?
  </p>
  <div class="mt-4 flex justify-end gap-2">
    <button class="rounded-md border px-3 py-1.5 text-sm">Abbrechen</button>
    <button class="rounded-md bg-blue-600 px-3 py-1.5 text-sm text-white">Bestätigen</button>
  </div>
</dialog>

4. Kombination mit Tailwind Transition Utilities

Tailwind stellt bereits ein ausgereiftes Set an Transition-Utilities bereit, etwa transition-all, duration-300 oder ease-out, die sich unverändert mit @starting-style kombinieren lassen, weil die Ausgangswerte in der starting-style-Regel lediglich definieren, wovon aus animiert wird, während Dauer, Timing-Funktion und die zu animierenden Eigenschaften weiterhin über die normalen Transition-Utilities gesteuert werden. Es ist deshalb keine separate Animations-API nötig, das bestehende Transition-System von Tailwind bleibt vollständig gültig.

In neueren Tailwind-Versionen mit CSS-Variablen-basiertem Theming lässt sich außerdem ein eigener Utility-Präfix wie starting: als Variante definieren, der intern die @starting-style-Regel erzeugt, sodass Entwickler den Ausgangszustand direkt als Utility-Klasse im Markup notieren können, statt eine separate CSS-Datei mit der @starting-style-Regel von Hand zu pflegen. Das hält den deklarativen Charakter von Utility-First auch für diese neue CSS-Funktion konsequent bei.

5. Zusammenspiel mit display: none und allow-discrete

Ein Sonderfall betrifft Eigenschaften, die selbst nicht kontinuierlich animierbar sind, allen voran display, das entweder none oder ein sichtbarer Wert ist, ohne Zwischenwerte. Normalerweise überspringt der Browser hier jede Transition, weil ein diskreter Eigenschaftswechsel sofort erfolgt, ohne dass es eine sinnvolle Zwischenstufe zwischen none und block gäbe, wodurch eine gleichzeitig laufende Opacity-Transition ins Leere liefe, sobald das Element komplett aus dem Layout verschwindet.

Der CSS-Wert transition-behavior: allow-discrete löst dieses Problem, indem er dem Browser erlaubt, den display-Wechsel bis zum Ende der übrigen Transition hinauszuzögern, statt ihn sofort auszuführen. In Kombination mit @starting-style ermöglicht das ein vollständig animiertes Ein- und Ausblenden eines Elements inklusive des display-Wechsels, was vorher praktisch immer JavaScript erforderte, weil reines CSS die Reihenfolge von display-Wechsel und Opacity-Animation nicht koordinieren konnte.

6. Vergleich zu bisherigen JavaScript-basierten Animate-on-Mount-Lösungen

Bibliotheken wie Framer Motion oder Alpine.js-Transition-Direktiven lösen das Mount-Animation-Problem bislang, indem sie den Startzustand per Skript setzen, im nächsten Frame den Zielzustand anwenden und dazwischen die eigentliche CSS-Transition auslösen. Das funktioniert zuverlässig, kostet aber zusätzliches JavaScript im Bundle, zusätzliche Rechenzeit für das Timing der Frames und macht die Animation von der korrekten Ausführung des Skripts abhängig, was bei langsamen Geräten oder blockiertem Hauptthread zu ausbleibenden Animationen führen kann.

@starting-style verlagert diese Logik vollständig in die CSS-Rendering-Pipeline des Browsers, wodurch die Animation garantiert im richtigen Frame startet, unabhängig davon, wie ausgelastet der JavaScript-Hauptthread gerade ist. Für Alpine.js-lastige Hyvä-Projekte bedeutet das, dass die x-transition-Direktive für viele einfache Mount-Animationen künftig durch reines CSS mit @starting-style ersetzt werden kann, was den JavaScript-Payload reduziert, ohne auf das visuelle Ergebnis verzichten zu müssen.

7. Exit-Animationen und die Grenzen von @starting-style

@starting-style behandelt ausschließlich das Erscheinen eines Elements, nicht sein Verschwinden, weil die Regel per Definition nur beim Übergang von einem nicht gerenderten oder nicht sichtbaren Zustand zu einem sichtbaren Zustand greift. Für das Ausblenden eines Elements, etwa ein Modal, das beim Schließen sanft verblasst, reicht eine normale Transition auf die Zielwerte in Kombination mit allow-discrete für den display-Wechsel am Ende, ganz ohne eigene @starting-style-Regel für diese Richtung.

Eine echte Grenze ergibt sich bei Elementen, die zwischen mehreren sichtbaren Zuständen wechseln, ohne jemals komplett aus dem DOM zu verschwinden, etwa ein Akkordeon, das zwischen offen und geschlossen wechselt, aber nie ganz unsichtbar wird, weil hier @starting-style nicht greift und weiterhin normale Transitions zwischen zwei definierten Zuständen die richtige Wahl bleiben. @starting-style ist gezielt für das erste Erscheinen gedacht, nicht als generelle Ersatzlösung für jede Art von Zustandsübergang.

8. Browser-Support-Stand von @starting-style

Chrome und Edge unterstützen @starting-style bereits seit Version 117 auf Basis von Blink, Safari hat die Unterstützung mit Version 17.5 nachgezogen, und Firefox hat die Implementierung ebenfalls umgesetzt, sodass die Regel inzwischen in allen drei großen Engine-Familien ankommt. Wie bei anderen neueren CSS-Features gilt auch hier, dass der genaue Support-Stand vor dem produktiven Einsatz auf einer aktuellen Referenzseite geprüft werden sollte, weil sich Versionsnummern und Rollout-Zeitpunkte je nach Zielgruppe des Projekts unterschiedlich stark auswirken.

Da @starting-style rein additiv wirkt und beim Fehlen einfach ignoriert wird, führt fehlende Unterstützung nicht zu einem kaputten Layout, sondern lediglich dazu, dass das Element ohne Einblend-Animation direkt im Zielzustand erscheint, was dem bisherigen Standardverhalten entspricht. Das macht @starting-style zu einem risikoarmen progressiven Enhancement, das sich schon vor vollständiger Browserabdeckung produktiv einsetzen lässt.

9. Best Practices für den produktiven Einsatz

Eine wichtige Praxisregel ist, die @starting-style-Werte bewusst dezent zu wählen, etwa eine leichte Skalierung von 0.95 auf 1 statt einer dramatischen Skalierung von 0 auf 1, weil zu starke Einblend-Effekte schnell unruhig wirken und die Wahrnehmung des eigentlichen Inhalts stören können. Kurze Transition-Dauern zwischen 150 und 300 Millisekunden mit einer sanften Easing-Funktion wie ease-out liefern in der Praxis die besten Ergebnisse für Modals, Tooltips und Dropdown-Menüs.

Außerdem sollte @starting-style nicht wahllos auf jedes neu gerenderte Element angewendet werden, weil zu viele gleichzeitige Einblend-Animationen, etwa in einer langen Liste neu geladener Karten, optisch überladen wirken und die Nutzerführung eher stören als unterstützen. Sinnvoll eingesetzt ist die Regel dort, wo ein einzelnes, klar abgegrenztes Element wie ein Modal, ein Toast oder ein Dropdown-Menü Aufmerksamkeit verdient und ein sanftes Erscheinen die Wahrnehmung des Kontexts unterstützt, statt sie zu zerstreuen.

Aspekt Ohne @starting-style Mit @starting-style Praxisrelevanz
Ausgangswert beim Mount Nicht definierbar, Element erscheint sofort im Zielzustand Explizit über den @starting-style-Block definierbar Ermöglicht Fade-in ohne JavaScript
display: none Übergang Springt sofort, keine Transition möglich Mit allow-discrete verzögert bis Transitionsende Nötig für Modals und Dropdowns
JavaScript-Bedarf Skript nötig für Zwei-Frame-Trick Kein Skript nötig, rein deklarativ Reduziert Bundle-Größe und Timing-Risiken
Browser-Support Nicht relevant, da kein neues Feature Chrome/Edge, Safari 17.5+, aktuelles Firefox Progressive Enhancement ohne Fallback-Code

Mironsoft

Tailwind-CSS-Architektur, Design-Systeme und Performance

Tailwind-Frontends, die trotz tausender Utility-Klassen wartbar bleiben?

Wir prüfen bestehende Tailwind-Projekte auf aufgeblähte Klassenlisten, inkonsistente Design-Tokens und ungenutzte CSS-Reste und bauen daraus ein Design-System, das sich sauber skaliert statt mit jeder Komponente unübersichtlicher zu werden.

Design-System-Review

Tokens, Spacing-Skala und Komponentenkonsistenz auf Wartbarkeit prüfen.

Performance-Optimierung

CSS-Bundle-Größe, Purge-Konfiguration und Ladezeiten systematisch reduzieren.

Component-Architektur

Wiederverwendbare, gut strukturierte Komponenten statt Klassenlisten-Wildwuchs aufbauen.

10. Zusammenfassung

@starting-style: Das Wichtigste auf einen Blick

@starting-style

Definiert den Ausgangswert eines Elements beim ersten Rendern für eine Transition.

allow-discrete

Verzögert den display-Wechsel bis zum Ende der Transition, nötig für echtes Ein-/Ausblenden.

Kein JavaScript

Ersetzt den Zwei-Frame-Trick mit requestAnimationFrame durch reines, deklaratives CSS.

Einsatzort

Ideal für Modals, Toasts und Dropdowns, nicht für generelle Zustandsübergänge.

11. FAQ: @starting-style: Das Wichtigste auf einen Blick

1Was macht die CSS-Regel @starting-style genau?
Sie definiert den Ausgangswert eines Elements für eine Transition, wenn dieses zum ersten Mal in den DOM eingefügt wird oder von display none zu einem sichtbaren Wert wechselt, sodass der Browser von diesem Wert aus zum Zielzustand animieren kann.
2Brauche ich für Modal-Animationen noch JavaScript?
Für das reine Einblenden beim Öffnen nicht mehr, @starting-style in Kombination mit allow-discrete übernimmt diese Aufgabe vollständig deklarativ über CSS, ohne dass ein Skript den Zeitpunkt der Klassenzuweisung koordinieren muss.
3Wofür wird allow-discrete konkret benötigt?
Eigenschaften wie display sind nicht kontinuierlich animierbar, allow-discrete verzögert den Sprung von none zu einem sichtbaren Wert bis zum Ende der übrigen Transition, sodass Opacity- und Transform-Animationen nicht ins Leere laufen.
4Kann ich @starting-style für Exit-Animationen nutzen?
Nein, die Regel behandelt ausschließlich das Erscheinen eines Elements, für das Ausblenden reicht eine normale Transition auf die Zielwerte in Kombination mit allow-discrete für den display-Wechsel am Ende.
5Welche Browser unterstützen @starting-style bereits?
Chrome und Edge seit Version 117, Safari seit Version 17.5 und aktuelle Firefox-Versionen unterstützen die Regel, der genaue Stand sollte vor dem produktiven Einsatz aber auf einer aktuellen Referenzseite geprüft werden.
6Was passiert in Browsern ohne Unterstützung?
Die @starting-style-Regel wird einfach ignoriert, das Element erscheint sofort im Zielzustand ohne Einblend-Animation, was dem bisherigen Standardverhalten entspricht und zu keinem kaputten Layout führt.
7Lässt sich @starting-style mit normalen Tailwind Transition Klassen kombinieren?
Ja, Dauer, Timing-Funktion und die zu animierenden Eigenschaften werden weiterhin über die gewohnten Tailwind Transition Utilities gesteuert, @starting-style definiert lediglich den zusätzlichen Ausgangswert.
8Ersetzt @starting-style die x-transition Direktive in Alpine.js?
Für viele einfache Mount-Animationen ja, weil die Logik komplett in CSS abgebildet werden kann, komplexere choreografierte Übergänge mit mehreren gestaffelten Elementen bleiben aber weiterhin eine Domäne von Alpine.js oder JavaScript.
9Sollte ich @starting-style auf jedes neu gerenderte Element anwenden?
Nein, zu viele gleichzeitige Einblend-Animationen wirken schnell überladen, die Regel eignet sich besonders für einzelne, klar abgegrenzte Elemente wie Modals, Toasts oder Dropdown-Menüs.
10Welche Transition-Dauer eignet sich für Entry-Animationen mit @starting-style?
In der Praxis liefern kurze Dauern zwischen 150 und 300 Millisekunden mit einer sanften Easing-Funktion wie ease-out die besten Ergebnisse, ohne dass die Animation als träge oder aufdringlich wahrgenommen wird.