3D✨ Premium

3D Menu Stack

Four menu links stacked in a 600 px perspective parent, each a little further back and paler than the one before (0, −10, −20, −30 px; opacity 1 down to 0.7); the item that is hovered, tapped or reached with the keyboard moves 20 px forward, slides 10 px right, turns −5° and takes an indigo-violet gradient with a drop shadow, in 0.3 s — a list whose depth tells the order, in CSS only.

CSS3DMenu

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. 3D has 50 effects, including 8 free. Explore the category →

3 usage examples

The four stages of a try dive — a dive club's page — 3D Menu Stack example 1

① The four stages of a try dive — a dive club's page

When"Your first dive" menu: "3 m — getting started", "6 m — buoyancy", "12 m — the tour", "20 m — level 1"; each deeper stage sits further back and paler, and the one under the pointer comes up to the front with its length and place.
WhyThe gradual setback of the items is literally depth: the further down the list, the "further" the stage, and the chosen stage rises toward the diver.
SettingsRoot without background over a water-blue backdrop, --bg-card: #0c2c45; 272 px wide stack, 14 px × 18 px item padding; hover gradient #0ea5e9 → #2563eb; length and place on the right of each item, lit on hover. The scene moves the highlight from one stage to the next and yields to the mouse.
The four courses of a tasting menu — a fine-dining restaurant's site — 3D Menu Stack example 2

② The four courses of a tasting menu — a fine-dining restaurant's site

When"Amuse-bouche", "Seared scallops", "Roast pigeon", "Grand Marnier soufflé" in the order of the meal; the hovered course comes forward with its wine pairing (Champagne brut, Muscadet sur lie, Chinon 2019, late-harvest Vouvray).
WhyThe order of the meal reads in the stack; the course coming forward on hover gives a highlight without opening a page, and the wine pairing shows only for the course being looked at.
SettingsCream background, --bg-card: #2b211b, 15 px Georgia text; hover gradient burgundy #7f1d1d → gold #a16207, set on the card color; 348 px stack. On a light background, the items set back (70 to 90% opacity) look paler: that is the intended effect.
Four reachable departments — contact page of a 3,000-resident town hall — 3D Menu Stack example 3

③ Four reachable departments — contact page of a 3,000-resident town hall

When"Civil registry", "Planning", "Schools", "Municipal police" stacked; the hovered department comes forward and shows its phone number and opening hours.
WhyA short, ordered list that gains depth under the pointer: visitors see right away which department they are choosing, and the number comes with it.
SettingsLight institutional background, --bg-card: #ffffff, #cbd5e1 border, dark text switching to white on the blue #1e3a8a → #2563eb gradient (with no color transition, so white never sits on white); items widened to 340 px and placed on the capture mouse's path.

How it works

