Role-based shadow tokens and how Box, Card, and popup surfaces apply them.
@prepared911/ui/styles.css as --elevation-* custom properties (vendored from Chroma). React exposes the five Box roles through Elevation on Box and FlexBox. Card uses CardElevation for its optional resting shadow.
After the Chroma elevation flip, each role token aliases a level stack (xs through xl, plus inset and drawer edges).
Role tokens resolve to multi-layer stacks in system.css. The table lists the alias each role uses today and typical consumers.
| Role | Elevation | Token | Resolves to | Typical use | Consumers in @prepared911/ui |
|---|---|---|---|---|---|
| Flat | None | — | none | List rows, dense tables, chrome with no lift | Default Box / Card |
| Sunken | Sunken | --elevation-sunken-shadow | --elevation-inset-shadow | Inset wells and recessed tracks | Box / FlexBox elevation only |
| Raised | Raised | --elevation-raised-shadow | --elevation-sm-shadow | Cards and panels resting on the page | Box, Card (CardElevation.Raised), Accordion, ChannelList (raised chip) |
| Overlay | Overlay | --elevation-overlay-shadow | --elevation-md-shadow | Tooltips, popovers, compact popups, chat chrome | Box, Popover, Tooltip, ChatInput, ChannelList (overlay panel), ChartFrame tooltip, popup-surface-overlay recipe |
| Modal | Modal | --elevation-modal-shadow | --elevation-xl-shadow | Blocking dialogs and modal shells | Box, Dialog (modal-surface recipe), popup-surface-modal recipe |
--elevation-floating-shadow (aliases --elevation-md-shadow). Toasts use --elevation-toast-shadow (aliases --elevation-lg-shadow). Neither is a Box Elevation value; components reference them in CSS.
| Token | Resolves to | Typical use | Consumers |
|---|---|---|---|
--elevation-floating-shadow | --elevation-md-shadow | Menus, listboxes, floating toolbars | Menu, Listbox, Toolbar (floating variant), ChatScroll jump control, Select / Combobox list surfaces |
--elevation-toast-shadow | --elevation-lg-shadow | Toast stack above page chrome | Toast |
| Token | Purpose | Consumers |
|---|---|---|
--elevation-resting-shadow | Selected pill in segmented controls | SelectionPill (used by SegmentedControl, ToggleGroup) |
--elevation-thumb-shadow | Switch and slider thumbs | SwitchTrack, Switch, Slider |
--elevation-bubble-shadow | Slider value bubble (dark theme region) | SliderBubble |
--elevation-layer-{xs,sm,md,lg,xl,drawer,inset} (per-mode rgba). --elevation-edge-color and --elevation-edge-inset-color paint flat rings and inset borders; --elevation-hairline-border composes the default hairline. Do not compose ad hoc shadows from removed --elevation-{xs..xl}-color ramp names; request a token upstream instead.
| Token | Light (default) | Dark ([data-theme='dark']) |
|---|---|---|
--elevation-layer-xs | rgba(17,17,20,0.06) | rgba(0,0,0,0.12) |
--elevation-layer-sm | rgba(17,17,20,0.05) | rgba(0,0,0,0.10) |
--elevation-layer-md | rgba(17,17,20,0.04) | rgba(0,0,0,0.08) |
--elevation-layer-lg | rgba(17,17,20,0.035) | rgba(0,0,0,0.07) |
--elevation-layer-xl | rgba(17,17,20,0.03) | rgba(0,0,0,0.06) |
--elevation-edge-color | rgba(17,17,20,0.05) | rgba(255,255,255,0.08) |
--elevation-edge-inset-color | rgba(17,17,20,0.06) | rgba(255,255,255,0.10) |
--elevation-soft-color-* steps in system.css still back resting, thumb, and bubble tokens until a later Chroma cleanup removes unused steps.
Pass elevation={Elevation.<Role>}. Background stays on background; elevation only sets box-shadow.
CardElevation.None (default) keeps a Surface card flat. CardElevation.Raised adds --elevation-raised-shadow on CardVariant.Surface only; Ghost accepts None at the type level.
tailwind/recipes/popup-surface.css and modal-surface.css. Use @apply in colocated component CSS, not ad hoc utility strings in className (see Portal usage).
popup-surface-shell: border, radius, overlay fill, motion hooks; under forced-colors: active, border color becomes CanvasText.popup-surface-overlay: shell + --elevation-overlay-shadow.popup-surface-modal: shell + --elevation-modal-shadow.modal-surface: shell + modal motion + --elevation-modal-shadow (Dialog).Drawer paints one of four edge tokens on the sliding panel. Drawer stacks use --elevation-layer-drawer (same geometry as the lg level, anchored per edge):
| Token | Applied on |
|---|---|
--elevation-drawer-inline-start-shadow | inlineStart panel (leading inline edge) |
--elevation-drawer-inline-end-shadow | inlineEnd panel |
--elevation-drawer-block-start-shadow | blockStart panel |
--elevation-drawer-block-end-shadow | blockEnd panel |
block-start casts down from a top-anchored panel and block-end casts up from a bottom-anchored panel.
Listbox paints --elevation-drawer-block-end-shadow on a pinned footer divider when footerShadow is enabled.
On the default dark theme, shadows are subtle against --color-bg-default; pair raised surfaces with BorderColor.Subtle or BorderColor.Default when fill and page are close in value.
Under forced-colors: active, system colors replace token fills and shadows no longer carry the edge. Shared popup shells keep a 1px border-style: solid edge (popup-surface-shell uses var(--border-width-default) and var(--color-border-default); forced colors switch the border to CanvasText). Surfaces that normally rely on shadow alone should still expose a border in that mode (Toast clears box-shadow and keeps a CanvasText border; Tooltip adds a CanvasText border).
Elevation is not a focus indicator. Pair interactive surfaces with the focus-ring recipes.
Elevation does not replace z-index. Portaled chrome still uses --z-index-* tokens. Band order for overlays (bottom → top): overlay → modal → popover → tooltip. .pr-tooltip sits at calc(var(--z-index-tooltip) + 100) so tooltips paint above popovers when those tokens share a numeric value.
Do
Elevation from the surface role (sunken well, raised card, overlay menu, modal dialog).CardElevation.Raised only on CardVariant.Surface.popup-surface-* and modal-surface recipes in component CSS.--shadow-1…--shadow-5 or Elevation.Low / Medium / High; they are removed.