Reduced motion, not zero
Keep fades under reduced motion, drop movement, gate hover.
Emil Kowalski@emilkowalskiFrom emil-design-eng and his animations.dev lessons.
Kill it all, or keep the fade
Open the dialog, press Archive, then switch modes.
animation: noneBad
- Q3 report
- Invoices, Sep
- Payroll
Archive “Q3 report”?
You can restore it later.
transition: noneKeep the fadeGood
- Q3 report
- Invoices, Sep
- Payroll
Archive “Q3 report”?
You can restore it later.
transition: opacity 200ms ease.toast {
transition: transform 250ms var(--ease-out), opacity 200ms ease;
}
.toast[data-open="false"] { opacity: 0; transform: translateY(100%); }
@media (prefers-reduced-motion: reduce) {
.toast { transition: opacity 200ms ease; }
.toast[data-open="false"] { transform: none; }
}
// JS motion never sees the media query.
const reduce = matchMedia("(prefers-reduced-motion: reduce)").matches;
Tap a card on touch
Tap a card in each pane, then tap another.
Ungated :hoverBad
.card:hoverstuck: –Gated hoverGood
@media (hover: hover)stuck: –.card { transition: transform 200ms var(--ease-out), box-shadow 200ms ease; }
.card:active { transform: scale(0.97); }
@media (hover: hover) and (pointer: fine) {
.card:hover { transform: translateY(-3px); box-shadow: var(--shadow); }
}
Why it matters, pros and cons
Why it matters
Movement causes nausea; a fade does not. Ungated hover sticks after a tap.
Pros
- Everyone still sees state changes.
- One media query, no second code path.
- No stuck hover on phones.
Cons
- JS springs ignore CSS; check
matchMediatoo. - Keep motion that is content, like spinners.
- Hover-only actions need another way in on touch.