Design reference
01Coverflow carousel
Sliders that show the system moving.
One carousel, three layers. Switch between the raw snap track, the controls, and the scroll-driven depth cue, then copy the same shape into a real page block.
Sibling of the section gallery, animations, and image gallery.
Layer switch
Motion layer The same live carousel changes its visible layer without changing the component.
- Layer 1 Plain scroll snap. The track still moves, lands, and takes keyboard focus.
- Layer 2 Buttons, dots, and live status come online after the site script runs.
- Layer 3 Depth and progress are scrubbed by the track position, not autoplay.
The work, illustrated
Six offers, one track.
Drag it, flick it, or tab to it and use the arrow keys. The readout below stays tied to the slide nearest the centre.
Progressive enhancement
How it keeps working.
The reference notes sit after the live specimen now. On small screens this block collapses, so the carousel stays close to the top.
How it works Six rules
- Layer 1 — it works with nothing. The track is a CSS
scroll-snapcontainer. No JavaScript, no animation support, no library: it still scrolls, still snaps, and is still a tab stop with native arrow-key scrolling. Specimen 03 is that carousel with the script taken away, live. - Layer 2. The buttons show up only once the script runs.
app.jsadds the back and next buttons, dot jumps, and a live “slide n of m” count. They stay hidden until it runs, because a dead button is worse than none. - Layer 3 — motion behind an
@supportsgate. The depth cue and the progress rail run onview()andscroll()timelines. Browsers without scroll-driven animations never match the gate and keep the plain snap track — which is a correct resting state, not a broken one. - Position is read, never counted. Which slide you are on comes from the scroll spot each frame. The slide whose centre is nearest the track centre wins. So a flick, a key, and a button can never disagree with the dots.
- The snap element is never transformed. If you move the snapping item itself, you move its own snap spot too. That feeds back into the scroll, and every snap point drifts off centre. So the depth runs on a figure inside it, on a timeline the parent sets.
- No autoplay, ever. Nothing moves unless you move it. Images carry set
width/heightso there is no layout shift, only transform and opacity animate, andprefers-reduced-motionstops every sample moving while leaving them all usable.
Buyer path carousel
The same component shaped like a real Lead Caravan page block: a visitor path from first click to qualified next step. This is the version to copy when the carousel needs to sell the system, not document the engine.
On a real page
A buyer path, one card at a time.
This is the same carousel without lab language: one focused story arc, visible next steps, and no new CSS.
Peek strip
The browse-it-all one. Many small slides at once, snapped to the START of the track, not the middle. No depth cue — at this size a rotation reads as wobble, and the point of the variant is that every slide is equally legible. Thumbnails, logo rails, avatar sets.
Placeholder faces
Six marks, browsable at a glance.
The face marks the site uses when a quote needs a face and we have no cleared photo. They are shapes on purpose, not people.
Baseline track (unenhanced)
The same section with the data-carousel hook removed, so app.js never wires it. This is exactly what a visitor gets with JavaScript off or blocked: no buttons, no dots — and a carousel that still works. Note that the depth cue survives: it is CSS, so it never needed the script in the first place. Scroll it, or tab to it and use the arrow keys.
JavaScript off
No buttons. Still a carousel.
Nothing drives this one. No buttons, no status line, no script. Scroll it with a pad or your finger, or press Tab until the track has focus and use the arrow keys. It snaps either way. The depth is still there, because that layer is plain CSS.
Anatomy
Three files, nothing else needed. Every sample above uses the same shared code and styles. No page has its own slider CSS.
-
public/styles.css.media-carouseland its parts — the snap track, slides, figures, control row, dots, and the progress rail — plus the@supportsgate that adds the scroll-driven layer and the--stripvariant. -
public/motion.csslc-coverflowandlc-scroll-progress— the two scroll-driven keyframe sets, built from the vendored--motion-*tokens and declared inside the reduced-motion no-preference query, so under reduced motion they don't exist at all. -
public/app.jsThe
[data-carousel]controller — buttons, dot jumps, the live status line, and the rAF-coalesced scroll read that decides which slide is current. It adds.carousel-ready, which is what unhides the controls.
Lead Caravan
Want this shape on a real page?
The classes are shared, not page-scoped — dropping .media-carousel onto a
marketing page is markup, not new CSS. The
section gallery carries it in the catalogue
of every reusable section shape.