Slide In Cards
Five icon + text cards of 280 px in a 280 px box (720 px of travel): each one slides 60 px and goes from 0 to 1 opacity over the first 40% of its entry, i.e. 34 px of scrolling — odd cards from the left, even cards from the right via nth-child(2n). animation-timeline: view(), CSS only, zero JavaScript.
Updated
This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Scroll has 70 effects, including 12 free. Explore the category →
3 usage examples



How it works
The sold HTML: the .demo-preview frame (centered flex, min-height: 300px, position: relative, #0a0a0f background, white text, box-sizing: border-box inherited by everything), the .sdc-scroll-container box (100% × 280px, overflow-y: auto, scroll-behavior: smooth), the .sdc-slidein-content stage (centered flex column, gap: 24px, padding: 60px 20px, 1,000 px tall) and five 280 px .sdc-slidein-card — padding 20, radius 14, white background at 4%, white border at 8%, a 44 px .sdc-slidein-icon tile (22 px SVG, four tints at 20%) and text (h4 13.6 px, p 11.5 px in white at 40%). The h4s carry an inline style="font-family: Inter, …"; Inter is not loaded, the system font takes over. Measured: 86 px card, 110 px pitch, 720 px of travel, no request or error, empty JS field.
Two @keyframes — sdcSlideInLeft from opacity: 0; translateX(-60px) to opacity: 1; translateX(0), sdcSlideInRight the same from +60px — and one rule: animation-timeline: view(), animation-range: entry entry 40%, animation-duration: auto, linear, animation-fill-mode: both; :nth-child(2n) only changes the name. view() ties the progress to the card's visibility inside the nearest scroll container — the box, not the page. The entry phase runs from the card's top edge on the box's bottom edge (0%) to the card fully inside (100%); entry entry 40% keeps only its first 40%, 34 px of scrolling for an 86 px card; both holds the first keyframe before, the last one after. Measured in Chromium: at rest, cards 1 and 2 in place, 3 to 5 at opacity 0 (card 3 right on the bottom edge); at scrollTop 120, card 4: .29 and 42.6 px; at 240, card 5: .58 and −25.1 px; at 306 everything is in, the remaining 414 px show nothing but empty space. Scrolling back reverses it all.
Two writing choices matter. The hidden state sits in the first keyframe, not in the base style: a browser that ignores animation-timeline plays a zero-length animation (animation-duration: auto computes to 0s in Firefox 153, measured) and shows the last keyframe, the card in place — a fallback with no @supports. The alternation is structural: :nth-child(2n) counts the stage's children, so a generated list, an inserted or deleted line recompute the parity, whereas fx-0537 Slide In and fx-0550 Slide In CSS Native require a -left/-right class per block. Two measured limits: a box with no travel makes the timeline inactive and shows everything in place (1,200 px box: five cards at opacity 1); below 400 px wide, the card shifted by +60 px overflows on the X axis (13 px at 390, 28 at 360), which overflow-x: hidden removes.
Accessibility
- prefers-reduced-motion not in the code: under emulation, same values (card 4 at .29 and 42.6 px at scrollTop 120). Nothing is lost: every card ends up visible after 34 px of scrolling, and nothing moves without the reader's gesture (WCAG 2.3.3). To cut it:
@media (prefers-reduced-motion: reduce) { .sdc-slidein-card, .sdc-scroll-hint { animation: none } .sdc-scroll-container { scroll-behavior: auto } }— measured: five cards at opacity 1, no transform. - Keyboard: Tab focuses the box without a
tabindexin Chromium 151 (arrow down ≈ 40 px, End → 720) and Firefox 153 (≈ 50 px); WebKit 26 leaves the focus onbody. Puttabindex="0",role="region", anaria-labeland a:focus-visiblestyle on.sdc-scroll-container;overscroll-behavior: containholds the page at the end of the travel. - Contrast (composited card background
#141419): white heading 18.4:1; paragraph at 40% 3.8:1 at 11.5 px, below 4.5:1 — raise it to 60% (7.1:1). The hint at 30% pulses between 1.2:1 and 1.8:1, in French whatever the language:aria-hidden="true"or remove it. - Screen readers: five
divs with no list role andh4s with no h2/h3 above —ul/liand the heading level that follows yours. A card atopacity: 0stays in the accessibility tree: all five are read in order, scrolled or not. The SVGs have neitheraria-hiddennor<title>: add one or the other.
Browser compatibility
CSS only, zero dependencies: animation-timeline: view() and animation-range (Chrome and Edge 115, Safari 26), flexbox, @keyframes. Firefox supports animation-timeline in no version: declaration ignored, duration computed to 0 s (measured, Firefox 153), animation frozen on its last keyframe — the five cards sit in place, opaque, from first paint to the bottom of the travel, with no slide. Nothing hidden, nothing overlapping: a flat rendering.
No @supports, and none is needed: since the hidden state is in the first keyframe, any browser that ignores animation-timeline shows the card in place. A list shorter than the box: inactive timeline, everything in place, everywhere.
The code
HTML structure to paste into your page (the CSS + JS come with Premium):
<div class="demo-preview">
<div class="sdc-scroll-container">
<div class="sdc-slidein-content">
<div class="sdc-slidein-card">
<div class="sdc-slidein-icon sdc-slidein-icon--purple">
<svg viewBox="0 0 24 24">…</svg>
</div>
<div class="sdc-slidein-text">
<h4>…</h4>
<p>…</p>
</div>
</div>
… ×5 (pastilles --purple, --pink, --cyan, --amber, --purple)
</div>
</div>
<span class="sdc-scroll-hint">…</span>
</div>
The full HTML + CSS + JS of Slide In Cards, ready to paste — plus all 811 effects. Or €299 once, lifetime access.
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Amplitude (CSS — @keyframes, translateX(∓60px)) |
±60 px | With 280 px cards, you need 400 px of width for +60 px to fit (13 px of overflow measured at 390), otherwise overflow-x: hidden. ±24 px shivers, ±120 px comes from off-frame. |
Range (CSS — animation-range: entry entry 40%) |
entry 0% → entry 40% (34 px of travel) | entry 0% entry 100% stretches the slide over the whole entry (86 px), entry 0% cover 50% up to the middle of the box, entry 20% entry 60% delays the start. |
Alternation (CSS — .sdc-slidein-card:nth-child(2n)) |
even from the right | :nth-child(2n+1) flips the parity, :nth-child(3n) reverses only one card in three; without the rule, everything comes from the left. The selector counts all the stage's children. |
Timeline (CSS — animation-timeline: view()) |
view() — the 280 px box | Remove height and overflow-y from the box: the window drives (measured, 1,400 px window: five visible cards in place at rest). view(block 40px) starts the entry 40 px higher. |
Box and stage (CSS — .sdc-scroll-container { height: 280px }, .sdc-slidein-content { height: 1000px }) |
280 / 1,000 px (720 px of travel, 414 of them empty) | The five cards end at 586 px: beyond that, empty space. Stage at height: auto to follow the content; at least one card below the bottom edge, otherwise nothing animates (1,200 px box: everything in place). |
Curve (CSS — animation-timing-function: linear) |
linear | On a scroll timeline, the curve redistributes the motion along the 34 px, not in time: ease-out covers most of the distance in the first pixels, linear sticks to the finger. |
Hint and smooth scrolling (CSS — .sdc-scroll-hint, scroll-behavior: smooth) |
“Scrollez ↓” pulsing 2 s / smooth | The hint is absolute relative to the frame and hard-coded in French: delete the span. smooth only eases programmatic scrolls (scrollTop = 300 reads back 0, reached in 265 ms) — auto if a button drives the box. |
FAQ
animation-fill-mode: both applies the first keyframe (opacity 0, 60 px offset) to any card that has not started its entry; at rest, card 3's top edge is right on the box's bottom edge, entry at 0%. 34 px of scrolling bring it in. If the list fits in the box, there is no travel: the timeline is inactive and everything shows in place (measured: 1,200 px box, five cards at opacity 1). Invisibility only exists below the bottom edge of a box that scrolls.view() attaches to the nearest scroll container. Remove height: 280px and overflow-y: auto from the box and the window drives — each card enters when its top edge passes the bottom of the screen (measured: 1,400 px window, five visible cards in place at rest, replayed on the way back). Switch the stage to height: auto, remove the hint (anchored to the frame, not to the screen) and, if a fixed bar covers the bottom, set scroll-padding-bottom on html: view() reads it.view(), 60 px, opacity 0 → 1, 280 px box), three differences. Direction: one -left/-right class per block for them, :nth-child(2n) alone here — decisive as soon as the list is generated or edited. The hidden state: for them opacity: 0; translateX(-60px) is the base style and the keyframe only has a to; here both states are in the keyframes, the card stays visible as soon as the animation does not apply. The range: entry entry 50% for fx-0537, entry cover 40% for fx-0550, entry entry 40% here, the shortest. And the card: text only for them, a 44 px tile here.