Box Shadow Generator — Why Your UI Looks "Flat Cheap"
A white card, box-shadow: 2px 2px 0 #000, and suddenly the mockup looks like Bootstrap 2012. Soft UI is not “more blur.” It is stacked light: ambient contact shadow plus a softer directional lift.
Anatomy of a believable shadow
| Layer | Role | Typical recipe |
|---|---|---|
| Ambient | Contact with surface | 0 1px 2px rgba(0,0,0,.06) |
| Key | Directional lift | 0 8px 24px rgba(0,0,0,.08) |
| Optional rim | Separation on dark bg | 0 0 0 1px rgba(255,255,255,.06) |
.card {
border-radius: 12px;
background: #fff;
box-shadow:
0 1px 2px rgba(15, 23, 42, 0.06),
0 8px 24px rgba(15, 23, 42, 0.08);
}
Tune stacks in a box shadow generator, then paste into tokens so every card matches.
Elevation scale (keep it boring)
:root {
--shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.06);
--shadow-md:
0 1px 2px rgba(15, 23, 42, 0.05),
0 6px 16px rgba(15, 23, 42, 0.08);
--shadow-lg:
0 2px 4px rgba(15, 23, 42, 0.04),
0 12px 32px rgba(15, 23, 42, 0.12);
}
Buttons at rest: sm. Dropdowns: md. Modals: lg. Map Figma “Elevation/2” to these tokens so marketing and app do not invent different blur languages.
Dark mode, print, and a11y
On near-black, translucent black shadows barely show. Reduce blur/opacity, lift the surface color, or add a hairline highlight:
.card--dark {
background: #16181d;
box-shadow:
0 0 0 1px rgba(255, 255, 255, 0.06),
0 10px 28px rgba(0, 0, 0, 0.45);
}
Shadows often vanish in print and Windows forced-colors mode — keep borders so cards do not dissolve. Focus rings need :focus-visible outlines, not elevation alone.
Performance and nesting
Huge spreads on hundreds of nodes hurt low-end phones. Prefer shadow on the elevated element, not every list row. Avoid stacking heavy shadows on parent and every child — double ambient looks dirty. Elevate the outer surface; keep inner chrome flatter.
Soft UI mistakes checklist
- Single hard offset with 0 blur
- Pure
#000at high opacity on white - Same token on dark and light
- Shadows on full-bleed flat sections
- Unique blur values on every card in one view
Subtle hover lift (translateY(-1px) + --shadow-md) reads as affordance; big jumps feel like bugs. Pair a 1px low-contrast border with soft shadow — many “premium” UIs need both. Ship a small elevation system, not a unique shadow per Figma frame.
Hover, drag, and modal stacks
Interactive elevations should move one step on the scale (sm → md), not jump to a unique blur. Dragging a card? Use md while held and snap back on drop. Modals should sit at lg (or a dedicated overlay token) so they clearly float above page cards — if the modal and the card share the same shadow, the hierarchy collapses.
Avoid animating blur radius on every frame; animate opacity of a pre-defined shadow class or a pseudo-element instead when you need motion.
Colored ambient shadows
Some brands tint shadows toward the primary hue (rgba(37, 99, 235, 0.15)) for glow cards. Use sparingly — tinted shadows on every element look like a neon theme. Reserve colored ambient for promotional hero cards; keep neutral shadows for dense app chrome.
If a designer hands you a screenshot with a huge soft halo, recreate it as two or three layers rather than one 0 40px 80px monster — then dial alpha down until it survives a gray background, not only a pure white artboard.
Focus rings vs shadows
Do not replace :focus-visible outlines with shadows alone. Shadows are decoration; focus rings are accessibility. Keep both jobs separate in the design system.