Depth on a flat screen
Shadows suggest that one thing floats above another. Used with restraint, they tell the eye what is a card, what is a button and what is a pop-up. Used heavily, they make a page look dated and noisy. CSS has three shadow tools: box-shadow, text-shadow and the drop-shadow() filter.
box-shadow
box-shadow: offset-x offset-y blur spread color;<style>
body { background: #f1f5f9; padding: 16px; }
.card { background: white; padding: 20px; border-radius: 12px; margin-bottom: 20px; }
.s1 { box-shadow: 0 1px 3px rgb(0 0 0 / 0.2); }
.s2 { box-shadow: 0 8px 24px rgb(15 23 42 / 0.15); }
.s3 { box-shadow: 6px 6px 0 #1e293b; } /* hard "sticker" shadow */
</style>
<div class="card s1">Small and subtle</div>
<div class="card s2">Soft and lifted</div>
<div class="card s3">Hard offset, no blur</div>- offset-x / offset-y: how far right and down (negative values go left and up).
- blur: how soft the edge is.
0is a crisp copy; larger is softer. - spread (optional): grows or shrinks the shadow before blurring.
- color: nearly always a transparent dark colour such as
rgb(0 0 0 / 0.15), so it blends with any background.
The first two lengths are required. With a light source above, a small positive vertical offset and no horizontal offset looks natural.
inset
Add inset to draw the shadow inside the box. It is good for pressed buttons and hollow input fields:
.input { box-shadow: inset 0 2px 4px rgb(0 0 0 / 0.12); }
.btn:active { box-shadow: inset 0 2px 6px rgb(0 0 0 / 0.3); }Layered shadows
A single hard shadow rarely looks real. In life, a shadow is crisp and dark right under an object and wide and faint further away. Layer two or three comma-separated shadows:
.elevated {
box-shadow:
0 1px 2px rgb(0 0 0 / 0.08),
0 4px 8px rgb(0 0 0 / 0.08),
0 16px 32px rgb(0 0 0 / 0.1);
}Increase offset and blur together to show higher "elevation", for instance more for a dropdown than for a card. Pick a few levels and reuse them across the site, ideally as CSS variables (a later lesson).
Focus rings and glows
A shadow with no blur and a spread acts as a ring that follows rounded corners, and unlike outline it respects border-radius in every browser:
.field:focus-visible { box-shadow: 0 0 0 3px rgb(59 130 246 / 0.5); }If you replace the default outline, keep the new focus indicator clearly visible. Note that in Windows high-contrast mode, shadows are removed, so keep a transparent outline as a backup: outline: 2px solid transparent;.
text-shadow
text-shadow: 1px 2px 3px rgb(0 0 0 / 0.4);It takes offsets, a blur, and a colour (no spread). Use a faint shadow to lift text from a busy background. Heavy shadows and multiple glows reduce legibility and look amateurish. Never use a shadow to fix poor contrast; fix the colours.
drop-shadow for images and shapes
box-shadow always follows the rectangle of the element. A logo with a transparent background would get a rectangular shadow. The drop-shadow() filter follows the actual visible shape:
.logo { filter: drop-shadow(0 4px 6px rgb(0 0 0 / 0.25)); }It works on PNG, WebP and SVG with transparency, and on any element's rendered shape.
Things to remember
- Shadows take no layout space and do not affect neighbours. A big shadow can be clipped by a parent with
overflow: hidden. - Large blurred shadows on many elements, or animated shadows, can slow older devices. Animate
opacityortransforminstead, or fade a pseudo-element's shadow. - In dark themes, shadows are less visible; use subtle borders or lighter surfaces to show elevation.
- Never use a shadow as the only signal of a state (such as selected). Add colour, text or shape too.
Common mistakes
- Pure black, fully opaque shadows, which look harsh.
- Huge blur and spread values everywhere.
- Shadows on text that reduce contrast.
- Using
box-shadowon transparent images instead ofdrop-shadow. - Animating box-shadow on big lists.
Exercise
Create a card with a three-layer soft shadow that grows slightly on hover (use a transition on box-shadow), and a button with an inset shadow when pressed.
Show one possible answer
<style>
.card {
padding: 20px; border-radius: 12px; background: white; max-width: 280px;
box-shadow: 0 1px 2px rgb(0 0 0 / .08), 0 4px 8px rgb(0 0 0 / .08), 0 12px 24px rgb(0 0 0 / .08);
transition: box-shadow .2s;
}
.card:hover { box-shadow: 0 2px 4px rgb(0 0 0 / .1), 0 8px 16px rgb(0 0 0 / .1), 0 24px 40px rgb(0 0 0 / .12); }
.btn { padding: 8px 18px; border: 0; border-radius: 8px; background: #4f46e5; color: white; }
.btn:active { box-shadow: inset 0 3px 6px rgb(0 0 0 / .35); }
</style>
<div class="card"><p>Hover me.</p><button class="btn">Press</button></div>Key points
- box-shadow takes horizontal offset, vertical offset, blur, spread and colour; inset puts it inside.
- Subtle shadows look best, with a low-opacity colour, a soft blur and a small vertical offset.
- Several comma-separated shadows layered together look more natural than a single heavy one.
- text-shadow works on text, drop-shadow on the visible shape of an image, including transparent PNG and SVG.
- Shadows do not take up layout space, and they must not be the only thing that signals a state.