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-md: 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-md). Numeric aliases --shadow-1…--shadow-6 map onto the same scale for Open Props compatibility. Themes override these tokens, so each pack carries its own shadow language (brutalist hard offsets, claymorphism’s inset highlights, and so on) without any component changes.
| 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-md, 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.