Flick cards
A pile of cards seen from the front, the top one straight, the ones under it leaning a little each its own way.
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 round the pile: a card sent flies a little more than its own size away before it turns back, so clip at the section, not at the pile. 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-flick-cards Marks the section that holds the pile. 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 pile, the buttons, the counter, the fields and the dots in. Without it on any section of the page, the embed marks the section holding each pile (data-flick-cards-pile). data-flick-cards-pile 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 presses. Without it the engine takes the parent of the first marked card, and mounts nothing when there is none. [ Required ] data-flick-cards-card Marks one card, a child of the pile. The first one is on top. Without it on any of them the engine takes the pile's own children as the cards. [ Optional ] data-flick-cards-prev Marks the button that brings the last card sent back on top. Anywhere in the section, and more than one is fine. Without it the keyboard still does it, Shift with an arrow or Backspace. [ Optional ] data-flick-cards-next Marks the button that sends the top card, to the right. Anywhere in the section. [ Optional ] data-flick-cards-current Marks the number of the card on top, which the engine writes on two figures. Without it there is no current number. [ Optional ] data-flick-cards-total Marks the total of the counter, which the engine writes as the card count. Without it there is no total. [ Optional ] data-flick-cards-field Marks a line of text the engine fills from the card on top, anywhere in the section. Its value names the field: an element with data-flick-cards-field="title" takes the words the top card carries in data-flick-cards-field-title, and comes in over the swap setting. A card that carries no such attribute leaves the line as it is. Without it nothing outside the pile changes but the counter. [ Optional ] data-flick-cards-tone On a card: its colour, any CSS colour. The engine hands the colour of the card on top to the section as the custom property --flick-cards-tone, which the stylesheet fades from one card to the next and paints the glow with. Without it the property keeps its last value. [ Optional ] data-flick-cards-dot Marks one entry of a list of the cards, anywhere in the section: the first one stands for the first card, and so on. The engine marks the entry of the card on top with data-flick-cards-active and aria-current, and a click on an entry puts its card on top. Without it the pile has no list. [ Optional ] data-flick-cards-glow Marks the box of the preview that the stylesheet paints with a soft light in the colour of the card on top. The engine does not read it. Without it there is no glow. [ Optional ] data-flick-cards-state Written by the engine on the section: ready once mounted, with data-flick-cards-ways beside it, both or x. While a drag runs the section also carries data-flick-cards-dragging, and the card on top data-flick-cards-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-flick-cards-axis both The ways a card can be sent. both: any direction, and the pile keeps every move of a finger. x: left and right only, the card stays level, and a vertical swipe scrolls the page. Options: both or x. data-flick-cards-spread 5 How far a card under the top one leans, in degrees. Each card keeps its own share of it, one to the left, the next to the right. data-flick-cards-rise 0.05 How far each card stands above the one before, in card widths. Negative steps down. data-flick-cards-shrink 0.04 The share of its size a card loses for each place back, 0 to 1. data-flick-cards-dim 0.14 The share of its light a card loses for each place back, 0 to 1. data-flick-cards-visible 4 How many cards are drawn in the pile; the rest wait under the last one, unseen. data-flick-cards-tilt 16 How far a card turns when the hand has carried it one card width aside, held by its edge, in degrees. Held nearer its middle it turns less, held by its foot the other way. data-flick-cards-distance 0.5 How far a card must be carried for it to be sent on release whatever the speed, in card widths. data-flick-cards-flick 1.2 The speed of the hand at release that sends the card before distance is reached, in card widths per second. data-flick-cards-carry 1 The share of the hand's speed the card keeps as it leaves, and as it falls back. data-flick-cards-duration 0.7 How long a card takes to settle on its place, in seconds. data-flick-cards-bounce 0.25 How much the spring gives, 0 to 1: the share of overshoot of a card reaching its place. data-flick-cards-threshold 6 How far a press travels, in pixels, before it becomes a drag; a shorter press is a click. data-flick-cards-swap 0.45 How long a line of text marked as a field takes to come in when another card comes on top, in seconds.
Two files and their pictures (4), 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
FlickCards.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-flick-cards-axis both The ways a card can be sent. both: any direction, and the pile keeps every move of a finger. x: left and right only, the card stays level, and a vertical swipe scrolls the page. Options: both or x. data-flick-cards-spread 5 How far a card under the top one leans, in degrees. Each card keeps its own share of it, one to the left, the next to the right. 0 squares the pile, only the steps show the cards behind A looser pile, the corners of the cards behind well out data-flick-cards-rise 0.05 How far each card stands above the one before, in card widths. Negative steps down. 0 keeps the cards centred on one another More of the top edge of each card behind shows data-flick-cards-shrink 0.04 The share of its size a card loses for each place back, 0 to 1. 0 keeps every card the same size The cards behind are smaller, a pile that recedes data-flick-cards-dim 0.14 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-flick-cards-visible 4 How many cards are drawn in the pile; the rest wait under the last one, unseen. A short pile, down to the top card alone at 1 A thicker pile; more than the number of cards changes nothing data-flick-cards-tilt 16 How far a card turns when the hand has carried it one card width aside, held by its edge, in degrees. Held nearer its middle it turns less, held by its foot the other way. 0 keeps the card upright in the hand and in the air The card swings more as it is pulled, and spins more once thrown data-flick-cards-distance 0.5 How far a card must be carried for it to be sent on release whatever the speed, in card widths. A short pull is enough to send it The card must be carried further, and a slow drag falls back data-flick-cards-flick 1.2 The speed of the hand at release that sends the card before distance is reached, in card widths per second. A lazy flick is enough Only a sharp flick sends it; anything softer falls back data-flick-cards-carry 1 The share of the hand's speed the card keeps as it leaves, and as it falls back. The card leaves at its least speed whatever the hand did, a tamer throw The card leaves faster and flies further before it turns back data-flick-cards-duration 0.7 How long a card takes to settle on its place, in seconds. A quick, dry pile A slow, soft pile data-flick-cards-bounce 0.25 How much the spring gives, 0 to 1: the share of overshoot of a card reaching its place. 0 lands every card without passing its place The cards wobble more as they land data-flick-cards-threshold 6 How far a press travels, 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 card moves, and clicks are safer data-flick-cards-swap 0.45 How long a line of text marked as a field takes to come in when another card comes on top, in seconds. 0 changes the words at once The words rise and fade in more slowly -
Drive it from JavaScript
When something else than the hand sends the cards, a timer or a pair of buttons of your own for one, or when the page reacts to the card that left and the side it went.const element = document.querySelector('[data-flick-cards]'); element.addEventListener('flickcards:change', (e) => { console.log(e.detail); }); const pile = FlickCards.mount(element); // the section auto() mounted, or a new one pile.next('left'); // sends the top card to the left pile.prev(); // brings it back on top element.addEventListener('flickcards:change', (e) => { console.log(e.detail.index, e.detail.sent, e.detail.side); }); // When the pile leaves the page, or before its cards change: FlickCards.destroy(element); // gives back the markup as it was
-
Fit it to your page
- Mark the parts.
data-flick-cardson the section, then inside it-pileon 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 pile with no card marked takes its own children as the cards; without-pile, the parent of the first card is the pile. - The page follows the pile. Three optional parts, anywhere in the section, say what is on top without a line of script. A field:
data-flick-cards-field="title"on an element, and on each card the words it takes,data-flick-cards-field-title="Long Shadow"; the name afterfieldis yours, and one card may feed several fields. The words come in overswapseconds. A colour:data-flick-cards-tone="#c4502a"on each card, which the section receives as the custom property--flick-cards-tone, registered as a colour so it fades from card to card. A list:data-flick-cards-doton each entry, in the order of the cards; the entry of the card on top carriesdata-flick-cards-activeandaria-current, and a click on an entry puts its card on top. - Any number of cards, two at least. The first card of the markup is on top.
visiblesays how many are drawn; with more cards than that, the others wait under the last one, and a card sent goes out of sight once it is back under the pile. - A card holds anything: a photograph edge to edge, a flat colour and a large title, a small picture in wide margins as in the preview, a quote, a price. Give the cards one size; the pile is as large as a card.
- The flick. The top card follows the hand one to one, in any direction, and turns about the point it is held by: pulled aside by its top it leans one way, by its foot the other. On release it is sent when the hand was moving away faster than
flick, or when the card was carried further thandistance. It leaves the way the hand went, at the hand's speed timescarry. Anything shorter or slower falls back on the spring, and so does a card thrown back toward the pile. - The loop. A card sent flies until it overlaps no other card, then goes to the bottom of the pile and slides back in under it. Several cards can be in the air at once.
- Keyboard. The pile is one Tab stop, the top card. Each arrow key sends the top card its own way. Shift with an arrow, or Backspace, brings the last card sent back on top, out from under the pile the way it left. The focus follows the top card. The two buttons are buttons:
-nextsends the top card to the right,-prevbrings the last one back. - A card may be a link. A press that travels less than
thresholdpixels is a click: on the top card it goes through. A card under it opens nothing, and neither does the release of a drag. - Sideways only.
data-flick-cards-axis="x"on the section keeps the card level and hands the vertical swipe back to the page: the setting for a pile in the middle of a long page on a phone. - One setting for every pile of the page, without opening the file:
window.FlickCardsSettings = { tilt: 24 }in a script placed beforeflick-cards.js. Three levels, least specific first:DEFAULTS, then that object, then adata-flick-cards-*attribute on the section. - States, written by the engine:
data-flick-cards-state="ready"anddata-flick-cards-ways(bothorx) on the section once mounted,data-flick-cards-draggingon it while a drag runs, anddata-flick-cards-activeon the card on top. A rule of yours can read them. - Reduced motion: the card does not follow the hand and nothing flies. A flick, a key or a button puts the next card on top at once.
- From JavaScript:
FlickCards.mount(el, overrides)returns the instance:element,settings,cards,index()(the card on top),next(side)(sends the top card'right','left','up'or'down'),prev()(brings the last one back),select(i)(puts card i on top, the pile closing up in place),hold(x, y)(holds the top card out, x and y card widths from the pile, as a hand would) andrelease()(lets it fall back),phase(i),refresh()(measures the card again) anddestroy().destroy()takes off every listener, the watch on sizes, the states, the counter and the fields it wrote, the marks on the dots, the colour and every style it set, so the markup is as it was.FlickCards.destroy(el),FlickCards.refresh(el),FlickCards.select(el, i),FlickCards.auto(root)andFlickCards.defaultscomplete it. In React,mountinuseEffectanddestroyin its cleanup. - Event:
flickcards:change, bubbling, each time another card comes on top,detail{ element, index, card, cards, direction, sent, side }: direction 1 when a card was sent, -1 when one came back, 0 at mount and afterselect();sentis the card that left, andsidethe way it went,left,right,upordown. It fires the moment the card is let go, and once at mount for the first card. A pile that sorts, kept on the right and dropped on the left, readsside. - The preview ships whole.
snippet.htmland the last part offlick-cards.css, after the linelook, left out of the Webflow Embed, are the preview: the same name, title, sleeves, list, lines, counter and buttons, in Geist and Geist Mono, loaded by the font tag of the Code tab, with the four photographs ofimages/. Theflick-cards-*classes are never read by the engine: on a pile 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, the entry of the card on top, the glow in its colour) 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 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 all round. A card sent flies a little more than its own size away from the pile before it turns back, in the direction of the flick. A parent that clips right at the edge of the pile cuts it in mid air; clip further out, at the section, as the preview does with
overflow: hidden, so the page never scrolls sideways. - **With
axisatboth, a finger on the pile does not scroll the page.** The pile takes every move of the finger, which is what lets a card go up or down. On a long page read on a phone, where the pile may fill the screen, setaxistox. - 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 pile.
- A control inside a card is pressed like the rest of the card: the press becomes a drag past
threshold, and on a card under the top one a click reaches nothing. A control that must always answer goes outside the pile. - **Settings handed to
mount()after the page loaded are dropped.** The script mounts every[data-flick-cards]on DOM ready, andmount()on a mounted section returns the running instance. A page-wide block goes inwindow.FlickCardsSettings, 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 and all of them straight, so little moves when it starts. With scripting turned off the pile is a row that scrolls sideways and snaps card by card.
- Leave room all round. A card sent flies a little more than its own size away from the pile before it turns back, in the direction of the flick. A parent that clips right at the edge of the pile cuts it in mid air; 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
Flick cards is a scroll animation for Webflow and vanilla JavaScript, with no library. Also called flick cards slider, swipe cards stack or swipeable card stack.
A pile of cards seen from the front, the top one straight, the ones under it leaning a little each its own way. The hand takes the top card and it follows, turning about the point it is held by; let go with a quick flick, in any direction, and the card leaves that way with the speed and the turn the hand gave it, while the next card straightens and moves up. Once it overlaps nothing the card changes order, and a spring brings it back in under the pile, so the pile goes round without end and takes as many cards as the markup holds. A flick too short or too slow sends nothing: the card falls back on the same spring, with a little give. The arrow keys send the top card their own way, two buttons send it and bring the last one back, a press that does not travel stays a click so a card may be a link, a counter reads the card on top, and under reduced motion the next card is simply there. The page follows the pile: a line of text marked as a field takes the words of the card on top, the section takes its colour, and a list of the cards marks its entry and puts a card on top at a click. The event says which card left and on which side, for a pile that sorts as well as it shows. No library: Pointer Events, one spring per card, a clock that sleeps at rest. Pattern type flick cards slider, swipe cards, card stack you throw.
Updated
Questions
Paste the section, or mark your own: data-flick-cards on the section, data-flick-cards-pile on the box that directly holds the cards and data-flick-cards-card on each card, a Collection List works. The Embed lays the cards in a pile and lets the hand send the top one; the two buttons carry data-flick-cards-prev and data-flick-cards-next.
The release. A card goes when the hand was moving away faster than flick, 1.2 card widths per second by default, or when it was carried further than distance, half a card width. It then leaves the way the hand went, at the hand's speed. Anything shorter or slower falls back on a spring, and so does a card thrown back toward the pile.
It flies until it overlaps no other card, goes to the bottom of the pile and slides back in under it, so the pile goes round without end. Any number of cards from two up works, with nothing to set: the engine counts the cards of the markup. data-flick-cards-visible says how many are drawn, four by default.
Yes. The flickcards:change event carries detail.sent, the card that left, and detail.side, the way it went: left, right, up or down. Listen to it on the section and keep or drop the card from there; data-flick-cards-axis="x" keeps the cards to left and right only.
A finger flicks a card as a mouse does. With axis at both the pile keeps every move of the finger; set it to x and a vertical swipe scrolls the page again. The pile is one Tab stop: each arrow key sends the top card its own way, and Shift with an arrow or Backspace brings the last one back. Under reduced motion the next card is simply there. It needs no library: one script, one spring per card.