Deck slider
A deck of cards a little offset from one another, seen in perspective, in place of a row.
Documentation
-
Copy to Webflow
Add Geist and Geist Mono to your site's fonts (Site settings, Fonts, Google Fonts) before pasting: Webflow removes any font your site has not installed. -
Add custom CSS
Custom CSS in WebflowThis CSS already ships inside the Webflow embed, so a pasted section needs nothing added. Paste it yourself, in the page or the site custom code, only if the effect runs on several pages. The tutorial explains where.
See the tutorialGive the cards one size and leave room at the sides of the deck: the leaving card swings out by a little more than its own width, so clip at the section, not at the deck. And leave the transform, the z-index, the opacity and the filter of a card to the engine, which writes the four. -
Add custom JS
Custom JavaScript in WebflowThis script already ships inside the Webflow embed, settings block included. You paste it yourself, before the closing body tag of the page or the site custom code, only when the effect has to run on several pages, and the tutorial says how to choose.
See the tutorial -
Tune it
How to add a custom attribute in WebflowA custom attribute is a pair, a name and a value, typed in the Settings panel of the element you selected, under Custom attributes. The name is what the script looks for, the value is what it reads. The tutorial shows the panel.
See the tutorialCustom attributesSelect the section on the canvas and open the Settings panel. Under Custom attributes, add a pair: the name on the left, the value on the right.Custom attribute What it does data-deck-slider Marks the section that holds the deck. It takes no value: leave the value field empty. That section is what the engine mounts on, what carries the settings and the state, and what it looks for the deck, the buttons and the counter in. Without it on any section of the page, the embed marks the section holding each deck (data-deck-slider-deck). data-deck-slider-deck Marks the box that directly holds the cards. The cards lie on one another in it, so it is as large as one card, and it is what the hand drags. Without it the engine takes the parent of the first marked card, and mounts nothing when there is none. [ Required ] data-deck-slider-card Marks one card, a child of the deck. The first one is in front. Without it on any of them the engine takes the deck's own children as the cards. [ Optional ] data-deck-slider-prev Marks the button that steps back: the last card comes round to the front. Anywhere in the section, and more than one is fine. Without it the deck is stepped by the drag and the keys. [ Optional ] data-deck-slider-next Marks the button that steps forward: the front card goes behind the deck. Anywhere in the section. [ Optional ] data-deck-slider-current Marks the number of the card in front, which the engine writes on two figures. Without it there is no current number. [ Optional ] data-deck-slider-total Marks the total of the counter, which the engine writes as the card count. Without it there is no total. [ Optional ] data-deck-slider-state Written by the engine on the section: ready once mounted. While a drag runs the section also carries data-deck-slider-dragging, and the card in front data-deck-slider-active. Never written by hand; the stylesheet reads them, and so can a rule of yours. [ Optional ] SettingsEvery setting is a custom attribute too, added the same way and on the same section. An attribute always wins over the defaults written in the code.Attribute Default What it is data-deck-slider-turn -20 How far the deck is turned about its upright axis, in degrees. Negative shows the cards behind on the right, positive on the left. data-deck-slider-pitch 6 How far the deck leans back, its top away from the eye, in degrees. data-deck-slider-depth 0.17 The gap between two cards of the deck, along its thickness, in card widths. data-deck-slider-shift 0.07 How far each card stands aside from the one before, in the plane of the deck, in card widths. Negative steps to the left. data-deck-slider-rise 0.06 How far each card stands above the one before, in card widths. Negative steps down. data-deck-slider-perspective 3.4 The distance of the eye from the deck, in card widths. data-deck-slider-visible 4 How many cards are drawn in the deck; the rest wait behind the last one, unseen. data-deck-slider-dim 0.2 The share of its light a card loses for each place back, 0 to 1. data-deck-slider-reach 1.06 How far the leaving card swings out before it goes behind, as a multiple of what just clears the deck. data-deck-slider-swing 7 How far the card leans as it swings out, in degrees. data-deck-slider-duration 0.95 How long one step takes to settle, in seconds. data-deck-slider-swipe 0.2 How far a drag must go for the step to be taken on release, in card widths; a quick flick takes it whatever the distance. data-deck-slider-threshold 6 How far a press travels sideways, in pixels, before it becomes a drag; a shorter press is a click.
Two files and their pictures (6), no build step.
It runs in the browser only. Load it with a script tag in plain HTML, in a
client-side script in Astro, never in the frontmatter. In React, call
DeckSlider.mount(ref.current) in useEffect and
destroy() in its cleanup.
-
Tune it, either way
Per element with an attribute, or once for the whole site by editingDEFAULTSat the top of the file.Attribute Default What it is Lower Higher data-deck-slider-turn -20 How far the deck is turned about its upright axis, in degrees. Negative shows the cards behind on the right, positive on the left. Nearer 0, the deck faces the eye and its depth shows less The deck is seen more from the side, the pictures narrower data-deck-slider-pitch 6 How far the deck leans back, its top away from the eye, in degrees. 0 stands it upright; below 0 it leans toward the eye The top edge recedes more, a deck seen from further below data-deck-slider-depth 0.17 The gap between two cards of the deck, along its thickness, in card widths. A tight deck, the cards almost on one another A deep deck, each card clearly behind and smaller than the one before data-deck-slider-shift 0.07 How far each card stands aside from the one before, in the plane of the deck, in card widths. Negative steps to the left. 0 lines the cards up behind one another More of each card behind shows at the side data-deck-slider-rise 0.06 How far each card stands above the one before, in card widths. Negative steps down. 0 keeps the cards level More of each card behind shows above data-deck-slider-perspective 3.4 The distance of the eye from the deck, in card widths. A near eye: strong perspective, the far edge of a card much shorter A far eye: flatter, closer to a drawing without vanishing point data-deck-slider-visible 4 How many cards are drawn in the deck; the rest wait behind the last one, unseen. A short deck, down to the front card alone at 1 A longer deck; more than the number of cards changes nothing data-deck-slider-dim 0.2 The share of its light a card loses for each place back, 0 to 1. 0 lights every card the same The cards behind fall into the dark sooner data-deck-slider-reach 1.06 How far the leaving card swings out before it goes behind, as a multiple of what just clears the deck. Under 1 the card turns back while it still overlaps the deck, and is seen jumping behind it The card swings wider, a slower and broader gesture data-deck-slider-swing 7 How far the card leans as it swings out, in degrees. 0 slides it out upright A card thrown with more of a twist data-deck-slider-duration 0.95 How long one step takes to settle, in seconds. A quick, dry step A slow, soft step data-deck-slider-swipe 0.2 How far a drag must go for the step to be taken on release, in card widths; a quick flick takes it whatever the distance. A short pull is enough The card must be pulled further, and a hesitant drag falls back data-deck-slider-threshold 6 How far a press travels sideways, in pixels, before it becomes a drag; a shorter press is a click. A drag starts at the slightest move, and a shaky click may be read as one A press has to travel further before the deck moves, and clicks are safer -
Drive it from JavaScript
When something else than the buttons steps the deck, a timer or a list beside it for one, or when the page reacts to the card in front.const element = document.querySelector('[data-deck-slider]'); element.addEventListener('deckslider:change', (e) => { console.log(e.detail); }); const deck = DeckSlider.mount(element); // the section auto() mounted, or a new one deck.next(); // the front card goes behind deck.select(4); // brings the fifth card to the front element.addEventListener('deckslider:change', (e) => { console.log(e.detail.index, e.detail.direction); }); // When the slider leaves the page, or before its cards change: DeckSlider.destroy(element); // gives back the markup as it was
-
Fit it to your page
- Mark the parts.
data-deck-slideron the section, then inside it-deckon the box that directly holds the cards and-cardon each card. Optional, anywhere in the section:-prevand-nexton the two buttons,-currentand-totalon the two numbers of the counter. A deck with no card marked takes its own children as the cards; without-deck, the parent of the first card is the deck. - Any number of cards, two at least. The first card of the markup is in front.
visiblesays how many are drawn; with more cards than that, the others wait behind the last one and fade in as they move up. - A card holds anything: a picture and a line as in the preview, a quote, a price. Give the cards one size; the deck is as large as a card.
- The front card is itself. A press that travels less than
thresholdpixels is a click: on the front card it goes through, and a link opens. On a card behind it brings that card to the front and opens nothing. A drag never opens a card. - The drag. The leaving card starts out under the hand, one to one. Let go past
swipecard widths, or with a quick flick, and the step is taken; a shorter drag falls back. A drag to the left steps forward, the front card leaving on the left; a drag to the right steps back, the last card coming round on the right. One drag takes one step. - Keyboard. The deck is one Tab stop, the front card. The left and right arrows step the deck, Home and End go to the first and last card, and the focus follows the card that comes to the front. The two buttons are buttons: Enter and the space bar press them.
- One setting for every deck of the page, without opening the file:
window.DeckSliderSettings = { turn: -28 }in a script placed beforedeck-slider.js. Three levels, least specific first:DEFAULTS, then that object, then adata-deck-slider-*attribute on the section. - States, written by the engine:
data-deck-slider-state="ready"on the section once mounted,data-deck-slider-draggingon it while a drag runs, anddata-deck-slider-activeon the card in front. A rule of yours can read them, to show the caption of the front card alone for instance. - Reduced motion: a step is immediate, by the buttons, the keys or a swipe, and the deck does not follow the hand.
- From JavaScript:
DeckSlider.mount(el, overrides)returns the instance:element,settings,cards,index()(the card in front),next(),prev(),select(i)(brings card i to the front the shortest way round),position()(the deck's position in steps),speed(),phase(),refresh()(measures the card again) anddestroy().destroy()takes off every listener, the watch on sizes, the states, the counter it wrote and every style it set, so the markup is as it was.DeckSlider.destroy(el),DeckSlider.refresh(el),DeckSlider.select(el, i),DeckSlider.auto(root)andDeckSlider.defaultscomplete it. In React,mountinuseEffectanddestroyin its cleanup. - Event:
deckslider:change, bubbling, each time another card comes to the front,detail{ element, index, card, cards, direction }, direction 1 forward and -1 back. It fires once at mount, for the first card, with direction 0, and at the middle of a step, when the leaving card goes behind. - The preview ships whole.
snippet.htmland the last part ofdeck-slider.css, after the linelook, left out of the Webflow Embed, are the preview: the same name, heading, cards, counter and buttons, in Geist and Geist Mono, loaded by the font tag of the Code tab. Thedeck-slider-*classes are never read by the engine: on a deck of your own, keep your classes, add the attributes, and drop that last part of the file and the font tag. The states of the look (the keyboard ring, the buttons under the pointer) sit just before that line and find their elements by the attributes of the markup, never by a class: Webflow renames a class pasted twice on one site (AB-116). Drop them too. - On Webflow, add the fonts first. Add Geist and Geist Mono to your site's fonts (Site settings, Fonts, Google Fonts) before pasting: the paste names them, and the Designer drops a font the site does not have.
- The words and the pictures of the markup are the preview's, and they ship as they are: pasted, the page reads the way the preview did. Put the user's own content in their place whenever they want, in the page's language.
- Mark the parts.
-
Avoid the pitfalls
- Leave room at the sides. The leaving card swings out by a little more than its own width, on the left going forward and on the right going back. A parent that clips right at the edge of the deck cuts the card in mid swing; clip further out, at the section, as the preview does with
overflow: hidden, so the page never scrolls sideways. - The cards share one size. They lie in one grid cell, and the engine measures the first one. Cards of different heights stretch to the tallest.
- Never set a transform, a z-index, an opacity or a filter on a card. The engine writes the four on every card. Style what is inside the card, or the box around the deck.
- **
reachunder 1 shows the trick.** The card changes order at the far point of its swing, where it covers nothing. Pulled in closer, it is still over the deck when it goes behind, and the eye sees the jump. - A control inside a card is pressed like the rest of the card: the press becomes a drag past
threshold, and on a card behind a click brings the card forward instead of reaching the control. A control that must always answer goes outside the deck. - **Settings handed to
mount()after the page loaded are dropped.** The script mounts every[data-deck-slider]on DOM ready, andmount()on a mounted section returns the running instance. A page-wide block goes inwindow.DeckSliderSettings, whichauto()reads itself. To change the cards,destroy(), change them,mount()again. - Before the script runs the cards already lie in one pile, the first on top, so nothing jumps when it starts. With scripting turned off the deck is a row that scrolls sideways and snaps card by card.
- Leave room at the sides. The leaving card swings out by a little more than its own width, on the left going forward and on the right going back. A parent that clips right at the edge of the deck cuts the card in mid swing; clip further out, at the section, as the preview does with
One click on Copy the AI prompt copies everything an assistant needs to build this effect: the complete code, the markup, the dependency, every setting and every pitfall.
What it does
Deck slider is a scroll animation for Webflow and vanilla JavaScript, with no library. Also called stacked cards slider, card stack slider or card deck carousel.
A deck of cards a little offset from one another, seen in perspective, in place of a row. At each step the card in front leaves the top of the deck, swings out to the side on a lean, drops to the depth of the last card and comes back in behind it, while every other card moves up one place; a step back plays the same move the other way round, the last card coming round to the front. The deck goes round without end and takes as many cards as the markup holds. Three ways to step: two buttons, a drag or a swipe on the deck (the card starts out under the hand, and a release far or quick enough finishes the step), and the arrow keys. A press that does not travel stays a click, so the front card is a real link and a card behind comes to the front. A vertical swipe still scrolls the page, a counter reads the card in front, and under reduced motion a step is immediate. No library: Pointer Events, one transform per card and per frame, a clock that sleeps at rest. Pattern type stacked cards slider, card deck carousel, card stack swipe.
Updated
Questions
Paste the section, or mark your own: data-deck-slider on the section, data-deck-slider-deck on the box that directly holds the cards and data-deck-slider-card on each card, a Collection List works. The Embed lays the cards on one another in perspective and steps them; the two buttons carry data-deck-slider-prev and data-deck-slider-next.
Any number from two up, with nothing to set: the engine counts the cards of the markup. The deck goes round without end in both directions. data-deck-slider-visible says how many cards are drawn in the pile, four by default; the others wait behind the last one and fade in as they move up.
Yes. The deck sets touch-action: pan-y, so a finger moving up or down scrolls the page and only a sideways move pulls the card. The card starts out under the finger; let go past a fifth of its width, or with a quick flick, and the step is taken. A shorter drag falls back.
Yes. A press that does not travel is a click: on the front card it goes through and the link opens, on a card behind it brings that card to the front instead. A drag never opens a card, because the click that follows it is swallowed.
The deck is one Tab stop, the front card; the left and right arrows step it, Home and End go to the first and last card, and the two buttons are real buttons. Under reduced motion a step is immediate. It needs no library: one script, one spring on a requestAnimationFrame clock that sleeps at rest.