The root .fx-0009-root (#0a0a0f background, at least 300 px tall) defines three variables: --bg-card: #12121a, --border and --radius-md. It centers .menu-stack, a flex column (8 px gap) that carries perspective: 600px, and four links <a class="menu-stack-item">: block, bold 14 px white text, 12 px × 30 px padding, card background, thin border, 0.3 s transition on everything.

The depth is written item by item with :nth-child: the 1st stays at translateZ(0), the 2nd moves back 10 px at ×0.98 and 90% opacity, the 3rd to −20 px, ×0.96, 80%, the 4th to −30 px, ×0.94, 70%. The perspective adds to the scale: measured in Chromium and Firefox, the four items are 143, 138, 133 and 128 px wide, and the stack narrows toward the bottom like stairs seen from the front. On hover (or :focus-visible), the item takes translateZ(20px) translateX(10px) rotateY(-5deg), a #6366f1 → #8b5cf6 gradient, a shadow cast down and to the left, and z-index: 10. This transform replaces the one from the :nth-child line: the further back the item, the bigger its jump (20 px for the 1st, 50 px for the 4th). Measured: 4% of pixels change when hovering the 2nd item.

Two writing choices. The items are links: Tab reaches them, Enter follows them, and a real menu needs nothing more. The hover gradient sits on top of the card color (linear-gradient(…) var(--bg-card)): a gradient alone resets the background color to transparent, and the item losing hover would go through transparency for 0.3 s, which would make it fade on a light page. The items set back have an opacity below 1: that is intended (they fade with distance), but anything placed on them fades with them.

Accessibility

  • Reduced motion: under prefers-reduced-motion: reduce, a block scoped to .fx-0009-root keeps the transition on background and shadow only, and brings the chosen item to translateZ(0): it comes to the front at once, without sliding or turning; the gradient and shadow mark it. Measured in Chromium and Firefox: no position transition running, 3.5% of pixels change on hover (4% without the preference).
  • Keyboard: four links, reached with Tab; focus gives exactly the hover state (:focus-visible), plus the browser's focus ring. In Safari, Tab only stops on links when the "Press Tab to highlight each item" option is on (otherwise Option + Tab) — a browser setting, measured in WebKit.
  • Contrast: white text on #12121a at 18.6:1; the 4th item, at 70% opacity, keeps 9.4:1 on the original background. The hovered item drops to 4.47:1 on the indigo and 4.2:1 on the violet #8b5cf6, under the 4.5:1 threshold for bold 14 px text: darken the end of the gradient (#7c3aed gives 5.7:1).
  • Touch: with a finger, the tapped item takes the hover state (measured with Chromium touch emulation and in Firefox), then the link is followed. Put a real address in each href.

Browser compatibility

CSS only (perspective on the parent, transform on each item, :nth-child, :focus-visible), no dependencies. Measured in Chromium 151 and Firefox 153: same widths (143 down to 128 px), same hover, same reduced-motion behavior, 0 errors. Each item is transformed on its own inside its parent's perspective, with no nested 3D planes: Playwright's WebKit (26.5), which flattens preserve-3d scenes, renders this one like Chromium (2% difference, from text smoothing).

Chrome 86+✓ Full
Firefox 85+✓ Full
Safari 15.4+✓ Full (render measured in WebKit 26; not checked on a real Safari)
Edge 86+✓ Full
Mobile iOS✓ Hover on tap not checked on a real iPhone
Android Chrome✓ Full

Without :focus-visible (old browser), keyboard focus does not bring the item forward: the browser's ring remains. Without perspective, the items no longer move back but keep their scale-down (×0.98 to ×0.94) and their paleness.

The code

HTML structure to paste into your page (the CSS + JS come with your purchase):

index.html — structure
<div class="fx-0009-root">
  <div class="demo-3d">
    <div class="menu-stack">   <!-- perspective: 600px -->
      <a class="menu-stack-item" href="#">Menu Item 1</a>
      <a class="menu-stack-item" href="#">Menu Item 2</a>   <!-- −10 px, ×0.98 -->
      <a class="menu-stack-item" href="#">Menu Item 3</a>   <!-- −20 px, ×0.96 -->
      <a class="menu-stack-item" href="#">Menu Item 4</a>   <!-- −30 px, ×0.94 -->
    </div>
  </div>
</div>

One-time payment, no subscription — the code is yours forever

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Item setback (CSS — translateZ of the four :nth-child) 0, −10, −20, −30 px Bigger steps (0, −25, −50, −75 px) deepen the stack and narrow the bottom further; beyond −100 px the last items get small. A 5th item needs its own :nth-child(5) line.
Paleness (CSS — opacity of the :nth-child) 1; 0.9; 0.8; 0.7 Below 0.6 the last item's text gets hard to read on a light background. Set 1 everywhere for a stack that recedes without fading.
Hover motion (CSS — transform of :hover, :focus-visible) translateZ(20px) translateX(10px) rotateY(-5deg) rotateY(-10deg) stresses the opening-door feel; remove translateX for an item that moves forward without sliding.
Colors (CSS — --bg-card, --border and hover gradient) #12121a; rgba(255, 255, 255, .08); #6366f1 → #8b5cf6 Keep var(--bg-card) after the gradient: it is the color underneath, the one that avoids going through transparency when hover ends.
Perspective (CSS — .menu-stack) 600 px 300 px exaggerates the setback and the turn; 1,200 px nearly flattens them.
Duration (CSS — transition: .3s) 0.3 s The same duration is used both ways; 0.2 s gives a snappier menu.

FAQ

On hover, the item's transform is replaced as a whole: the 4th goes from translateZ(-30px) scale(.94) to translateZ(20px) with no scale, a 50 px move forward, while the 1st only moves 20 px. That is what makes a distant item "rise". For equal jumps, add each item's setback into its hover rule (for instance an extra translateZ(-10px) for the 2nd).
Yes: the items are <a> links, reachable with the keyboard, with the same relief on focus as on hover. Replace the href="#" with your addresses and, for main navigation, wrap the stack in a <nav aria-label="…">. Keep four to six items: beyond that, the last ones recede so much they get small.
Items 2 to 4 have an opacity of 0.9 down to 0.7: on the original dark background you only see a slight fade, on a light background they turn grayish. It is the sign of distance. If you want solid cards, set opacity: 1 everywhere and let the setback and scale-down tell the depth.