Gallery✨ Premium

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.

CSS3DPerspective

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

Picking your character before a match — 3D Carousel example 1

① Picking your character before a match

WhenIn the Vexmorande card game, the ranked-match setup screen shows five characters as illustrated cards on the ring; on the right, a panel gives the front character's name and its strength, magic and agility gauges, with the Play button. Each notch of the ring changes the character on offer.
WhyDepth makes the choice concrete: the chosen character is in front, large and sharp, the others wait behind. The right panel only has to read which card is not inert to update.
SettingsStage format --c3d-ratio: 350/250, portrait cards (--c3d-card-ratio: 3/4, --c3d-card-w: 38%), an SVG illustration in an <img> + an HTML name per card, golden arrows (scene CSS). The scene updates the panel when the inert attribute changes; in the capture it clicks the real arrow every 0.9 s.
Autumn favourites in a bookshop's window — 3D Carousel example 2

② Autumn favourites in a bookshop's window

WhenOn the Feuillardine bookshop home page, an "Autumn releases" block shows five covers on the ring; on the left, the front book's title, a line from the team and a "Reserve in store" link. The covers move one notch with each click on the black arrows.
WhyFive covers fit in a block next to the text instead of a grid filling the whole screen; the front cover acts as the highlight, the ones behind make you want to turn.
Settings--c3d-ratio: 320/230, 3/4 portrait covers (--c3d-card-w: 32%), drop shadow and dark brown arrows (scene CSS). The title on the left follows the front cover. The capture clicks the real arrow every second.
Three subscription plans on mobile — 3D Carousel example 3

③ Three subscription plans on mobile

WhenIn the Orlivane (documentaries) sign-up flow, "Plan" step on a phone: three cards (Essential, Family, Yearly) on the ring, price and perk on each, Continue button at the bottom. You turn the ring with your thumb; the arrows are hidden.
WhyWith three cards, the ring shows one plan sharp in front and the other two behind: you compare without leaving the screen, and the gesture is enough on mobile.
SettingsThree cards (120° per notch), --c3d-card-w: 74%, --c3d-card-ratio: 4/5, arrows hidden by the scene CSS, front card made fully opaque (.c3d-card:not([inert]) { opacity: 1 !important }, see the FAQ) so the prices of the cards behind do not show through. The capture sends the real code a touch pointerdown then pointerup every 1.1 s; the white dot is a scene marker.

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-visible outline.
  • 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 are role="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-y on 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.

Chrome 104+✓ Full (measured)
Firefox 112+✓ Full (measured)
Safari 15.5+✓ Not verified (3D flattened in the test WebKit)
Edge 104+✓ Full (Chromium engine)
Mobile iOS 15.5+✓ Not verified
Android Chrome 104+✓ Full (touch measured)

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):

index.html — structure
<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 / propertyDefaultEffect
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

That's the original design: opacity follows depth and is about 0.67 in front, 0.43 on the sides, 0.06 at the back. For a fully opaque front card, add .c3d-card:not([inert]) { opacity: 1 !important }.
As many as you like: the step is 360° divided by the number of cards. Beyond eight, the back cards become almost invisible and overlap; three to six give the best depth.
Safari supports CSS 3D, but we couldn't verify it: the WebKit used in our tests flattens 3D transforms. Navigation (arrows, swipe) works there; check the rendering on a device before publishing.