3D Carousel
Five cards set on a ring in perspective: the arrows or a drag turn the ring one notch and bring the next card to the front.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Gallery has 44 effects, including 6 free. Explore the category →
3 usage examples



How it works
The .c3d-stage holds the perspective; inside it, the .c3d-ring keeps 3D (transform-style: preserve-3d) and each .c3d-card is centred in the middle. For each card the script computes its angle on the circle (360° / number of cards × distance from the active card), then places it with translateX (sine) and translateZ (cosine, towards the back). The further a card is, the smaller (scale 0.5 to 0.83), more transparent and lower in the stack (z-index) it gets. Moving from one position to the next takes 0.6 s.
Nothing turns on its own: only the ‹ › arrows and a horizontal drag of more than 50 px (finger or mouse) move the ring one notch, looping. Clicking a card does nothing.
Sizes follow the stage width, read on every resize (ResizeObserver): radius = 40% of the width, perspective = twice the width, card = 32% of the width. At 495 px you get the original values back (200 px radius, 1000 px perspective, 160 px card); at 360 px everything shrinks in the same proportions and nothing sticks out of the screen. Each .fx-0167-root on the page has its own ring.
Accessibility
- Named arrows: real "Previous card" / "Next card" buttons, placed above the ring, with a
:focus-visibleoutline. - One active card: the four back cards are
inert; screen readers and the keyboard only reach the front card ("slide, 2 of 5"). - Roles: a named
aria-roledescription="carousel"region, cards arerole="group"; texts in the HTML, to translate. - Reduced motion: under
prefers-reduced-motion: reduce, the ring jumps straight to its new position without an animated rotation. - Touch: Pointer Events and
touch-action: pan-yon the stage: a horizontal drag turns the ring, the page still scrolls vertically.
Browser compatibility
CSS 3D (perspective, preserve-3d), the translate property (Chrome 104, Firefox 72, Safari 14.1), aspect-ratio, ResizeObserver and inert (Chrome 102, Firefox 112, Safari 15.5 according to MDN). Measured in Chromium 151 and Firefox 153 (Playwright): two rings with only the second one driven, reduced motion, 390 px without horizontal scrolling, finger swipe (Chromium), 0 errors. Playwright's WebKit flattens CSS 3D: navigation works there, but the depth rendering could not be checked for Safari.
Without JavaScript the five cards are stacked in the centre and the arrows do nothing. Without CSS 3D the cards stay flat in a row and navigation still works.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0167-root">
<div class="c3d-slider" role="region" aria-roledescription="carousel" aria-label="…">
<div class="c3d-stage">
<div class="c3d-ring">
<div class="c3d-card" role="group" aria-roledescription="slide" aria-label="1 of 5">…</div>
…
</div>
</div>
<button class="c3d-arrow c3d-prev" type="button" aria-label="…">…</button>
<button class="c3d-arrow c3d-next" type="button" aria-label="…">…</button>
</div>
</div>
One-time payment, no subscription — the code is yours forever
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Maximum width (CSS — --c3d-max-width) |
520px | The stage takes its container's width up to this limit; radius, perspective and cards follow. |
Stage proportions (CSS — --c3d-ratio) |
9 / 4 | Height of the area holding the ring. Raise it for portrait cards. |
Card width (CSS — --c3d-card-w) |
32.3% | As a percentage of the stage. Above 40%, side cards overlap more. |
Card format (CSS — --c3d-card-ratio) |
16 / 10 | 3/4 for book covers or playing cards. |
Card content (HTML — .c3d-card) |
5 gradients "Slide 1" to "Slide 5" | An <img> covers the card; text and buttons accepted. The ring adapts to the number of cards (360° / N per notch). |
Rotation duration (CSS — .c3d-card transition) |
.6s | How fast a card reaches its new place. |
FAQ
.c3d-card:not([inert]) { opacity: 1 !important }.