Skip to main content
All articles

Developer

What Box-Shadow Values Real Websites Actually Use

We downloaded the homepages of the top 1,000 sites and counted every box-shadow in their CSS: 33,438 declarations across 533 reachable sites. Measured offsets, blur, spread, color, and layering — with the dataset to download.

9 min readSep 16, 2026GainUtility

533

sites crawled with usable CSS

21894

total shadow layers measured (px)

5596

distinct usable values

80.1%

of sites ship a real box-shadow

If you write CSS, you write box-shadow eventually — and you usually guess what looks right. This time the values are measured, not guessed. We took the top 1,000 domains from the Tranco list (pinned 2026-09-15), downloaded each homepage, parsed every box-shadow declaration in its shipped CSS, and counted what's actually in production.

The whole pipeline is reproducible: the list is pinned, the crawler and analyzer scripts live in this repo, and every measured value per site is downloadable as a CSV. No number in this article is hand-picked to make a point.

533

sites crawled with usable CSS

21894

total shadow layers measured (px)

5596

distinct usable values

80.1%

of sites ship a real box-shadow

How the measurement works

Each site's homepage was fetched over https with http fallback. From the served HTML we kept inline <style> blocks and linked stylesheets, up to 30 sheets per site or 8 MB per sheet, following inline @import one level deep. Sheet contents were parsed with the CSS tree parser; only box-shadow and the -webkit-box-shadow vendor twin were extracted.

Of 1,000 sampled domains, 533 served a homepage we could parse. The rest are accounted for, not hidden: 222 domains had no DNS record, 85 answered with an HTML bot-wall (403), 20 failed TLS hostname checks, 47 timed out, and the remainder split across 4xx/5xx statuses, connection resets, and certificate errors. Note the list includes shell-style pages and app entry points with no CSS at all — 28 of the 533 sites shipped no stylesheet to parse.

A declaration was counted when it appeared in the sheet. Identical -webkit-box-shadow and box-shadow pairs on the same rule collapse to one entry (3,531 such vendor twins), and values were deduplicated per site before the distinct-value counts. Shadows written entirely by JavaScript are out of scope by design.

The headline numbers below are px-space measurements: layers whose offsets, blur, and spread are all in px, laid out by the browser default font-size of 16px for typographic units (7 layers used rem and are reported separately). 3,216 declarations using other non-px units were measured for truthfulness but excluded from the pixel statistics.

Almost every top site ships a real shadow

427 of 533 parsed sites — 80.1% — ship at least one resolvable box-shadow on the homepage. 'none' exists too: 390 sites reset the effect somewhere, and with 5,301 none declarations it is the single most common value, ahead of every literal shadow. That reset is normal: frameworks null out a default box-shadow before adding their own, and some effects you see on the page come from elements the type selector misses.

From the 533 sites we collected 33,438 raw declarations, collapsing to 29,907 after dedupe and the vendor-twin merge. The distinct value set is 9,168 strings, and 5,596 of those are fully usable px values (or implemented in px after resolving their custom properties).

Where the shadow sits

Site shadows are anchored to the box: 93.1% of the 21,887 measured layers use an X offset of exactly 0. Nothing drifts sideways in production CSS — even the most elaborate framework tokens keep X pinned.

Y is a real choice with two modes. 48.5% of layers sit exactly at y=0, and 50.2% push down by a small amount — the 75th percentile vertical offset is 3px. Upward shadows, the glow-below look, are a rounding error at 1.3% of layers. If you color an element but the background of the page is what should glow, write negative y — otherwise site data says: down a little, or straight down.

Vertical offset (Y) across 21,887 measured layers

Where the shadow is placed vertically

034926984104761396803059

y offset in px — 0 is level with the box, negative values read upward

Almost half of all layers sit exactly at 0. When a site does offset, it is downward and small — the busiest bin after 0 is 1–2 px.

The blur is a lot smaller than the demos

Starter-kit demos blur at 10–25px. Production does not: 39.8% of layers ship blur 0 — those are the hard edges of focus rings and thin underlines — and among soft shadows the median blur is 3px, with the 75th percentile at 10px and the 90th at 24px.

The blur and the y-offset move together: when a site softens a shadow, it does so with a compact radius, not a fog bank. Values past ~40px blur exist but are the top of the tail.

