Installation
Host-owned composition. Illustrative demos
below use data-au-elev / --au-elev-*;
copy the install snippet into host CSS. Shallow UIs should keep
semantic --au-surface /
--au-surface-muted.
/* Nested elevation ladder (host copy). Light: shadow after step 2. Dark: lighter fills + layered shadow. */
:root {
--au-elev-1: #fafafa; --au-elev-2: #fcfcfc; --au-elev-3: #fff; --au-elev-4: #fff;
--au-elev-5: #fff; --au-elev-6: #fff; --au-elev-7: #fff; --au-elev-8: #fff;
--au-elev-shadow-1: none;
--au-elev-shadow-3: 0 0 0 1px rgba(0,0,0,.06), 0 1px 2px rgba(0,0,0,.04), 0 4px 12px rgba(0,0,0,.06);
--au-elev-shadow-5: 0 0 0 1px rgba(0,0,0,.08), 0 2px 4px rgba(0,0,0,.04), 0 8px 24px rgba(0,0,0,.08);
--au-elev-shadow-7: 0 0 0 1px rgba(0,0,0,.1), 0 4px 8px rgba(0,0,0,.05), 0 16px 40px rgba(0,0,0,.1);
}
:root[data-theme="dark"], html.dark {
--au-elev-1: #171717; --au-elev-2: #1e1e1e; --au-elev-3: #252525; --au-elev-4: #2c2c2c;
--au-elev-5: #333; --au-elev-6: #3a3a3a; --au-elev-7: #414141; --au-elev-8: #484848;
}
[data-au-elev] { background: var(--au-elev-bg); box-shadow: var(--au-elev-shadow); }
[data-au-elev="3"] { --au-elev-bg: var(--au-elev-3); --au-elev-shadow: var(--au-elev-shadow-3); }
[data-au-elev="5"] { --au-elev-bg: var(--au-elev-5); --au-elev-shadow: var(--au-elev-shadow-5); }
[data-au-elev="7"] { --au-elev-bg: var(--au-elev-7); --au-elev-shadow: var(--au-elev-shadow-7); }
/* Host rule: child overlay = parent level + 2 (page→3, dialog→5, menu-in-dialog→7). */
The problem
In light mode, shadow behind white surfaces signifies elevation. In dark mode, progressively lighter backgrounds do the work instead.
Fixed backgrounds break nesting: a dropdown that matches its dialog looks like one flat slab.
Same fill for dialog and menu — the menu disappears into the dialog.
Invite to your workspace
The solution
Three pieces: tokens, substrate level, and an elevated wrapper.
Components stay on semantic --au-surface*;
hosts apply the ladder when overlays nest.
Tokens
Eight bg/shadow pairs. Light flattens to white after step 2 (shadow carries elevation). Dark keeps adding lightness plus a layered shadow recipe.
Dark
Light
Substrate
Each container knows its level. A menu on the page, inside a popover, and inside a dialog should each land two steps above their substrate — without props threaded through every opener.
Elevated
Wrap a panel and set its level. The shadow recipe for that level stays; only the fill climbs, so a popover still reads as a popover three layers down.
Example
Invite dialog
Dialog at surface 5, role picker at surface 7 — no React
context. Host sets data-au-elev from the parent
substrate.
Correct: menu is two steps above the dialog fill.
Invite to your workspace
Host contract
-
Semantic tokens (
--au-surface,--au-surface-muted,--au-surface-hover) stay the default for shallow chrome. - When overlays nest (dialog → menu, card → popover), the host assigns levels: typically parent + 2.
-
dialogand future menus should not hard-code a single dark gray; they should inherit or receive a substrate level from the host. - Keep this ladder out of default component CSS unless a component contract explicitly depends on it.