axi0motion

Card deck

A 3D stack of cards set up and back from one another; on a timer, a fling or Next the front card drops away and slides in at the back.

  • enhancer
  • cards
  • stack
  • 3d
  • autoplay
  • swipe
  • since 0.2

Background Studio: tune it below, then copy the code

Your system asks for reduced motion: the demo shows its final state and nothing moves.

Live demo

Card deck example

Blue hour

A pale moon over a calm blue harbor

Violet sky

Purple ridges under a pink sunset

Desert noon

Orange desert ridges at noon

Moonrise

Dark peaks under a moonlit sky

Controls

5000 ms
56 px
48 px
4 deg

Code

It follows the controls above: change an option and the code changes with it.

<link rel="stylesheet" href="/axi0/motion/motion.css?v=6">
<script src="/axi0/motion/motion.js?v=10" defer></script>

<div data-motion="card-deck" aria-label="This week">
  <article><h3>Blue hour</h3><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='230' viewBox='0 0 400 230'%3E%3ClinearGradient id='s' x2='0' y2='1'%3E%3Cstop stop-color='%230b2340'/%3E%3Cstop offset='1' stop-color='%235fa8d3'/%3E%3C/linearGradient%3E%3Crect width='400' height='230' fill='url(%23s)'/%3E%3Ccircle cx='217' cy='60' r='22' fill='%23f4f1de'/%3E%3Cpath d='M0 117 Q50 96 100 103 T200 110 T300 106 T400 117 V230 H0Z' fill='%232f5d7c'/%3E%3Cpath d='M0 153 Q50 138 100 138 T200 146 T300 140 T400 153 V230 H0Z' fill='%231d3f5a'/%3E%3Cpath d='M0 190 Q50 206 100 180 T200 190 T300 180 T400 190 V230 H0Z' fill='%230f2638'/%3E%3C/svg%3E" alt="A pale moon over a calm blue harbor"></article>
  <article><h3>Violet sky</h3><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='230' viewBox='0 0 400 230'%3E%3ClinearGradient id='s' x2='0' y2='1'%3E%3Cstop stop-color='%232b1055'/%3E%3Cstop offset='1' stop-color='%23d53a9d'/%3E%3C/linearGradient%3E%3Crect width='400' height='230' fill='url(%23s)'/%3E%3Ccircle cx='82' cy='49' r='20' fill='%23ffdd99'/%3E%3Cpath d='M0 122 Q50 135 100 119 T200 126 T300 120 T400 122 V230 H0Z' fill='%237e3aa1'/%3E%3Cpath d='M0 158 Q50 177 100 147 T200 157 T300 150 T400 158 V230 H0Z' fill='%234a2071'/%3E%3Cpath d='M0 195 Q50 208 100 183 T200 194 T300 192 T400 195 V230 H0Z' fill='%23200b3a'/%3E%3C/svg%3E" alt="Purple ridges under a pink sunset"></article>
  <article><h3>Desert noon</h3><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='230' viewBox='0 0 400 230'%3E%3ClinearGradient id='s' x2='0' y2='1'%3E%3Cstop stop-color='%23f6a04d'/%3E%3Cstop offset='1' stop-color='%23ffd8a8'/%3E%3C/linearGradient%3E%3Crect width='400' height='230' fill='url(%23s)'/%3E%3Ccircle cx='233' cy='60' r='24' fill='%23fff7ea'/%3E%3Cpath d='M0 115 Q50 135 100 103 T200 109 T300 109 T400 115 V230 H0Z' fill='%23d9824a'/%3E%3Cpath d='M0 152 Q50 137 100 145 T200 159 T300 143 T400 152 V230 H0Z' fill='%23a65a2e'/%3E%3Cpath d='M0 186 Q50 168 100 183 T200 187 T300 186 T400 186 V230 H0Z' fill='%235c2f17'/%3E%3C/svg%3E" alt="Orange desert ridges at noon"></article>
  <article><h3>Moonrise</h3><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='230' viewBox='0 0 400 230'%3E%3ClinearGradient id='s' x2='0' y2='1'%3E%3Cstop stop-color='%2310141f'/%3E%3Cstop offset='1' stop-color='%233c4f76'/%3E%3C/linearGradient%3E%3Crect width='400' height='230' fill='url(%23s)'/%3E%3Ccircle cx='145' cy='62' r='17' fill='%23e6e9f2'/%3E%3Cpath d='M0 118 Q50 131 100 115 T200 114 T300 115 T400 118 V230 H0Z' fill='%232a3550'/%3E%3Cpath d='M0 158 Q50 146 100 156 T200 161 T300 149 T400 158 V230 H0Z' fill='%231b2338'/%3E%3Cpath d='M0 186 Q50 165 100 184 T200 184 T300 171 T400 186 V230 H0Z' fill='%230d111c'/%3E%3C/svg%3E" alt="Dark peaks under a moonlit sky"></article>
</div>

The wrappers: CardDeck.tsx (React, TypeScript) and CardDeck.vue (Vue). Both render the same attributes as the HTML; the loader does the rest.

Options

AttributeDefaultWhat it does
data-motion-delay5000time on each card; 0 turns the timer off
data-motion-dx56how far right each card behind sits
data-motion-dy48how far up each card behind sits
data-motion-skew4the slant of the deck
data-motion-labelCardsthe name read out when the element has no aria-label

Accessibility

- The deck is a labeled region announced as a carousel; each card is a group announced as "slide", "2 of 4". Only the front card can take focus. - With a timer, a Stop/Start button comes first; the timer pauses under the pointer and stops for good when focus moves inside. Next sends the front card to the back. - Flinging the front card does the same as Next; every gesture has a button. - Reduced motion turns the timer off and swaps cards with no movement. Reduced motion shows every state at once, with no movement, and stops anything that would move on its own. Frame loops and CSS animations pause off screen and in hidden tabs. Only transform, opacity and paint animate, so nothing around it moves.

Size

card-deck.js is 2.4 KB gzip, with components-kit.js 3.9 KB it needs. A page loads the loader (2.7 KB) and core.js (1.4 KB) once, whatever it uses.