Blur radius across 21,887 measured layers

How much the shadow is softened

0241348267239965202549+

blur in px

39.8% of layers use blur 0 — those are focus rings and thin borders, not glows. Among soft shadows, single-digit blurs dominate: the 75th percentile is 10 px.

Spread, inset, and the focus ring pattern

Spread defaults to none: 69.7% of layers carry spread 0. Positive spread (24%) mostly manufactures hard borders — the ring look — while negative spread (6.4%) shrinks the shadow to hug the box edge. Only 12.7% of layers are inset, and those cluster in a recognizable family: top-INSET pressed-button styles such as the Bootstrap classic inset 0 3px 5px rgba(0,0,0,.125).

What you actually see in the top values is the ability of spread to fake a border. The two most-shipped literal shadows in the whole sample are focus rings: 0 0 0 2px #fff (27 sites) and inset 0 0 0 2px #fff (18 sites) — white rings that read as borders without a border.

Spread and inset across 21,887 measured layers
No spread
15,255 · 69.7%
Positive spread
5,253 · 24%
Negative spread
1,401 · 6.4%
Inset shadows
2,780 · 12.7%

Color: neutral, almost never opaque

The palette reads as one color: black at low opacity. 73.8% of measured layers are achromatic, and 74.9% have alpha at 0.3 or below, with the busiest bands at near-zero alpha (the largest single bucket is at or below 0.05) and at 0.1–0.15. Exactly-opaque shadows are rare — 21.3% of layers — and even those are mostly rings and utility borders, not depth shadows.

The syntax split is now hex-first: 11,453 layers (52.3%) write their color as hex such as #0000001a, and 9,585 (43.8%) as rgba(). The hex-in-alpha form carries the Tailwind token stream; HSL and named colors are negligible. Browsers default an omitted color to currentcolor, and that shows up too — 0.5% of layers lean on the default.

Shadow opacity across 21,887 measured layers
>0..0.05
4,604
0.05..0.1
2,750
0.1..0.15
3,477
0.15..0.2
2,031
0.2..0.25
728
0.25..0.3
1,154
0.3..0.4
885
0.4..0.5
628
0.5..0.7
465
0.7..1
357
exact1
4,625

One shadow, not a stack

A single layer is the default: 73.5% of the 14,517 shadowed declarations we could resolve use exactly one layer. Two layers cover 14.3%, and three or more just 12.2%. The stacks that do exist are token-driven, not art direction — the count climbs at 5–6 layers because Tailwind's ring utilities ship an inset-shadow plus a ring-offset-shadow plus a ring-shadow and their defaults as separate layers.

The tail is real but tame: one declaration reached 60 layers and another 142, both framework-table flowers that effectively render as archipelagos of nearly invisible rings.

Layers per declaration (of 14,517 resolvable declarations)
1 layer
10,670 · 73.5%
2 layers
2,071 · 14.3%
3+ layers
1,776 · 12.2%

The most-shipped values

This table ranks values by how many of the 533 sites ship them. The var() bundles are Tailwind's ring set and Bootstrap's button focus; the literals are dominated by ring-style hard shadows and small rgba drops. Material Design's canonical three-layer stack ships on 10 sites, and Google's two-layer variant on 9 — the stacks above are company templates, not the web-wide norm.

In the dataset, a row's 'declarations' column is how many times the value appeared across sheets; 'sites' is how many distinct domains placed it. kind records how we classified it: ok means the value stands as-is, global means it inherits meaning like inherit or unset, and varPartial marks values still holding a custom property after our resolution.

The most-shipped shadows, by number of sites where the value appears
ValueSitesDeclarations
none3905218
unset60143
var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)30874
0 0 0 2px #fff27105
var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)26496
var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)20134
inset 0 0 0 2px #fff1855
inherit1629
inset 0 0 0 1px #fff1537
0 0 0 1px #0000001a,0 1px 3px #0000001a1414
0 0 3px #33361414
0 0 0 1px #fff1315
0 0 0 0 transparent1318
01214
inset 0 1px 1px rgba(0,0,0,.075)1236
0 5px 15px rgba(0,0,0,.5)1212
0 .5rem 1rem rgba(0,0,0,.15)1214
0 0 1em #fff1117
0 1px 1px rgba(0,0,0,.05)1111
0 .125rem .25rem rgba(0,0,0,.075)1114
0 0 3px 1px #3898ec1117
0 1px 0 rgba(0,0,0,.15)1019
0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12),0 2px 4px -1px rgba(0,0,0,.2)1010
0 2px 10px rgba(0,0,0,.2)1011
inset 0 3px 5px rgba(0,0,0,.125)1026

