Representative interview topic

Frontend interview: How would you design degradable scroll-driven animations with CSS?

FrontendMedium
Offer.cc Editorial TeamPublished Updated

Question

Design a reading progress bar and card entrance effect with CSS scroll-driven timelines, and explain scroll containers, view timelines, fallback, and accessibility.

1. Question and context

A content page needs a reading progress bar at the top, and cards should fade in as they enter a scroll container. The team first used a scroll event listener and requestAnimationFrame, but low-end devices show main-thread jank. Redesign it with CSS Scroll-driven Animations, explain scroll progress versus view progress, and cover unsupported browsers, reduced motion, and verification.

2. What the interviewer evaluates

  • Whether you understand that CSS animations can follow a scroll timeline or view timeline instead of the default time-based timeline.
  • Whether you distinguish scroll() tracking a scroller's position from view() tracking an element's visibility range inside its nearest scroller.
  • Whether you handle named timelines, animation ranges, overflow prerequisites, and shorthand reset order.
  • Whether compatibility, performance, keyboard reading, reduced motion, and a no-JavaScript fallback are part of the design.

3. Clarifications to ask before answering

  1. Should the progress bar follow the document or an inner article scroller?
  2. Should a card animate once on entry, or move forward and backward with its visible fraction?
  3. Which browsers are required, and what visual and functional fallback is acceptable?
  4. Should users who prefer reduced motion still see progress changes, and could motion affect reading order or focus?

4. A 30-second answer framework

I would bind the reading bar to the scroller's scroll progress timeline and bind cards to view progress timelines. scroll() represents progress from the scroller's start to its end; view() represents an element entering and leaving the visible range of its nearest scroller. Keyframes still define the visual change, while animation-timeline supplies progress. The container must have real scrollable distance and the animation range must be explicit. Unsupported browsers keep a static progress cue or use a throttled, lightweight JavaScript fallback; core content never depends on motion. I would add supports checks, reduced-motion behavior, keyboard tests, and low-end device measurements.

5. Step-by-step deep answer

Step 1: Establish the timeline semantics

A scroll progress timeline is driven by a scroller's position, normally moving from 0% at the start to 100% at the end. A view progress timeline is driven by the target element's visible range relative to its nearest scroller, which suits cards entering and leaving the viewport. Both build on the CSS Animations and Web Animations API timeline model.

Step 2: Implement the reading progress bar

Make the article container overflow for real, name its scroll timeline, and point the progress element's animation-timeline at that name. Do not set animation-timeline without keyframes, and do not treat a container with no scroll range as a usable timeline.

css
.article-scroller {
  overflow-y: auto;
  scroll-timeline-name: --article-scroll;
  scroll-timeline-axis: block;
}

.reading-progress {
  transform-origin: left center;
  animation: grow-progress linear;
  animation-timeline: --article-scroll;
}

@keyframes grow-progress {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

Step 3: Implement a view timeline for cards

A card can use an anonymous view() timeline that tracks visibility in the nearest ancestor scroller. For tighter control, name it with view-timeline-name and adjust the start and end boundaries with view-timeline-inset. The range should match the intended visible zone so a card does not finish its entire animation as soon as it touches an edge.

css
.card {
  animation: reveal-card linear both;
  animation-timeline: view(block 15% 15%);
}

@keyframes reveal-card {
  entry 0% { opacity: 0; transform: translateY(1rem); }
  cover 45% { opacity: 1; transform: translateY(0); }
  exit 100% { opacity: 0.6; }
}

Step 4: Handle ranges and shorthand traps

Named and anonymous timelines require the correct scroller relationship; a mismatch in overflow, axis, or element hierarchy can leave the timeline with no progress. Declare animation-timeline after the animation shorthand because the shorthand resets omitted timeline components to their defaults. Use animation-range or range keyframes for explicit entry, cover, and exit phases, then test in target browsers.

Step 5: Design fallback and accessibility verification

Use @supports to detect animation-timeline. When enhancement is unavailable, keep a static progress cue, normal reading order, and readable contrast. If a dynamic fallback is necessary, throttle scroll work and avoid repeated layout. Under prefers-reduced-motion: reduce, remove translation and continuous motion while retaining progress information and focus order. Test keyboard scrolling, screen readers, rapid reverse scrolling, nested scrollers, and frame time on low-end devices.

6. High-quality sample answer

I would bind the reading bar to the article scroller's scroll progress timeline and cards to view progress timelines in their nearest scroller. The first represents scroll position from 0% to 100%; the second represents the element entering, covering, and leaving the visible range. I would verify real overflow, then define keyframes and animation-timeline in an order that prevents the shorthand from resetting the timeline. @supports would provide a static fallback, with throttled JavaScript only when necessary. Reduced-motion would remove translation and continuous effects while preserving content, progress, and focus. I would verify keyboard use, screen readers, nested scrolling, and low-end frame time.

7. Common mistakes

  • Treating scroll() and view() as the same progress → cards do not follow visibility → explain scroller position versus element visibility first.
  • Declaring a scroll timeline on a container that does not overflow → no animation progress → check content size, overflow, and axis.
  • Letting the animation shorthand overwrite animation-timeline → the default timeline is restored → declare animation-timeline again after the shorthand.
  • Showing content only in modern browsers → older browsers lose core behavior → keep a static cue and normal reading order.
  • Setting only opacity to zero for reduced motion → feedback disappears → remove translation and continuous motion while keeping semantic and visible state.

8. Follow-up questions and responses

Follow-up 1: When do you use a scroll timeline versus a view timeline?

Use a scroll timeline for progress across an entire scrolling region. Use a view timeline for an element entering, covering, and leaving a scroller. The deciding factor is the source of progress, not whether JavaScript is present.

Follow-up 2: Why might animation-timeline be declared but produce no animation?

Common causes are no real scroll range, an axis mismatch, a timeline name outside the ancestor relationship, or a later animation shorthand resetting the timeline. Check overflow, timeline relationships, and declaration order in that sequence.

Follow-up 3: How do you degrade for older browsers?

Keep a static progress cue and complete content. If dynamic progress is essential, use a throttled scroll listener for a small update. The fallback must not change reading order, keyboard access, or focus behavior.

Follow-up 4: How do you respect reduced motion?

Under prefers-reduced-motion: reduce, remove translation, scaling, and long-running motion. Keep static or instantaneous progress updates, content order, and readable feedback, and verify that rapid scrolling does not hide information.

Public sources

Related questions