<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/keyframes-and-prefers-reduced-motion-done-right-ykjefv3ov" -->

---
title: @keyframes and prefers-reduced-motion done right | daily.dev
description: A practical guide to building CSS keyframe animations correctly, covering the fade-up pattern using only transform and opacity for GPU-friendly performance....
canonical: https://daily.dev/posts/keyframes-and-prefers-reduced-motion-done-right-ykjefv3ov
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: @keyframes and prefers-reduced-motion done right | daily.dev
og:description: A practical guide to building CSS keyframe animations correctly, covering the fade-up pattern using only transform and opacity for GPU-friendly performance....
og:url: https://daily.dev/posts/keyframes-and-prefers-reduced-motion-done-right-ykjefv3ov
og:image: https://api.daily.dev/og/posts/YKjeFV3ov.png
og:image:alt: @keyframes and prefers-reduced-motion done right
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# @keyframes and prefers-reduced-motion done right

**[Flavio Copes](https://daily.dev/sources/flaviocopes)** · 5 min read · 2 upvotes · 1 comments

## Summary

A practical guide to building CSS keyframe animations correctly, covering the fade-up pattern using only transform and opacity for GPU-friendly performance. Explains fill modes (why 'both' is almost always what you want), per-keyframe timing functions for realistic motion like bouncing, and how to properly implement prefers-reduced-motion — including the nuance that 'reduced' means replacing motion with opacity fades rather than simply disabling all animation. Also warns about the pitfall of animation: none breaking elements that rely on fill-mode: forwards for their final visible state.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://flaviocopes.com/css-keyframes-reduced-motion>

## Questions this post answers

### Why does my CSS animation with a delay show the element fully visible before it starts animating?

Without a fill mode, keyframe styles only apply while the animation is actively running, so during a delay the element falls back to its normal CSS styles. Adding animation-fill-mode: both (or backwards) applies the first keyframe's styles during the delay, and forwards keeps the last keyframe's styles after it ends, preventing the visible flash before the animation starts.

_daily.dev surfaces practical CSS fixes like this for developers debugging animation timing quirks._

### How do I make a CSS animation respect prefers-reduced-motion without just disabling it entirely?

Instead of removing the animation completely, swap movement-based keyframes for an opacity-only version inside an @media (prefers-reduced-motion: reduce) block, since the setting targets motion like sliding, zooming, and spinning rather than all visual change. Infinite decorative loops should stop and scroll-behavior: smooth should also be disabled under this same query, though loading spinners can remain.

_developers building accessible animations can find these prefers-reduced-motion patterns on daily.dev._

### Why does an element disappear permanently after prefers-reduced-motion sets its animation to none?

Setting animation: none removes the animation but not the fill-mode's applied state, so an element relying on fill-mode: forwards to reach its final visible state reverts to its initial CSS styles instead, such as opacity: 0. If that's the element's base style, it stays invisible forever, so testing with reduced motion enabled in OS settings or browser DevTools is essential.

_daily.dev helps developers catch accessibility edge cases like this before they ship broken animations._

## Community discussion

Top comments from developers on daily.dev.

**@agustinbarrientos** · 0 upvotes

> Entrance animations belong under `no-preference`

## Similar posts on daily.dev

- [Combining Scroll-Driven Animations with @starting-style](https://daily.dev/posts/combining-scroll-driven-animations-with-starting-style-zeo2xs4ay) · Bram.us · 30 upvotes · 0 comments
- [Scroll-driven CSS animations](https://daily.dev/posts/scroll-driven-css-animations-uw6plcluy) · Flavio Copes · 142 upvotes · 6 comments
- [Keyframes Tokens: Standardizing Animation Across Projects — Smashing Magazine](https://daily.dev/posts/keyframes-tokens-standardizing-animation-across-projects-smashing-magazine-iy4yrcwe4) · Smashing Magazine · 3 upvotes · 0 comments
- [How to create awesome staggered animations in CSS](https://daily.dev/posts/how-to-create-awesome-staggered-animations-in-css-k09m57ftt) · LogRocket · 52 upvotes · 2 comments

---

Tags: [#css](https://daily.dev/tags/css), [#accessibility](https://daily.dev/tags/accessibility)

[View this post on daily.dev](https://daily.dev/posts/keyframes-and-prefers-reduced-motion-done-right-ykjefv3ov)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"@keyframes and prefers-reduced-motion done right","url":"https://daily.dev/posts/keyframes-and-prefers-reduced-motion-done-right-ykjefv3ov","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/keyframes-and-prefers-reduced-motion-done-right-ykjefv3ov"},"datePublished":"2026-08-05T07:38:04.882Z","dateModified":"2026-09-13T21:45:04.763Z","description":"A practical guide to building CSS keyframe animations correctly, covering the fade-up pattern using only transform and opacity for GPU-friendly performance....","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/7219243bb6b6113ecc90bd933965a4ef?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/7219243bb6b6113ecc90bd933965a4ef?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Flavio Copes","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Flavio Copes","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/1f2dbd275797411bbd37bfdaaf0951a9","url":"https://daily.dev/sources/flaviocopes"},"commentCount":1,"discussionUrl":"https://daily.dev/posts/keyframes-and-prefers-reduced-motion-done-right-ykjefv3ov","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":2},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":1}],"keywords":"css,accessibility","timeRequired":"PT5M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Flavio Copes","item":"https://daily.dev/sources/flaviocopes"},{"@type":"ListItem","position":3,"name":"@keyframes and prefers-reduced-motion done right"}]}
{"@context":"https://schema.org","@type":"WebPage","@id":"https://daily.dev/posts/keyframes-and-prefers-reduced-motion-done-right-ykjefv3ov","comment":[{"@type":"Comment","text":"Entrance animations belong under no-preference","datePublished":"2026-08-13T20:43:25.786Z","url":"https://daily.dev/posts/YKjeFV3ov#c-NobI0VQzs","author":{"@type":"Person","name":"Agustin Barrientos","url":"https://daily.dev/agustinbarrientos","image":"https://media.daily.dev/image/upload/s--5ayxQnqn--/f_auto/v1788281802/avatars/avatar_wQYYVe5Tbj0NJ7C7qPoa8?_a=BAMAMicg0"}}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/keyframes-and-prefers-reduced-motion-done-right-ykjefv3ov#faq","mainEntity":[{"@type":"Question","name":"Why does my CSS animation with a delay show the element fully visible before it starts animating?","acceptedAnswer":{"@type":"Answer","text":"Without a fill mode, keyframe styles only apply while the animation is actively running, so during a delay the element falls back to its normal CSS styles. Adding animation-fill-mode: both (or backwards) applies the first keyframe's styles during the delay, and forwards keeps the last keyframe's styles after it ends, preventing the visible flash before the animation starts. daily.dev surfaces practical CSS fixes like this for developers debugging animation timing quirks."}},{"@type":"Question","name":"How do I make a CSS animation respect prefers-reduced-motion without just disabling it entirely?","acceptedAnswer":{"@type":"Answer","text":"Instead of removing the animation completely, swap movement-based keyframes for an opacity-only version inside an @media (prefers-reduced-motion: reduce) block, since the setting targets motion like sliding, zooming, and spinning rather than all visual change. Infinite decorative loops should stop and scroll-behavior: smooth should also be disabled under this same query, though loading spinners can remain. developers building accessible animations can find these prefers-reduced-motion patterns on daily.dev."}},{"@type":"Question","name":"Why does an element disappear permanently after prefers-reduced-motion sets its animation to none?","acceptedAnswer":{"@type":"Answer","text":"Setting animation: none removes the animation but not the fill-mode's applied state, so an element relying on fill-mode: forwards to reach its final visible state reverts to its initial CSS styles instead, such as opacity: 0. If that's the element's base style, it stays invisible forever, so testing with reduced motion enabled in OS settings or browser DevTools is essential. daily.dev helps developers catch accessibility edge cases like this before they ship broken animations."}}]}
```

