Glassmorphism✨ Premium

Glass Depth Stack

Three glass plates stacked like steps (×0.82, ×0.91, ×1) that slide under the pointer at three amplitudes, 8, 16 and 26 px: the back layers move less than the front one, depth reads in the gap — CSS, variables and 1.3 KB of JavaScript, frozen when the user reduces motion, solid when they reduce transparency.

CSS 3DParallaxbackdrop-filter

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Glassmorphism has 47 effects, including 6 free. Explore the category →

3 usage examples

Skin, muscles, bones — an anatomy app for medical students — Glass Depth Stack example 1

① Skin, muscles, bones — an anatomy app for medical students

WhenThe student revises the right knee: over the X-ray plate, three stacked layers — bones, muscles, skin — with the landmarks to palpate on the front layer.
WhyThe parallax separates the planes like a dissection: moving the mouse, the student sees the skin slide more than the muscles, and the muscles more than the bone, and remembers the order of the layers.
SettingsStack enlarged to 330 × 236 px, on the right; one tint per layer (pale blue bone, red muscle, flesh skin) replacing the white gradient; a named tab in the band each back layer shows; knee bones as sharp shapes behind the glass, plate grid.
Backdrop, flats, proscenium — a theatre company's website — Glass Depth Stack example 2

② Backdrop, flats, proscenium — a theatre company's website

WhenA new production's page: the three planes of the set as glass layers in front of the red curtain, title, running time and dates on the proscenium.
WhyStage scenery is made of planes: the parallax reproduces what the audience sees when changing seats in the hall, the backdrop barely moves and the proscenium follows the eye.
SettingsBackdrop tinted orange to violet, flats green, proscenium nearly white; curtain with sharp folds (14, 12 and 8 px stripes) and a slatted floor behind the stack; 22 px serif title, dates as pills.
A metal payment card shown in relief — an online bank's offer page — Glass Depth Stack example 3

③ A metal payment card shown in relief — an online bank's offer page

WhenThe prospect discovers the premium plan: the steel card on the front layer, limits and cover on the back layers, the price and the account-opening button alongside.
WhyDepth gives weight to the offer without video or heavy images; the glass lets the brand's green and gold bands through, and each back layer sums up one argument in one line.
SettingsBack layer tinted gold, middle aqua; a 150 × 95 px card in a brushed gradient on the front layer in display: flex; sharp brand bands at 120° behind the stack, masked on the left by a gradient to keep the text readable.

How it works

A 16/10 scene (.gds-scene, 14 px corners, a midnight-blue radial gradient, perspective: 900px) holds three blurred pink, cyan and violet blobs, then the stack .gds-stack: min(70%, 320px) wide at 4/3, i.e. 320 × 240 px from a 458 px frame upwards. Inside, three .gds-layers overlap with inset: 0, 22 px corners, the same glass — a white gradient from 16% to 5%, a white rim at 22%, backdrop-filter: blur(var(--gds-blur, 6px)) saturate(150%). They differ by three variables: scale --s (0.82 / 0.91 / 1), opacity (55 / 75 / 100%) and amplitude --d (8 / 16 / 26 px). The text lives on the front layer.

The motion fits in one line: transform: translate3d(calc(var(--px, 0) * var(--d)), calc(var(--py, 0) * var(--d) + var(--lift, 0%)), 0) scale(var(--s, 1)), smoothed by transition: transform .12s ease-out. The script listens to pointermove on every .gds-scene of the page (several stacks possible), computes the pointer position from −0.5 to 0.5 and writes --px and --py between −1 and 1, reversed: pointer on the left, layers to the right. Measured 20 px from the corners in Chromium, Firefox and WebKit: ±7.6 / 15.2 / 24.7 px horizontally; when the pointer leaves, everything returns to 0. The layers do not come closer in depth (the Z component is 0): depth comes from scale and the amplitude gap, the front layer slides three times as much as the back one, like a foreground seen from a car.

The stack's settings aim at one thing: keeping all three layers visible. Shrunk to 82 and 91% and centered, the back layers would stay entirely hidden under the front layer at every pointer position (their edges always inside the front's, the 18 px amplitude gap never making up for the 29 px margin): the --lift variable (−22% for the back, −11% for the middle) raises them like steps. At rest the back sticks out by 31 px and the middle by 16 px; pointer top left, 47 and 25 px; bottom right, 15 and 7 px. .gds-stack does not declare transform-style: preserve-3d: in Firefox, a parent set that way whose child is transformed cancels its backdrop-filter (measured with real screen captures over a striped background), and since the layers stay at Z = 0 it would add nothing. The .gds-solid fallback is set by the script when the user reduces transparency or the browser ignores backdrop-filter; Safari before 18 reads the -webkit- form. The script walks through every .gds-scene: several stacks coexist, each with its own --px and --py; remove the gdsScene and gdsStack ids when you duplicate the block, the script does not use them. An older variant of this stack also exists in the category.

