Stacked Cards
Three stacked cards fan out on hover — pure CSS animation with spring bounce, zero dependencies.
You'll get access to the interactive demo with a free account.
This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Cards has 41 effects, including 6 free. Explore the category →
3 usage examples



How it works
The three .card-stack-item elements are absolutely positioned inside their container (position: absolute; width: 100%; height: 100%) and perfectly overlap at rest. Stack order is controlled by descending z-index values (3 → 2 → 1 from first to third child): the first child sits on top.
On .card-stack-container hover, the CSS selector :hover .card-stack-item:nth-child(n) applies three distinct transform values: the top card slides left (translateX(-40px) rotate(-8deg)), the middle card lifts up (translateY(-10px) without rotation), and the bottom card slides right (translateX(40px) rotate(8deg)). No script triggers this movement.
The transition uses 0.4s cubic-bezier(.175, .885, .32, 1.275) — a back-out easing where the fourth parameter (1.275 > 1) causes a slight overshoot at the end of travel, giving a natural spring feel on landing.
Only the transform property is animated: browsers composite this property on the GPU, avoiding any page reflow or repaint. The effect stays performant even on dense card lists.
Accessibility
- prefers-reduced-motion: ABSENT from the base code. No
@media (prefers-reduced-motion: reduce)rule is present — the transition fires regardless of system preference. Add@media (prefers-reduced-motion: reduce) { .card-stack-item { transition: none } }if the animation may cause discomfort. - Keyboard interaction: the effect relies solely on
:hover(mouse only). Keyboard users do not see the fan. To fix this, replace:hoverwith:hover, :focus-withinon.card-stack-container. - Text contrast: cards use purple-to-fuchsia gradients (
#6366f1 → #d946ef) on a#12121abackground. White text on these gradients comfortably exceeds the WCAG AA ratio (≥ 4.5:1). - Semantics: the container is a plain
<div>with no ARIA role. If the cards represent functional content (selection, navigation), addrole="group"and anaria-labeldescribing the set. - Touch: on mobile,
:hoverfires on first tap and resets on the second — native browser behavior, generally acceptable for a purely decorative effect.
Browser compatibility
100% CSS effect — transforms and transitions. Supported in all modern browsers since 2015.
Without CSS transition support, cards overlap and snap apart instantly on hover — no script crashes, the layout stays intact.
The code
HTML structure to paste into your page (CSS + JS available with a premium account):
<div class="card-stack-container">
<div class="card-stack-item">Card 1</div>
<div class="card-stack-item">Card 2</div>
<div class="card-stack-item">Card 3</div>
</div>
Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
| --bg-card | #12121a | CSS variable — fallback background color for cards. Replace with your brand card background if not using a gradient. |
| --radius-xl | 1rem | CSS variable — corner radius of each card. Increase (e.g. 1.5rem) for a rounder look, decrease (e.g. 0.5rem) for angular styling. |
| --border | rgba(255,255,255,0.08) | CSS variable — card border color and opacity. Raise the opacity (e.g. 0.20) for a more defined edge on light backgrounds. |
| translateX(±40px) | ±40px | Lateral spread distance (nth-child 1 and 3 lines in the :hover block). Increase to space cards further apart (e.g. ±60px for wider payment-card shapes). |
| rotate(±8deg) | ±8deg | Tilt angle of the side cards. Decrease (e.g. ±4deg) for a subtle effect, increase (e.g. ±14deg) for an expressive fan. |
| translateY(-10px) | -10px | Lift of the center card on hover. Set to 0 to remove it, increase (e.g. -20px) for a more pronounced rise. |
| transition: .4s cubic-bezier(…) | .4s / back-out | Duration and easing for all cards (transition line on .card-stack-item). Replace with 0.25s ease-out to remove the bounce, or 0.6s to slow it down. |
| background (nth-child) | indigo→fuchsia gradients | Each card has its own background on :nth-child(1/2/3). Replace with a solid color, brand gradient, or background-image for illustrated content. |
FAQ
:hover selector with transform and transition — no script needed. It integrates natively into React, Vue, Svelte, or any other framework without directives or hooks..card-stack-item elements inside the container and extend the :nth-child(n) selectors in the :hover block. Distribute rotations symmetrically (e.g. ±6° / 0° / ±12° for 4 cards) and adjust translateX values to avoid overlap.<input type="checkbox"> paired with a label fires the :checked state, which activates the transform values via an adjacent sibling selector. For more control (programmatic toggle, persistent state), one line of JS is enough: add or remove a class on the container on click.