Prompt and use cases
The goal is to tie animation progress to scrolling without running a JavaScript handler on every scroll event. CSS scroll-driven animations expose scroll-progress and view-progress timelines through animation-timeline, scroll(), and view(). The implementation still needs a fallback, reduced-motion behavior, containment decisions, and a clear browser-support policy.
What the interviewer evaluates
- Whether you distinguish a scroll container’s progress from an element’s visibility progress.
- Whether you place the
animation-timelinedeclaration after theanimationshorthand. - Whether you choose transform and opacity before layout-triggering properties.
- Whether you provide a feature query or non-animated baseline.
- Whether you respect
prefers-reduced-motionand preserve readable content without motion. - Whether you measure long pages, nested scrollers, sticky elements, and mobile performance.
Clarifications before answering
- Is progress tied to the page, a nested scroll container, or an element entering the viewport?
- Must the animation communicate state, or is it decorative?
- Which browsers and embedded webviews are in scope?
- What should users with reduced motion see?
- Can the design degrade to a static progress bar or reveal?
- Are there sticky, transformed, or virtualized ancestors that change the containing block?
30-second answer framework
“I would use a scroll-progress timeline for page progress and a view-progress timeline for cards entering the scroller. I would define a static readable baseline, then enhance inside a feature query with animation-timeline declared after the animation shorthand. I would animate transform or opacity, test nested scrollers and sticky elements, and disable nonessential motion under prefers-reduced-motion. I would verify support, frame cost, keyboard access, and content comprehension before shipping.”
Step-by-step deep answer
Step 1: Select the timeline.
Use a scroll-progress timeline when the animation maps to the scroll range of a container. Use a view-progress timeline when the animation maps to an element’s travel through the scrollport.
Step 2: Build a baseline.
Render the progress indicator and cards in a useful static state first. A browser that ignores the animation should still expose the content, order, contrast, and controls.
Step 3: Add the CSS enhancement.
@supports (animation-timeline: scroll()) {
.progress {
animation: grow-progress linear both;
animation-timeline: scroll(root block);
}
.card {
animation: reveal linear both;
animation-timeline: view(block);
animation-range: entry 0% cover 35%;
}
}Declare animation-timeline after the shorthand because the shorthand resets timeline-related sub-properties. Name the timeline explicitly when nested scroll containers could make the nearest scroller ambiguous.
Step 4: Keep the animated properties cheap.
Prefer opacity and transform. Avoid animating width, height, top, left, or large shadows on a long list unless profiling proves the cost acceptable. Do not add will-change to every card as a guess.
Step 5: Handle reduced motion.
@media (prefers-reduced-motion: reduce) {
.progress,
.card {
animation: none;
transform: none;
opacity: 1;
}
}The content and progress meaning must remain available without animation. A subtle instant state or static indicator is safer than merely shortening a long transition.
Step 6: Test geometry and support.
Test the root scroller, nested horizontal and vertical scrollers, sticky elements, dynamic content, zoom, keyboard navigation, and mobile viewport changes. Feature detection should control enhancement, not hide the baseline.
Step 7: Measure and observe.
Use DevTools performance traces to check style, layout, paint, and compositor work. Test a long list and low-powered mobile device, and watch for content that becomes unreadable when a timeline is unsupported or interrupted.
High-quality sample answer
“The page progress bar uses scroll(root block) because its value is the root scroll range. Each card uses view(block) with an entry-to-cover range because its reveal should follow its own visibility. I render cards normally first, then add the timelines inside @supports; the declaration follows the animation shorthand. I limit motion to transform and opacity, remove nonessential animation for reduced-motion users, and name the scroller when nesting makes the nearest container ambiguous. I test keyboard access, sticky and dynamic content, unsupported browsers, and a long mobile list with performance traces.”
Common mistakes
- Use a scroll event for every frame → handler work can compete with scrolling → prefer a CSS timeline when the relation is declarative.
- Confuse
scroll()andview()→ the animation starts at the wrong moment → map the timeline to container progress or element visibility. - Put
animation-timelinebefore the shorthand → the shorthand can reset it → declare the timeline afterward. - Animate layout properties by default → long pages can trigger expensive work → start with transform and opacity, then profile.
- Ship motion without reduced-motion handling → users lose control over a preference → provide a static state.
- Hide content until animation support exists → unsupported browsers lose information → build the baseline first.
- Assume the nearest scroller is correct → nested containers change the timeline → name or test the intended scroller.
Follow-up questions and responses
Follow-up 1: What is the difference between scroll() and view()?
scroll() tracks a scroll container’s progress. view() tracks an element’s visibility progression through that container’s scrollport.
Follow-up 2: Why must the timeline follow the shorthand?
The animation shorthand resets timeline-related sub-properties, so a preceding timeline declaration can be cleared.
Follow-up 3: What is the fallback?
Keep content visible in its final readable state and use a static progress indicator or a small JavaScript enhancement only when the product requirement truly needs it.
Follow-up 4: How do you support reduced motion?
Disable nonessential animation, remove transforms, and preserve the same content and status meaning with a static style.
Follow-up 5: Can you animate height?
Only after profiling and with a clear need. Transform and opacity usually avoid layout work; animating height can invalidate layout across siblings.
Follow-up 6: What happens in a nested scroller?
The nearest applicable scroll container may differ from the page. Test the geometry and name the intended timeline or use the correct scroller argument.
Follow-up 7: How do you verify it in production?
Record support usage, performance traces on representative devices, reduced-motion behavior, and accessibility checks. Keep the static baseline so unsupported or interrupted timelines remain usable.