Materials
Elevation, radius, and glass edge fades for depth and scroll chrome.
Use elevation for floating surfaces. Pair with shadow-hairline or a border for edges — lift and edge are separate tokens. Use glass fades when content scrolls under sticky chrome.
Preview
Name
Usage
Overview
Materials cover how surfaces lift, how corners round, and how scroll edges soften. Shadows use elevation levels 100–400, exposed as shadow-sm through shadow-xl, plus a dedicated hairline edge. Radius runs from 2xs to 4xl. Glass pairs masked backdrop-blur-md with a surface tint gradient.
Shadow scale
Four elevation levels plus a hairline edge token.
| Class | Level | Usage |
|---|---|---|
| shadow-hairline | edge | 1px theme-aware edge without a separate border. |
| shadow-sm | 100 | Subtle lift — chips, thumbs, tight cards. |
| shadow-md | 200 | Popovers, tooltips, small floating panels. |
| shadow-lg | 300 | Cards, menus, and elevated surfaces. |
| shadow-xl | 400 | Modals, dialogs, and high-focus overlays. |
Component mapping
Pick elevation from the role of the surface, not from visual preference alone.
| Component type | Recommended shadow | Rationale |
|---|---|---|
| Switch thumbs, chips | shadow-sm | Barely lifted control chrome. |
| Tooltips, small popovers | shadow-md | Clear separation from the trigger. |
| Cards, menus, selects | shadow-lg | Default elevated panel depth. |
| Modals and dialogs | shadow-xl | Highest practical elevation for focus. |
| Flat cards and tables | border / shadow-hairline | Sit in the layout rather than above it. |
Shadow and hairline
Elevation and edge are separate tokens. Choose based on whether the surface floats or sits flat in the layout.
| Pattern | When to use |
|---|---|
| shadow-sm … shadow-xl | Elevated surfaces — cards, menus, dialogs, floating panels. |
| shadow-hairline | Theme-aware 1px edge when you want a crisp outline without a border token. |
| border border-border-primary | Non-elevated dividers, sidebars, tables, and flat cards-by-design. |
Prefer one clear edge treatment — hairline or border — unless the pattern needs both with shadow-lg.
Hover states
Increasing elevation on hover signals that a surface is interactive.
| State | Class | Usage |
|---|---|---|
| Default | shadow-sm | Resting interactive card. |
| Hover | hover:shadow-md | Subtle lift that reads as clickable. |
| Active | active:shadow-sm | Return to base elevation for press feedback. |
tsx
<div className="shadow-sm hover:shadow-md active:shadow-sm transition-shadow"> Card content </div>
Radius
Prefer these corner radii over arbitrary rounded-[…] values.
Preview
Name
Usage
| Class | Value | Usage |
|---|---|---|
| rounded-2xs | 0.125rem (2px) | Tight chips and dense UI. |
| rounded-xs | 0.25rem (4px) | Badges and compact controls. |
| rounded-sm | 0.375rem (6px) | Small controls and inputs. |
| rounded-md | 0.5rem (8px) | Buttons, inputs, and checkboxes. |
| rounded-lg | 0.625rem (10px) | Larger controls and tiles. |
| rounded-xl | 0.75rem (12px) | Cards, menus, and popovers. |
| rounded-2xl | 1rem (16px) | Elevated panels and sheets. |
| rounded-3xl | 1.25rem (20px) | Feature cards and heroes. |
| rounded-4xl | 1.5rem (24px) | Composer and large shells. |
| rounded-full | 9999px (full) | Pills, avatars, and switches. |
Nested radius
When a rounded child sits inside a padded, rounded parent, keep curves concentric: inner = outer − padding. Pick the nearest lower scale step when the result is not exact.
| Outer | Padding | Inner |
|---|---|---|
| rounded-md (8) | p-1 (4) | rounded-xs (4) |
| rounded-lg (10) | p-0.5 (2) | rounded-md (8) |
| rounded-xl (12) | p-1 (4) | rounded-md (8) |
| rounded-xl (12) | p-1.5 (6) | rounded-sm (6) |
| rounded-xl (12) | p-2 (8) | rounded-xs (4) |
tsx
{/* Select popup: rounded-md + p-1 → items use rounded-xs */} <div className="rounded-md border p-1"> <button type="button" className="rounded-xs px-2.5 py-1.5"> Apple </button> </div>
Glass
Soften content as it scrolls under sticky chrome. Stack a masked blur with a tint gradient — not a solid translucent bar. Prefer EdgeFade so top and bottom edges stay consistent.
Scroll the list — masked backdrop-blur-md and tint share one fade so the edge reads as glass, not a milky wash.
Name
Recipe
Usage
| Layer | Recipe | Role |
|---|---|---|
| Blur | backdrop-blur-md backdrop-saturate-150 | Frosted glass behind sticky edges and floating chrome. |
| Tint strong | from-surface/80 | Start of an edge fade — keeps content readable. |
| Tint soft | via-surface/40 | Mid stop before the edge becomes fully clear. |
| Edge top | EdgeFade edge="top" | Sticky headers and scroll tops (logo bars, page chrome). |
| Edge bottom | EdgeFade edge="bottom" | Scroll bottoms — keep last items clear with padding. |
Composition
Two absolute layers behind the chrome. Blur and tint share one mask so they ease out together — separate fade curves read as a milky wash. Tint uses from-surface/80 via-surface/40 to-transparent (or background-primary on the page canvas). Avoid overflow: hidden on ancestors of the fade or backdrop-filter may not sample the scrolling content.
tsx
import { EdgeFade } from "@/components/edge-fade" <div className="sticky top-0 z-10"> <div className="pointer-events-none absolute inset-x-0 top-0 -z-10 h-[calc(100%+0.75rem)]"> <EdgeFade edge="top" tone="surface" /> </div> <div className="relative px-5 pt-5 pb-4"> {/* sticky chrome */} </div> </div> {/* Bottom edge — pin to the scroll viewport */} <div className="pointer-events-none absolute inset-x-0 bottom-0 z-10 h-12"> <EdgeFade edge="bottom" tone="surface" /> </div>
Add bottom padding on the scroll content so the last items can clear the fade (about the same height as the bottom edge).
Usage guidelines
Do
- Use
shadow-sm–shadow-xlfor elevated surfaces - Increase elevation on hover (
shadow-sm→shadow-md) withtransition-shadow - Use
shadow-lgfor cards and menus,shadow-xlfor modals - Prefer radius tokens (
rounded-md,rounded-xl) over custom radii - Nest radii with
inner = outer − padding(e.g. select items inside a padded popup) - Use
borderorshadow-hairlinefor flat layout chrome - Use masked
backdrop-blur-mdplus a tint gradient for scroll edges — preferEdgeFade
Don't
- Don’t invent one-off multi-glow shadows outside the scale
- Don’t jump from
shadow-smtoshadow-xlon hover - Don’t apply elevation to inline text or icons alone
- Don’t use
rounded-[13px]when a scale step exists - Don’t reuse the parent radius on a padded child — curves will clash
- Don’t skip transitions when elevation changes — abrupt lifts feel broken
- Don’t cover titles or last nav items with an oversized edge fade — keep the fade short and pad the scroll content
- Don’t use a flat opaque bar when the intent is glass — always mask the blur and fade the tint to transparent