---
title: "Content is invisible without scroll-driven-animation support (smooth/scroll-timeline-no-fallback)"
description: "smooth/scroll-timeline-no-fallback: `animation-timeline: view()/scroll()`, `view-timeline` and `animation-range` have no fallback path: a browser that…"
canonical: https://void-design.vercel.app/rules/smooth/scroll-timeline-no-fallback
lastModified: 2026-09-16
---

# Content is invisible without scroll-driven-animation support

`smooth/scroll-timeline-no-fallback` · severity **error** · category Smoothness · detected by `void smooth` and `void audit`

## Why it matters

`animation-timeline: view()/scroll()`, `view-timeline` and `animation-range` have no fallback path: a browser that doesn't implement them (Firefox, at every version through 151) evaluates the enclosing `@supports (animation-timeline: …)` block as false and drops every rule inside it — including the keyframes that would ever make the element visible. If the element's base rule (outside that block) sets `opacity: 0`, `visibility: hidden`, a collapsed scale/mask or an off-screen translate, the content isn't degraded, it's gone — permanently, not just until scrolled into view.

## How to fix it

Keyframes own the from-state: the resting cascade (everything outside `@supports (animation-timeline: …)`) must already be the finished, fully visible state, the way `@utility reveal` in packages/tokens/css/base.css does it — `opacity: 0` lives only inside `@keyframes`, never in the base rule. Move the hidden state into the keyframe referenced from inside the `@supports` block; see skills/motion/references/fallbacks.md.

## Example

```
/* wrong: base rule hides it unconditionally, only @supports undoes it */
.hero-sub { opacity: 0; }
@supports (animation-timeline: view()) {
  .hero-sub { animation: reveal linear both; animation-timeline: view(); }
}

/* right: resting state is already visible; the keyframe owns opacity: 0 */
@supports (animation-timeline: view()) {
  .hero-sub { animation: reveal linear both; animation-timeline: view(); }
}
@keyframes reveal { from { opacity: 0; translate: 0 0.75rem; } }
```

## References

- https://developer.mozilla.org/en-US/docs/Web/CSS/animation-timeline
- https://caniuse.com/mdn-css_properties_animation-timeline

## More smoothness rules

`void smooth` reports 13 rules in this category. How the page feels after it loads: dropped frames during a scripted scroll, long animation frames, INP of real clicks, shifts after input, animated layout properties, idle rAF loops and reduced-motion handling.

- `layout-shift-after-input` Layout shifts long after an interaction — [smooth/layout-shift-after-input](https://void-design.vercel.app/rules/smooth/layout-shift-after-input)
- `layout-shift-on-scroll` Layout shifts while scrolling — [smooth/layout-shift-on-scroll](https://void-design.vercel.app/rules/smooth/layout-shift-on-scroll)
- `animate-layout-property` Animation of a layout-triggering property — [smooth/animate-layout-property](https://void-design.vercel.app/rules/smooth/animate-layout-property)
- `transition-all` transition: all — [smooth/transition-all](https://void-design.vercel.app/rules/smooth/transition-all)
- `will-change-overuse` will-change is applied too broadly — [smooth/will-change-overuse](https://void-design.vercel.app/rules/smooth/will-change-overuse)
- `reduced-motion-ignored` Animations keep running with prefers-reduced-motion — [smooth/reduced-motion-ignored](https://void-design.vercel.app/rules/smooth/reduced-motion-ignored)
- `raf-loop-idle` requestAnimationFrame loop runs while idle — [smooth/raf-loop-idle](https://void-design.vercel.app/rules/smooth/raf-loop-idle)
- `multiple-webgl-contexts` Multiple WebGL contexts — [smooth/multiple-webgl-contexts](https://void-design.vercel.app/rules/smooth/multiple-webgl-contexts)

Detected by `void smooth` and `void audit`. Explain it in a terminal: `void rules smooth/scroll-timeline-no-fallback`
