Foundations
Elevation
Seven levels from flat to sheet. Every shadow in the system is directional and dual-layered — a spread shadow for depth, a tight shadow for edge definition.
Scale
Seven named levels
The production bundle confirms --shadow-card, --shadow-elevated, --shadow-inset, --shadow-list, and --shadow-rgb as named tokens. Concrete box-shadow values extracted below.
noneNo elevation. The default state for every surface — cards, rows, inputs at rest.
Body text, list rows, inputs (unfocused)
--shadow-insetA 1px inset ring that distinguishes a surface from its parent without lifting it. Used for bordered inputs and chip outlines.
Text Field border, Chip / Tag outline
--shadow-cardSubtle two-layer shadow that lifts a card off the page without competing with its content. The lightest true elevation.
Cards, Popovers resting, inline suggestions
--shadow-listDropdown and flyout menus sit one level above the page surface. The second shadow leg anchors the shadow under the menu edge.
Menus, Dropdowns, Autocomplete lists, Context Menus
--shadow-elevatedFull-featured modal elevation. The wide spread shadow creates depth; the 1px ring clarifies the modal edge against any background.
Modals, Action Sheets, Activity Views, Popovers (active)
--shadow-8Bottom sheets cast their shadow upward — the inverse direction signals that the surface rises from below rather than floating above.
Bottom sheets, Drawers, Sheet panels
--shadow-focusKeyboard focus uses an outline, not a shadow — but for components where outline clips the design, a ring box-shadow is the documented alternative.
Focused inputs, focused buttons when outline clips
Shadow composition
Two-layer shadows, one composable base
Instagram's shadow system builds from --shadow-rgb (the base colour) rather than hard-coding black. This lets dark mode shift the shadow hue without changing the structure.
/* Shadow base */
--shadow-rgb: 0, 0, 0;
/* Named elevation levels */
--shadow-inset: 0 0 0 1px rgba(var(--shadow-rgb), .08) inset;
--shadow-card: 0 1px 4px rgba(var(--shadow-rgb), .10),
0 0 0 .5px rgba(var(--shadow-rgb), .06);
--shadow-list: 0 2px 8px rgba(var(--shadow-rgb), .15),
0 1px 1px rgba(var(--shadow-rgb), .10);
--shadow-elevated: 0 2px 26px rgba(var(--shadow-rgb), .30),
0 0 0 1px rgba(var(--shadow-rgb), .10);
--shadow-8: 0 -6px 16px rgba(var(--shadow-rgb), .18);[data-theme="dark"] {
--shadow-rgb: 0, 0, 0;
/* Optional: increase alpha slightly to compensate for dark bg */
}Layer order
Z-index paired with elevation
The production bundle confirms --layer-1 through --layer-10 as named z-index tokens. Elevation and layer always move together — a higher shadow level must sit on a higher layer.
default (0)Flat / HairlinePage, rows, inputs--layer-1CardCards, inline chips--layer-2ListMenus, dropdowns, tooltips--layer-8ElevatedModals, dialogs, sheets--layer-10Focus ringActive focus on top of allUsage
Do / Don't
Do
- Use --shadow-elevated for any surface that interrupts the main flow (modals, action sheets, popovers).
- Pair z-index with shadow level — a surface with --shadow-list must also sit on --layer-2 or higher.
- In dark mode, rely on surface colour contrast first; shadow provides secondary depth cues.
Don’t
- Invent custom box-shadow values — the two-layer shadow grammar is specific and any one-off creates visual inconsistency.
- Use elevation on flat list rows or body text — shadow at that level reads as interactive.
- Forget the upward shadow for bottom sheets — 'box-shadow: 0 -Npx' is intentional; a downward shadow contradicts the panel's origin.