axi0motion

Card fan

A fan of cards bounces in; the one under the pointer straightens while the others make room.

  • enhancer
  • hover
  • cards
  • images
  • spring
  • entrance
  • 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 fan example

Evening hillsOpen waterSoft shapesGreen ridgesViolet shapes

Controls

120 px
7 deg
46 px
1.08
80 ms

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 class="m-card-fan" data-motion="card-fan">
  <img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 400' width='300' height='400' preserveAspectRatio='xMidYMid slice'%3E%3Cdefs%3E%3ClinearGradient id='a' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%233b2a6b'/%3E%3Cstop offset='.55' stop-color='%23ff6b8b'/%3E%3Cstop offset='1' stop-color='%23ffb36b'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='300' height='400' fill='url%28%23a%29'/%3E%3Ccircle cx='190' cy='238' r='44' fill='%23ffe2b0'/%3E%3Cpath d='M0 290 Q70 240 140 285 T300 270 V400 H0Z' fill='%238a3b6e'/%3E%3Cpath d='M0 330 Q90 290 170 330 T300 320 V400 H0Z' fill='%234a2152'/%3E%3Cpath d='M0 365 Q120 340 300 372 V400 H0Z' fill='%2324122e'/%3E%3C/svg%3E" alt="Evening hills"><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 400' width='300' height='400' preserveAspectRatio='xMidYMid slice'%3E%3Cdefs%3E%3ClinearGradient id='a' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%239be7ff'/%3E%3Cstop offset='.5' stop-color='%233b82f6'/%3E%3Cstop offset='1' stop-color='%23172554'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='300' height='400' fill='url%28%23a%29'/%3E%3Ccircle cx='220' cy='90' r='26' fill='%23f0fbff'/%3E%3Cpath d='M0 220 Q40 205 75 220 T150 220 T225 220 T300 220 V400 H0Z' fill='%232563eb' opacity='.7'/%3E%3Cpath d='M0 270 Q40 252 80 270 T160 270 T240 270 T320 270 V400 H0Z' fill='%231d4ed8' opacity='.8'/%3E%3Cpath d='M0 330 Q50 310 100 330 T200 330 T300 330 V400 H0Z' fill='%230f1f5c'/%3E%3C/svg%3E" alt="Open water"><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 400' width='300' height='400' preserveAspectRatio='xMidYMid slice'%3E%3Crect width='300' height='400' fill='%23ffd6c2'/%3E%3Ccircle cx='70' cy='90' r='110' fill='%23ff9f8a'/%3E%3Ccircle cx='250' cy='300' r='130' fill='%23ff6b8b' opacity='.85'/%3E%3Ccircle cx='190' cy='150' r='60' fill='%23ffe9a8'/%3E%3Ccircle cx='90' cy='320' r='50' fill='%23f6a04d' opacity='.9'/%3E%3C/svg%3E" alt="Soft shapes"><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 400' width='300' height='400' preserveAspectRatio='xMidYMid slice'%3E%3Cdefs%3E%3ClinearGradient id='a' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%23e6fbd6'/%3E%3Cstop offset='1' stop-color='%237dd3a8'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='300' height='400' fill='url%28%23a%29'/%3E%3Cpath d='M0 230 L60 160 L120 225 L190 140 L300 240 V400 H0Z' fill='%233f9f73'/%3E%3Cpath d='M0 290 L80 215 L150 280 L230 205 L300 260 V400 H0Z' fill='%23227552'/%3E%3Cpath d='M0 350 L100 290 L190 345 L300 300 V400 H0Z' fill='%230f3d2b'/%3E%3C/svg%3E" alt="Green ridges"><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 400' width='300' height='400' preserveAspectRatio='xMidYMid slice'%3E%3Cdefs%3E%3ClinearGradient id='a' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%23c4b5fd'/%3E%3Cstop offset='1' stop-color='%234c1d95'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='300' height='400' fill='url%28%23a%29'/%3E%3Ccircle cx='220' cy='110' r='90' fill='%238a7dff' opacity='.8'/%3E%3Ccircle cx='80' cy='280' r='110' fill='%236d28d9' opacity='.7'/%3E%3Ccircle cx='170' cy='250' r='40' fill='%23f5d0fe'/%3E%3C/svg%3E" alt="Violet shapes">
</div>

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

Options

AttributeDefaultWhat it does
data-motion-spread120pixels between card middles
data-motion-angle7how far the cards lean, in degrees
data-motion-push46how far the neighbors move aside
data-motion-scale1.08how much the held card grows
data-motion-stagger80milliseconds between cards bouncing in

Accessibility

Only transforms move, so the layout never shifts, and the cards keep their order, alternative text and links for screen readers. Keyboard focus on a card does what the pointer does; on touch, a tap holds a card and a second tap lets go. A fan already on screen at load appears in place; one further down bounces in when it arrives. Reduced motion: the fan appears in place and changes without movement.

Size

card-fan.js is 1.7 KB gzip. A page loads the loader (2.7 KB) and core.js (1.4 KB) once, whatever it uses.