SuperAPI AI Directory

css-scroll-driven-native

Frontend & websites

View repository

Updated 10-16 01:13

About this skill

Skill: css-scroll-driven-native

Scroll effects with public CSS spec — animation-timeline: scroll() / view() + animation-range — not a single line of JavaScript.

WHAT

Purpose and context

  • Purpose: add scroll effects (progress bar, reveal-on-view, subtle parallax) with pure CSS animation-timeline as progressive enhancement — browsers without support or with reduce-motion enabled still see fully static content.
  • Trigger (when to use): the user says "css scroll driven animation", "animation-timeline", "scroll animation without JS", "pure CSS page scrolling", "scroll-driven CSS", or /css-scroll-driven-native; or wants to add 0-JS "smoothness" on top of scroll-effects. Choose per the table "When to choose this vs scroll-effects" (Reference section).
  • Non-goals: not the primary mechanism when you need reliable multi-browser production behavior now (that's scroll-effects, JS IntersectionObserver); do not write JS; do not hardcode browser version numbers.

Mental model

timeline (scroll(root) | view()) + animation-range → keyframes; toàn bộ nằm trong @supports → @media not (prefers-reduced-motion: reduce); ngoài hai khối đó = trạng thái tĩnh hiện đủ nội dung.

Input and output contract

Field Required? Meaning
In needed mechanism (progress / reveal / parallax) Yes choose 1–3 of the 3 mechanisms
In design.md of the project No if present, adjust color/spacing tokens to it
Out CSS (+ minimal markup) adapted from assets/demo.css / assets/demo.html Yes all animations in @supports + reduce-motion guard
Out verify evidence Yes 0%/50%/100% scroll screenshots + reduce-motion screenshot via playwright-verify

Rules and capabilities

Rules (MANDATORY):

  • RULE-01 (MUST): Always have a fallback @supports: every animation lives inside @supports (animation-timeline: scroll()) or @supports (animation-timeline: view()). OUTSIDE the @supports block is the default STATIC state — the element shows full content, with no animation, NEVER hidden/opacity:0 as the default. If writing opacity:0 for a reveal effect, that value may ONLY be set inside @supports, not in the base rule.
  • RULE-02 (MUST): prefers-reduced-motion: wrap the animation in @media not (prefers-reduced-motion: reduce) (nested inside @supports, see demo.css). When the user enables reduce-motion → every element returns to the static state exactly like a browser without support.
  • RULE-03 (MUST): Do NOT hardcode version numbers in code or comments — check caniuse.com/css-scroll-driven-animations each actual use (supporting table in the Reference section).
  • RULE-04 (MUST): animation-duration must still be declared (1ms or any value >0) even though the actual duration is controlled by the timeline — some engines require this value to trigger the animation.
  • RULE-05 (MUST): Verify with playwright-verify on the Chromium browser (CDP supports scroll-driven animations by default): capture screenshots at 0%/50%/100% scroll, assert the progress bar/scaleX and card opacity change according to scroll position; at the same time capture additional ones with prefers-reduced-motion: reduce emulation (Playwright emulateMedia) to assert NO animation but the content still shows fully.
  • Capabilities: read asset skill + design.md; write CSS/HTML into the target project; run the headless Chromium browser with media emulation.

Failure boundaries

  • Requirement "must run reliably on every browser immediately" → clarify/switch scroll-effects as the base, this skill only overrides.
  • Content is hidden when @supports is wrong or reduce-motion is on → failed (violates RULE-01/02), fix before delivery.
  • 0%/50%/100% images are identical on Chromium → failed (animation not triggered, usually missing animation-duration).

HOW

Main workflow

Step Type Inputs Action Outputs/exit Failure/next
W01 judgment requirement + browser target Choose this skill or scroll-effects according to the comparison table; check caniuse decision need multi-browser reliability → B01
W02 deterministic assets/demo.* Read assets/demo.html + assets/demo.css, take the needed mechanism snippet —
W03 effect snippet, design.md ADAPT color/spacing tokens; put the animation in @supports + reduce-motion CSS in the project —
W04 deterministic page already attached Verify RULE-05: capture 0/50/100% + emulateMedia reduce images + assert assert red → fix, repeat W04

Branches

ID Kind Guard Behavior Skip / failure Rejoin
B01 user_optional need reliable multi-browser support but still want smoother 0-JS combine: scroll-effects as the base, this skill overrides when the browser supports it only need Chromium → skip W02

Validation and stopping

Deterministic assert at W04 (scaleX/opacity change with scroll position; reduce-motion has no animation, content is sufficient). Needs eyes: smoothness/design fit. Stop when both image types pass; 2 rounds still red → report to user with images.

Examples

  • Positive: "add a reading progress bar with pure CSS" → .csdn-progress with animation-timeline: scroll(root) in @supports + reduce-motion guard → images at 0%/50%/100% for scaleX 0 → ~0.5 → 1.
  • Boundary/failure: reveal card placed opacity:0 in the base rule → on Firefox, which does not yet support it, the card disappears → violates RULE-01, move opacity:0 into @supports then verify again with reduce-motion.

Reference — When to choose this vs scroll-effects

css-scroll-driven-native (this skill) scroll-effects
JS 0 lines IntersectionObserver + rAF
Browser support Chrome/Edge good; Firefox/Safari rolling out, may not be enabled by default All modern browsers
When to use Progressive enhancement — adds "smoothness" for browsers that already support it, NOT the main mechanism if it needs to run reliably in multi-browser production right away The main mechanism when it needs to run reliably right away, without waiting for rollout

Recommendation: use this skill inside @supports (animation-timeline: scroll()), static fallback (no animation, no hidden content) — or combine both: scroll-effects as the base, this skill overrides when the browser supports it.

⚠️ Check real support before using in production at caniuse.com/css-scroll-driven-animations — don't trust the hard numbers written in this document, the API is rolling out fast and the support table may have changed.

Current browser support table (recorded at the time of writing the skill, 2026-09 — may have changed, always check yourself):

Browser animation-timeline: scroll() animation-timeline: view()
Chrome / Edge Yes Yes
Firefox Behind flag / rolling out Behind flag / rolling out
Safari Rolling out, not yet stable Rolling out, not yet stable

Reference — Assets

assets/demo.html + assets/demo.css — 3 demo mechanisms, read then ADAPT (change color/spacing tokens according to the project's design.md if any). Open directly in a supported browser (recent Chrome/Edge) to see it actually run; unsupported browsers will automatically fall back to the static state.

Reference — 3 mechanisms (minimal markup + mechanism)

1. Progress bar based on page scroll

A bar fixed at the top, its length following the % of the whole page scrolled.

<div class="csdn-progress"></div>
.csdn-progress {
  animation-name: progress-scale;
  animation-duration: 1ms; /* giá trị bất kỳ để kích hoạt engine — thời lượng thật do timeline điều khiển */
  animation-timeline: scroll(root);
  animation-fill-mode: both;
}
@keyframes progress-scale {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

scroll(root) tied to the scroll progress of the whole page — no need for a scroll listener, no need to compute scrollY / scrollHeight by hand.

2. Reveal-on-view when an element enters the viewport

<article class="csdn-card">…</article>
.csdn-card {
  animation-name: reveal-in;
  animation-timeline: view();
  animation-range: entry 0% cover 40%;
  animation-fill-mode: both;
}

view() is the individual timeline of EACH element based on its position in its own viewport — completely replacing IntersectionObserver. animation-range: entry 0% cover 40% means the animation runs from when the element starts entering the viewport (entry 0%) until it has "covered" 40% of the distance through the viewport.

3. Light parallax using view()

Background and content use different animation-range values on the same timeline view() → creating the feeling of layers drifting at different speeds, without needing to manually calculate scrollY .

.parallax-bg      { animation-timeline: view(); animation-range: cover 0% cover 100%; }   /* trôi chậm, suốt cả quãng */
.parallax-content { animation-timeline: view(); animation-range: entry 0% cover 35%; }    /* chạy nhanh, chỉ đoạn đầu */

See assets/demo.css section 3 for the full keyframes.

Origin

  • In addition to skills/scroll-effects (JS-based, runs in all browsers), a 0-JS option for browsers that already support it — the two skills do NOT replace each other, use them according to the "When to choose this one" table above.