Depth
Nine semantic shadow tokens across four categories — Surface, Floating, Container, Side panel — plus three control-level micro accents (shadow-thumb, shadow-thumb-lg, shadow-well) for seated thumbs and recessed wells in the filter rail. Each composition stacks 3-6 low-opacity layers (2-6% each) so the accumulation reads as natural light, not a single drop shadow. Direction matches the surface's relationship to its surroundings: directional for cards and side panels, all-sides for containers.
01Philosophy
Five principles that separate shadows that feel crafted from shadows that feel generated. Borrowed from the working notes of designers (Josh Comeau, Jakub Krawczyk) who've thought about this carefully.
Low opacity, many layers
Never use a single high-opacity shadow. Stack 3-6 layers at 2-6% each. Accumulation creates depth without harshness; a single 25%-opacity drop shadow reads as a Photoshop relic.
Progressive blur
Blur should increase as Y-offset increases. Light diffuses as it travels — shadows get softer the further they fall from the source. 2px → 4px → 8px → 16px → 32px is the canonical ladder.
Negative spread on outer layers
On the largest-blur layers, use negative spread (e.g. -4px, -8px) to keep the shadow from bloating outward. That contains the falloff while preserving softness.
The border-shadow
A 0 0 0 1px shadow at 6% opacity acts as a soft border that respects border-radius. Use this instead of a real border — same visual weight, no fight with the radius.
Context dictates direction
Cards on a page cast downward (directional). Containers within a page glow on all sides (symmetric). Side panels cast toward the body they overlap (directional left or right). Match the shadow to the relationship between surface and surroundings.
02The scale
Nine tokens, grouped by what kind of surface they're for. Each row shows the same surface on light (neutral-50) and dark (neutral-950) pages so you can see how the token flips across themes.
Surface
3 tokens
shadow-flat
--shadow-flat
light
dark
Truly inline content. No ring, no shadow.
Plain page text, headings, anything that's part of the document flow with no surface of its own.
shadow-hairline
--shadow-hairline
light
dark
Soft 1px border-shadow at 6% — respects radius.
The default for resting cards and list items. Replaces a real border with a subtle 1px shadow at 6% opacity — same visual weight, but doesn't fight the border-radius.
shadow-base
--shadow-base
light
dark
Directional lift — a card that's been singled out.
Cards designed to look raised over their siblings. Stat cards on a dashboard, hero cards, the active step in a wizard.
Floating
1 token
shadow-elevated
--shadow-elevated
light
dark
The universal lift for anything that floats above the page.
Use this for every transient + modal surface — popovers, dropdowns, hover cards, tooltips, menus, combobox results, AND dialogs, alert-dialogs, drawers, sheets. 6 progressive layers (1px ring → 2 → 4 → 12 → 24 → 40 px) at 2-4% opacity each create the natural light falloff. Size + position carry most of the elevation signal; the shadow accumulates softly.
Container
2 tokens
shadow-section
--shadow-section
light
dark
Symmetric all-sides glow, contained spread.
Inner page containers — sidebars detached from the chrome, grouped surfaces that hold a sub-region. 1px contact ring + symmetric layers, no Y offset.
shadow-page
--shadow-page
light
dark
Soft symmetric, atmospheric, no hard border.
Full-page wrappers floating on a neutral backdrop — the kind you see in centered-app layouts where the page itself feels like a card on top of the workspace.
Panels
3 tokens
shadow-panel-float
--shadow-panel-float
light
dark
Atmospheric all-sides — for free-floating panels.
Use for panels that aren't docked to a single edge — a floating tool tray, a bottom-mounted music player dock, an inspector card that follows your cursor. The 40px ambient layer carries the depth signal on every side.
shadow-panel-left
--shadow-panel-left
light
dark
Directional — for panels docked to the right edge.
Persistent side panels that live on the right of the workspace (e.g. inspector, activity feed). The shadow casts left toward the page body to anchor the panel's edge.
shadow-panel-right
--shadow-panel-right
light
dark
Directional — for panels docked to the left edge.
Persistent side panels on the left — navigation rails, file-tree drawers. The shadow casts right onto the main canvas so the panel reads as overlaid, not embedded.
03In context
Six destinations from the scale wired to real components: a resting Card (hairline), a singled-out stat (base), a Popover (elevated), a Dialog (also elevated — modals share the floating shadow), an embedded Section (section), and a right-docked panel (panel-left).
shadow-hairline · resting Card
Marcus Reed
QB · Class of 2027
2,847 yards · 24 TDs · 4 INTs over six straight wins.
shadow-base · singled-out stat
Conversion
73%
shadow-elevated · Popover
Filter by status
shadow-elevated · Dialog
Pull this offer?
The recruit will see this status change on next sync. You can re-open the offer later from the activity log.
shadow-section · embedded container
Kanban · Class of 2027
shadow-panel-left · right-docked Sheet
Activity
Marcus Reed
- Coach Patel logged a call · 14 min
- Status: Pending visit
- Film clips synced
04Hover transitions
When a Card is interactive, hairline → base on hover with a 160ms transition is the canonical move. The 1px translateY adds the optical lift; the shadow change carries the rest.
05Creating new shadows
When you need a shadow not covered above, follow this four-step process. Almost always: pick the closest existing token and adjust opacity / blur — don't reinvent the layer count.
Determine the elevation level
Sitting on the surface → base. Floating above (popovers, dialogs, drawers, sheets) → elevated. A container within a page → section or page. A side panel (left- or right-docked, or free-floating tool tray) → panel-left, panel-right, or panel-float.
Determine directionality
Cast downward → use Y offsets (down). Glow evenly → keep X and Y at 0 (all-sides). Docked to an edge → use X offset on the edge layer only.
Start with a template
Pick the closest existing token and adjust: more presence → bump middle-layer opacities (not the outer ones). More spread → larger blur, less negative spread. Less intense → reduce all opacities. Tighter → more negative spread.
Test on white and off-white
Shadows that look good on pure white may be invisible on neutral-100. Test the surface against the real backgrounds it'll live on, not just a stage.
06Dark mode
Shadows behave badly on dark surfaces — the contrast between dark and slightly darker is too low to register lift. The dark-mode tokens compensate with a brighter contact ring and a top-edge highlight, the same way real glass catches a window.
Black shadows on dark surfaces don't register — there's not enough contrast between a dark surface and a slightly darker shadow for the eye to read elevation. We don't fight that.
Dark-mode tokens lean on a brighter contact ring (one neutral step lighter than the page) plus a faint top-edge highlight— the kind of inset light you'd see on a glass surface near a window. The bottom shadows remain, but contribute less. Net result: a card reads as "made of a different material" than its background, which is what elevation actually communicates.
Dark preview
shadow-base
Lit by the top-edge highlight.
shadow-elevated
Lit by the top-edge highlight.
shadow-section
Lit by the top-edge highlight.
shadow-panel-float
Lit by the top-edge highlight.