Elevation
DraftElevation provides cues about the surface depth and stacking order of elements in an experience.
- Usage
- Tokens
- Code
- Status & changelog
Common alternative names
Shadows, shadow tokens, surface tokens, depth, z-index
Usage
A component's elevation is communicated with a shadow. This is determined by its elevation (z-index) and its relationship to other surfaces. Some everyday use cases for shadows are:
- Elements laid on top of a map
- A sheet overlaying a map
- A dialog overlaying a screen
- A snackbar overlapping content on a screen
- A button dock with content overflowing behind it.
Don't use shadows to distinguish a component's boundary unless it is elevated above the main surface. For example, don't use a shadow around a banner or card; use color or borders instead.
Shadows shouldn't be used to indicate that something is tappable or can be scrolled but can be used to indicate a component is being dragged or picked up from its original position.
Shadows should NOT flip in dark mode.
Types
Shallow
Shallow shadows are used for most use cases. They provide subtle depth for sticky headers, footers, and docked toolbars.
| Token | Value | Use case |
|---|---|---|
--shadow-shallow-above | 0px -4px 16px rgba(0, 0, 0, 0.12) | Sticky footers, bottom-docked toolbars |
--shadow-shallow-below | 0px 4px 16px rgba(0, 0, 0, 0.12) | Sticky headers, top nav bars |
Deep
Deep shadows are available when you have a component with a darker background. It's difficult for the eye to distinguish a shadow behind darker elements, for example, on a Snackbar or Tooltip.
| Token | Value | Use case |
|---|---|---|
--shadow-deep-above | 0px -16px 48px rgba(0, 0, 0, 0.22) | Bottom sheets, drawers sliding up |
--shadow-deep-below | 0px 16px 48px rgba(0, 0, 0, 0.22) | Top drawers, docked panels |
Shadow scale
In addition to directional shadows, Arch UI provides a six-step primitive shadow scale from barely-there (xs) to dramatic (2xl). Each step roughly doubles the blur radius and offset of the one before it.
Choosing the right shadow level
| Surface type | Recommended token | Why |
|---|---|---|
| Page content (flat) | None | Ground-level surfaces need no shadow |
| Cards at rest | --shadow-component-sm | Subtle lift to separate from background |
| Cards on hover | --shadow-component-md | Increased depth signals interactivity |
| Dropdowns and menus | --shadow-overlay | Must feel clearly above page content |
| Modals and dialogs | --shadow-overlay | Floating layer with scrim backdrop |
| Tooltips | --shadow-component-md | Small surface, moderate lift is enough |
| Sticky headers | --shadow-shallow-below | Directional shadow avoids visible edges on sides |
| Bottom sheets | --shadow-deep-above | Strong upward shadow for dramatic entrance |
Inner shadow
An inset shadow used for pressed states, input fields, or recessed surfaces.
Shadow and motion
Avoid animating shadows on elements that do not change elevation. Shadow transitions have a rendering cost and should only fire when the depth relationship genuinely changes.
Z-index layering
Z-index controls stacking order when elements overlap. Arch UI defines a fixed set of z-index tokens with generous gaps between tiers, so components never compete for position.
700600500400300200100100-1Stacking contexts
Remember that z-index only works within a stacking context. A z-index: 700 tooltip inside a z-index: 100 dropdown will not float above a z-index: 400 modal. Key rules:
- Creating a stacking context:
position+z-index,transform,opacity < 1,filter,will-change, andisolation: isolateall create new stacking contexts. - Keep contexts flat: Avoid nesting stacking contexts unnecessarily. Portals (rendering at the document root) are the preferred pattern for modals, toasts, and tooltips.
- Use portals for high-tier elements: Any component using
--z-semantic-modalor above should render via a portal to escape parent stacking contexts.
Do / Don't
box-shadow: var(--shadow-component-sm);Use semantic tokens so shadow intensity can be tuned globally.
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.16);Hardcode box-shadow values or use primitives directly in component CSS.
box-shadow: var(--shadow-shallow-below);Use directional shadows on sticky elements so shadow only casts in the scroll direction.
box-shadow: var(--shadow-lg);Applying an omnidirectional shadow on a sticky header creates unwanted glow on the sides.
z-index: var(--z-semantic-modal);Use semantic z-index tokens and render high-tier elements via portals.
z-index: 9999;Magic numbers bypass the layering system and create stacking conflicts.
Shadow tokens
Primitive shadow scale
| Token | CSS variable | Value |
|---|---|---|
| shadow-xs | --shadow-xs | 0 1px 2px 0 rgba(0, 0, 0, 0.05) |
| shadow-sm | --shadow-sm | 0 1px 4px hsla(0, 0%, 0%, 0.16) |
| shadow-md | --shadow-md | 0 2px 8px hsla(0, 0%, 0%, 0.16) |
| shadow-lg | --shadow-lg | 0 4px 16px hsla(0, 0%, 0%, 0.16) |
| shadow-xl | --shadow-xl | 0 8px 24px hsla(0, 0%, 0%, 0.16) |
| shadow-2xl | --shadow-2xl | 0 16px 48px hsla(0, 0%, 0%, 0.22) |
Directional and special shadows
| Token | CSS variable | Value |
|---|---|---|
| shadow-inner | --shadow-inner | inset 0 2px 4px 0 rgba(0, 0, 0, 0.05) |
| shadow-shallow-above | --shadow-shallow-above | 0px -4px 16px rgba(0, 0, 0, 0.12) |
| shadow-shallow-below | --shadow-shallow-below | 0px 4px 16px rgba(0, 0, 0, 0.12) |
| shadow-deep-above | --shadow-deep-above | 0px -16px 48px rgba(0, 0, 0, 0.22) |
| shadow-deep-below | --shadow-deep-below | 0px 16px 48px rgba(0, 0, 0, 0.22) |
Semantic shadow tokens
| Token | CSS variable | Resolves to |
|---|---|---|
| shadow-component-sm | --shadow-component-sm | {shadow.sm} |
| shadow-component-md | --shadow-component-md | {shadow.md} |
| shadow-overlay | --shadow-overlay | {shadow.xl} |
Z-index tokens
Primitive z-index
| Token | CSS variable | Value |
|---|---|---|
| z-hide | --z-hide | -1 |
| z-base | --z-base | 0 |
| z-raised | --z-raised | 10 |
| z-dropdown | --z-dropdown | 100 |
| z-sticky | --z-sticky | 200 |
| z-overlay | --z-overlay | 300 |
| z-modal | --z-modal | 400 |
| z-popover | --z-popover | 500 |
| z-toast | --z-toast | 600 |
| z-tooltip | --z-tooltip | 700 |
Semantic z-index
| Token | CSS variable | Resolves to | Use in component CSS |
|---|---|---|---|
| z-semantic-dropdown | --z-semantic-dropdown | {z.dropdown} (100) | Select, Combobox, MenuButton |
| z-semantic-sticky | --z-semantic-sticky | {z.sticky} (200) | StickyHeader, Dock |
| z-semantic-overlay | --z-semantic-overlay | {z.overlay} (300) | Overlay, Scrim |
| z-semantic-modal | --z-semantic-modal | {z.modal} (400) | Modal, Dialog, Drawer |
| z-semantic-popover | --z-semantic-popover | {z.popover} (500) | Popover, FloatingPanel |
| z-semantic-toast | --z-semantic-toast | {z.toast} (600) | Toast, Snackbar |
| z-semantic-tooltip | --z-semantic-tooltip | {z.tooltip} (700) | Tooltip |
Shadow transitions
When an element changes elevation — for example a card lifting on hover — transition the box-shadow property to make the change feel physical.
.card {
box-shadow: var(--shadow-component-sm);
transition: box-shadow 200ms ease-out;
}
.card:hover {
box-shadow: var(--shadow-component-md);
}
Status & changelog coming soon.