A third of sites route shadows through custom properties

9,248 of the 33,438 raw declarations — 27.7% — reference a var(), and they spread wide: 260 of the 533 sites. Framework tokens are the reason: Tailwind accumulates five vars per ring declaration, and Bootstrap resolves its shades into --bs-*-box-shadow on the button.

We resolved 2,444 var() declarations to concrete pixels using only values the site itself defined: a same-rule definition first, then the sheet's :root, html, and universal selectors, then the fallback written inside the var() itself. Across those declarations, the variable lookups that succeeded split as 1,888 walked a root-level variable, 999 walked a rule-level variable, and 999 used the fallback inside the var(). A declaration can chain several lookups, so those three numbers add up to more lookups than declarations. The remainder stays honestly labelled var()-opaque — we do not guess at a site's runtime value.

The verdict table

Eight popular claims about box-shadow, graded against the measured set. 'Verdict' is GainUtility's conclusion — the numbers are yours to check.

ClaimWhat sites do (measured)Verdict
Blur heavily for a soft modern lookMedian blur 3 px; 75th percentile 10 px; 39.8% of layers blur 0Only for big glows
Stack two–three layers like Material73.5% of declarations are a single layer; 3+ layers are 12.2%Only for design tokens
Offset down-and-right for depthOffset X is 0 on 93.1% of layers; Y is 0 on 48.5% and down on 50.2%Offset down — or not at all
Neutral color, low opacity73.8% achromatic; 74.9% at alpha ≤ 0.3; busiest bands ≤ 0.05 and 0.1–0.15Correct
Inset shadows are the depth stapleInset: 12.7% of layers, mostly pressed-button systemsA deliberate choice, not the default
Upward (negative-Y) glow is commonNegative Y offset: 1.3% of layersRare

Six claims about box-shadow, graded against 21,887 measured layers.

Limits you should know before quoting this

  • Coverage: 533 of 1,000 sampled domains resolved to a parseable homepage. The 467 gaps are enumerated in the methodology, not smoothed over — expect a western/heavy-platform skew in what remains.
  • Homepage only: marketing pages over-represent component kit CSS and under-represent the apps behind them. A <style>-less shell on the landing page (28 sites) says nothing about the product's tab styles.
  • Resolution: values resolved through custom properties only use what the site defined. A var() left opaque stays opaque — we count it under var()-opaque, excluded from px statistics.
  • Sheet caps: 30 sheets and 8 MB per sheet bound the crawl; styles layered in above those caps are absent. Served vs. computed: eases such as transition are out of scope, and a few effects are injected by JS toggling classes with id-less selectors we trimmed.
  • Date: the sample is a single day (list pinned 2026-09-15) and CSS moves; re-run the scripts for a fresh slice.
Check the numbers yourself

Every measured value, per site: box-shadow-values.csv (columns domains, value, declarations, kind), regenerated from the pinned Tranco list.

The crawler and analyzer used to collect it live in the repo under scripts/: collect-box-shadow.mjs and analyze-box-shadow.mjs.

Download the dataset (CSV)

How to use this in a design

Start every shadow from 0 0 (X and Y both zero). Move Y down by 1–4 px at most, keep blur in single digits for regular raises, and leave X alone. Write the color as black at an alpha between 0.08 and 0.3 — the measured sweet band — instead of a tint. Add a second layer only when a component token demands it, and treat inset like the pressed-state tool it is.

Two value shapes are worth adopting, because production validates them: the accessible focus ring 0 0 0 2px #fff (and its inset twin for filled backgrounds) — one of the most-shipped shadows in the whole sample — and the soft one-layer raise 0 1px 2px rgba(0,0,0,.1), which ships verbatim on sites like TinyURL and OpenStreetMap. The data ranks convention, not taste: a conventional shadow is what users read as normal, and that is the point of these defaults.