Welcome to Vanilla Breeze
This bell pulls live notifications from /go/notify/messages — the same contract documented at /docs/concepts/service-contracts/. Static articles like this one are the no-JS / no-backend fallback.
This bell pulls live notifications from /go/notify/messages — the same contract documented at /docs/concepts/service-contracts/. Static articles like this one are the no-JS / no-backend fallback.
How depth works in Vanilla Breeze: a themeable elevation ramp, the box-shadow vs drop-shadow distinction that trips most authors up, and the opt-in data-shadow effects (tint, material, shape, glow).
Depth in Vanilla Breeze is a token layer, not a component — shadows have no runtime behaviour to encapsulate, so they ship as CSS custom properties and a small attribute API. This page covers the one distinction authors get wrong (box-shadow vs drop-shadow), the elevation ramp, and the opt-in data-shadow effects.
box-shadow vs drop-shadowThis is the single most useful thing to know about shadows. They look similar but paint completely differently, and reaching for the wrong one is why a shadow on a rounded badge or a transparent logo comes out as an unexpected rectangle.
box-shadow | filter: drop-shadow() | |
|---|---|---|
| Paints | The border-box — always a rectangle (rounded by border-radius). | The element’s actual alpha — follows the real shape. |
| Use for | Cards, panels, popovers, raised surfaces. | clip-path shapes, transparent PNGs, SVG, text. |
| Composes with | Other box-shadows (comma-stacked layers). | Other filter functions. |
| VB hook | --shadow-* tokens, data-shadow="tint", data-shadow="material" | data-shadow="shape" |
/* WRONG — box-shadow paints a rectangle behind the clipped shape */.badge { clip-path: polygon(50% 0, 100% 100%, 0 100%); box-shadow: 0 4px 6px #0006; } /* ALSO WRONG — clip-path is applied after the filter, so the element clips its own drop-shadow away and nothing shows */.badge { clip-path: polygon(50% 0, 100% 100%, 0 100%); filter: drop-shadow(0 4px 6px #0006); } /* RIGHT — clip the child, drop-shadow the (unclipped) parent */.badge { clip-path: polygon(50% 0, 100% 100%, 0 100%); }.badge-shadow { filter: drop-shadow(0 4px 6px #0006); } /* wraps .badge */
A transparent PNG, SVG, or text element carries its own alpha, so drop-shadow goes directly on it. Only clip-path needs the wrapper, because the clip would otherwise cut the shadow it just generated.
Six themeable levels live in src/tokens/shadows.css. The default theme uses layered, “photographic” ramps at the higher steps — a tight contact stop plus progressively softer ambient stops — so raised surfaces read with real depth instead of one flat blur.
--shadow-xs: 0 1px 2px 0 oklch(0 0 0 / 0.05);--shadow-m: 0 4px 6px -1px oklch(0 0 0 / 0.1), 0 2px 4px -2px oklch(0 0 0 / 0.1);--shadow-2xl: 0 1px 2px 0 oklch(0 0 0 / 0.05), 0 4px 8px -2px oklch(0 0 0 / 0.06), 0 10px 20px -4px oklch(0 0 0 / 0.08), 0 20px 36px -8px oklch(0 0 0 / 0.11), 0 34px 58px -12px oklch(0 0 0 / 0.14);
Reference the tokens directly — box-shadow: var(--shadow-m).
| Token | Use |
|---|---|
--shadow-xs … --shadow-2xl | The elevation scale, lowest to highest. |
--shadow-inner | Inset shadow for wells and pressed states. |
--shadow-flush | Hairline + soft drop, an alternative to border: 1px solid on raised surfaces. See Feel. |
--shadow-none | Explicit no-shadow reset. |
data-shadow effectsOpt-in, composable, theme-aware effects — the depth-layer sibling of data-border-effect. They read VB tokens, so they re-skin with the active theme, and they live in @layer bundle-effects so they win over a component’s own box-shadow. No JavaScript, no custom element.
| Token | Effect |
|---|---|
tint | Luminous, accent-tinted drop shadow derived from --color-accent. Re-tints with the theme. |
material | Rests at --shadow-m, raises to --shadow-xl with a translateY(-4px) lift on hover/focus. |
shape | Alpha-true drop-shadow for transparent PNG, SVG, or text — not the border-box rectangle. For a clip-path shape, put it on a wrapper (a clipped element clips its own shadow). |
glow | Soft accent halo. Static by default; add flicker for a gentle pulse. |
<article data-shadow="tint">Accent-tinted card</article> <button data-shadow="material">Lifts on hover</button> <!-- transparent image carries its own alpha: apply shape directly --><img src="/logo.png" alt="…" data-shadow="shape" /> <!-- clip-path shape: apply shape to a wrapper of the clipped element --><span data-shadow="shape"><span class="clipped-badge"></span></span> <aside data-shadow="glow flicker">Pulsing accent halo</aside>
Motion is gated. material keeps its elevation feedback under prefers-reduced-motion: reduce but drops the lift transform and transition. glow is static by default; its flicker pulse only runs under prefers-reduced-motion: no-preference, so the static halo is the reduced-motion fallback.
tint and glow keep their color-mix transparency at or above ~50% so the hue survives over textured surfaces; pushed more opaque, the colour muddies.
The elevation ramp followed by all four effects (move the pointer over the material card to see it raise):
shadow-wcSurface (bg-wc) and edge (border-wc) earn web components — SVG/canvas overlays, perimeter geometry, resize refitting. Depth needs none of that: box-shadow and drop-shadow paint with the box and scale with it automatically. A component would wrap nothing, so depth ships as tokens and this attribute layer instead.