prefers-reduced-motion – Animationen, die niemandem übel werden
Manche Menschen bekommen von bewegten Oberflächen Schwindel oder Übelkeit. Ihr Betriebssystem weiß das längst – mit einer Media Query fragst du es einfach ab und lieferst dieselbe Seite ohne Bewegung aus.
Parallax, einfliegende Karten, sanft aufploppende Dialoge: Für die meisten sieht das gut aus. Für Menschen mit vestibulären Beschwerden ist es der Grund, eine Seite wieder zu schließen – bewegte Flächen können Schwindel und Übelkeit auslösen. Genau dafür gibt es in macOS, Windows, iOS und Android seit Jahren einen Schalter („Bewegung reduzieren"), und der Browser reicht ihn an dein CSS weiter.
Die eine Regel, die fast alles erledigt
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}Warum 0.01ms statt none? Weil animationend und transitionend dann trotzdem feuern. Skripte, die auf das Ende einer Animation warten, laufen also weiter – sie warten nur nicht mehr.
Besser: bewusst gestalten statt pauschal abschalten
Die Holzhammer-Regel ist ein guter Sicherheitsgurt. Schöner wird es, wenn du je Effekt entscheidest. Ein Wechsel von Bewegung zu Überblendung bleibt oft verständlich, ohne zu stören:
.karte {
transition: transform .3s ease, opacity .3s ease;
}
@media (prefers-reduced-motion: reduce) {
.karte {
transition: opacity .3s ease; /* nur noch Helligkeit, keine Bewegung */
transform: none;
}
}Umgekehrt geht es genauso: Animationen erst gar nicht anschalten, wenn niemand danach gefragt hat.
@media (prefers-reduced-motion: no-preference) {
.hero { animation: schweben 6s ease-in-out infinite; }
}Dasselbe in JavaScript
Wenn du mit requestAnimationFrame oder einer Bibliothek animierst, fragst du dieselbe Einstellung ab:
const ruhig = window.matchMedia('(prefers-reduced-motion: reduce)');
if (!ruhig.matches) starteAnimation();
// Reagiert auch, wenn jemand den Schalter im laufenden Betrieb umlegt
ruhig.addEventListener('change', e => e.matches ? stopAnimation() : starteAnimation());Fallstrick
prefers-reduced-motion ist keine Erlaubnis, Bewegung ganz aus der Gestaltung zu streichen – und auch keine Ausrede, sie unbegrenzt einzusetzen, solange die Abfrage existiert. Zwei Dinge sind trotz Abfrage heikel: Bewegung, die automatisch startet und nicht anhält (Karussells, Videoschleifen), und alles, was über den halben Bildschirm wandert. Gib beidem einen Stopp-Knopf.
Ich baue Websites so, dass sie auf allen Geräten und für alle Besucher ruhig funktionieren. Wenn du das für dein Projekt brauchst: bymw.de.
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.