Accessibility

  • Reduced motion: under prefers-reduced-motion, the script ignores the pointer — measured in all three engines, layers still at both corners. The steps stay visible, depth reads without moving.
  • Reduced transparency and no blur: the script sets .gds-solid on the scene when prefers-reduced-transparency: reduce matches or when CSS.supports knows neither backdrop-filter nor its prefixed form. Measured under emulation in Chromium: solid layers (#1b2142, #222a52, #14162a), no more blur, desaturated blobs. Firefox 153 and WebKit 26 do not know this transparency setting yet: the fallback only applies there if blur is missing.
  • Contrast: the white text on the front layer holds thanks to its drop shadows (black text-shadow at 40 and 55%). Without them, measured on the front layer: 2.62:1 median, 2.22:1 on the lightest area, when the pink blob passes underneath. Keep the shadows, or darken the front layer if the text gets longer.
  • Touch and keyboard: the parallax only responds to a hovering pointer. With a finger, swiping scrolls the page and the browser stops tracking; with a keyboard, nothing moves. No content depends on the motion: the three layers and their text stay readable at rest. If the back layers carry information, also give it a place in the front layer's text or in the page.

Browser compatibility

CSS (variables, calc(), translate3d, backdrop-filter) + 1.3 KB of JavaScript, no dependency. Measured in Chromium 151, Firefox 153 and WebKit 26: same amplitudes, same return to zero, same stop under reduced motion; the 6 px blur shows in all three, as the stack does not use preserve-3d (real screen capture for Firefox and WebKit, whose automated captures never show backdrop-filter). -webkit-backdrop-filter covers Safari before version 18. pointermove events exist since Chrome 55, Firefox 59 and Safari 13.

Chrome 76+✓ Full
Firefox 103+✓ Full
Safari 13+✓ Full (-webkit- prefix)
Edge 79+✓ Full
Mobile iOS 13+✓ Layers visible, parallax only on hover (mouse, trackpad)
Android Chrome 76+✓ Layers visible, parallax only on hover

Without backdrop-filter (Firefox before 103 for instance), the script sets .gds-solid: three solid midnight-blue layers, white text, steps and parallax intact. Without JavaScript, the layers stay stepped at rest, with no parallax and no automatic fallback. With no hovering pointer (phone, tablet), the still steps are enough to read the three planes.

The code

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

index.html — structure
<div class="demo-preview">
  <div class="gds-scene">
    <div class="gds-bg">
      <span class="gds-orb gds-orb--1"></span> … (3 blobs)
    </div>
    <div class="gds-stack">
      <div class="gds-layer gds-layer--back"></div>
      <div class="gds-layer gds-layer--mid"></div>
      <div class="gds-layer gds-layer--front">
        <div><h3>…</h3><p>…</p></div>
      </div>
    </div>
  </div>
</div>
Unlock the full code — $9.90/mo incl. tax

The full HTML + CSS + JS of Glass Depth Stack, ready to paste — plus all 840 effects. Or $299 once, lifetime access.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Amplitudes (CSS — --d on .gds-layer--back, --mid, --front) 8 / 16 / 26 px The gap between layers creates depth: 4 / 12 / 30 px emphasizes it, 6 / 8 / 10 px flattens it. Beyond 40 px for the front, the layer leaves the scene at the corners.
Steps (CSS — --lift) −22% (back), −11% (middle), as a percentage of the layer height Layers stick out by 31 and 16 px at rest for a 240 px height. 0% hides them under the front again. For a sideways stack, add a horizontal offset in the first term of translate3d.
Scale and opacity (CSS — --s, opacity) 0.82 / 0.91 / 1 and 55 / 75 / 100% Closer scales (0.94 / 0.97) give a deck of cards; wider ones (0.7 / 0.85), a tunnel. Too low an opacity makes the back layer invisible on a dark background.
Blur per layer (CSS — --gds-blur) 6px for all three layers All three layers blur the same. For a blur that grows towards the front: --gds-blur: 3px on the back, 6px on the middle, 12px on the front; each layer also blurs the ones behind it.
Layer tint (CSS — background of each .gds-layer--…) the same white gradient from 16% to 5% One tint per layer helps name them: linear-gradient(135deg, rgba(255,120,110,.22), rgba(200,60,60,.08)) for a red layer. Keep opacity under 35% so the blur stays visible.
Sensitivity (JS — var amp = 1) 1: layers at full amplitude at the scene edge 0.5 halves the motion. To follow the pointer instead of opposing it, remove the minus sign in front of nx and ny.
Smoothness (CSS — transition: transform .12s ease-out) 0.12 s 0.3 s gives a stack that floats behind the pointer, 0 s a stack glued to the gesture. The same duration applies to the return to zero (pointerleave).

FAQ

The scene declares perspective: 900px, but the Z component of translate3d is 0: no layer moves towards the screen. Depth is a parallax illusion — smaller scale and slower motion for what is far —, the same as in a platform game. For real 3D, give each layer a Z (translate3d(…, …, -60px) for the back, -30px for the middle) and put transform-style: preserve-3d back on .gds-stack; beware, Firefox then loses all layer blur (measured), and Chromium computes each layer's blur from what lies behind it in the plane, not in 3D. Flat parallax is the compromise that works everywhere.
Both stack three glass plates, but depth does not read the same way. Layered Glass layers three fixed plates, in pure CSS, whose blur grows from one plate to the next (8, 16 then 28 px): the thickening glass tells what is in front, and only a slight grow on hover animates it. Glass Depth Stack separates its layers in parallax: they slide under the pointer at 8, 16 and 26 px, with 1.3 KB of JavaScript, and the gap in motion gives the depth. Choose Glass Depth Stack when the gesture is part of the reading (exploring strata, a set); Layered Glass for still depth, with no script, identical by finger and keyboard.
The steps at rest: three layers sticking out by 31 and 16 px, readable without motion. The parallax only starts on hover; a sliding finger scrolls the page, and the browser then stops tracking the pointer. To animate the stack on mobile, read the device tilt (deviceorientation, which needs permission on iOS) and write the same --px / --py between −1 and 1 — the CSS does not have to change.