At a glance
CSS bytes downloaded but not applied to the rendered page, ranked per stylesheet. Lighthouse’s Coverage analysis identifies which CSS rules never matched any element during the audit; the bytes those rules occupy are “unused CSS”. Brands routinely ship 30-60 percent of their CSS bundle on every page with only 40-70 percent actually applied. Smaller impact than psi_unused_js but easier to fix, tree-shaking unused CSS is largely automatic with modern build tools.
| What it counts | Per CSS resource: total bytes, used bytes (rules that matched DOM elements), unused bytes (rules that did not match). Lighthouse uses Chrome’s CSS Coverage API to instrument which selectors matched during the audit; the difference between downloaded and matched is “unused”. |
| Sample type | Lab data from a single Lighthouse audit. |
| Why bundles accumulate unused CSS | (1) Theme stylesheets ship rules for every variant: blog templates, multi-language layouts, demo content, alternate skins. (2) Component libraries (Bootstrap, Tailwind base, BC theme styles) ship base rules that may never apply on the merchant’s customised pages. (3) Removed features leave dead rules: a theme update removes a widget but leaves its CSS rules. (4) Per-component CSS bundled site-wide: PDP-specific styles shipped on the homepage; cart-drawer styles shipped on every page including pages without cart action. |
| Common ecommerce patterns | (1) Stencil theme bundle: 130KB total, ~60-80KB unused per page. (2) Klaviyo popup CSS: 30KB total, ~25KB unused on pages without popup. (3) Chat widget CSS: 20KB total, ~15KB unused on most page-loads (chat invisible until hovered). (4) Cookie consent banner CSS: small bytes but present on every page. |
| Optimisation playbook | (1) PurgeCSS or UnusedCSS during build: scan templates, remove unmatched rules. Saves 30-60 percent of theme CSS. (2) Critical CSS inline: extract above-the-fold CSS, inline in HTML, defer the rest. Reduces render-blocking even when the deferred CSS still has unused rules. (3) Per-template CSS bundles: split theme CSS by template; each page loads only its own bundle plus shared core. Modern bundlers handle this via dynamic imports or media attributes. (4) Lazy-load widget CSS: chat widget, popup, cookie banner CSS load when widgets activate, not at page-load. |
| Smaller impact than unused JS | CSS parse + execute is faster than JS parse + execute. The same 100KB of CSS costs 50-150ms on mobile; 100KB of JS costs 200-500ms. Unused CSS is mostly a payload concern (download time, bandwidth); unused JS is also an execution-cost concern. Address unused JS first; tackle unused CSS when JS work is mature. |
| Currency | n/a, list of byte-savings opportunities. |
| Time window | T (current state). |
| Alert trigger | top entry > 50KB unused (red), > 25KB unused (amber). |
| Sentiment key | null |
| Roles | owner, operations |
Calculation
Calculated automatically from your Website Performance (PageSpeed + CrUX) data. See the At a glance summary above for what the metric tracks and the worked example below for a typical reading.Worked example
A UK-based BigCommerce fashion store homepage audit, mobile, Wednesday 15 May 26.| Rank | Resource | Total bytes | Used bytes | Unused | Coverage % | Recommendation |
|---|---|---|---|---|---|---|
| 1 | stencil-bundle.css (theme) | 134KB | 52KB | 82KB | 39% | PurgeCSS to remove unused theme rules |
| 2 | klaviyo-popup.css | 28KB | 4KB | 24KB | 14% | Defer load; popup styles unused on most page-loads |
| 3 | tidio-chat.css | 22KB | 6KB | 16KB | 27% | Defer chat widget CSS to post-load |
| 4 | web-fonts.css | 12KB | 8KB | 4KB | 67% | Inline critical font-face declarations |
| 5 | cookie-consent.css | 8KB | 6KB | 2KB | 75% | Acceptable; small bytes |
| Total CSS shipped | 204KB | 76KB | 128KB | 37% used |
- 63 percent of all CSS shipped is unused on this page-load. Smaller proportion than the typical 66 percent unused JS but still a meaningful 128KB of dead bytes downloaded and parsed.
- Stencil theme bundle at 82KB unused (rank 1) is the dominant fix. Stencil themes ship rules for many use cases (blog templates, multi-language layouts, alternate skins, demo content) that the merchant doesn’t use. PurgeCSS during build scans the actual theme templates, removes unmatched rules, typically cuts 40-60 percent of theme CSS bundle size.
- Klaviyo popup CSS at 24KB unused (rank 2) is loaded synchronously on every page even though the popup fires only on certain triggers. Defer the load to when the popup is about to mount; saves 24KB on most page-loads where the popup never shows.
- Tidio chat at 16KB unused (rank 3) has similar pattern, chat widget styles loaded site-wide but only ~27 percent of rules apply on a typical page. Defer to post-load; the chat is invisible until hovered anyway.
- Web fonts CSS at 4KB unused (rank 4) is mostly applied. Tightening this is polish; not worth significant effort.
- Cookie consent at 2KB unused (rank 5) is acceptable. Skip.
- Cumulative impact post-fixes: PurgeCSS theme work (-50KB) + Klaviyo defer (-24KB) + Tidio defer (-16KB) = roughly 90KB reduction. Total CSS bytes drop from 204KB to ~115KB, with most CSS deferred to post-load. Page-weight reduction is meaningful; render-blocking impact is the bigger win because deferred CSS no longer blocks first paint.
- Read top 3 entries. Theme bundle is almost always #1; third-party widget styles next.
- Verify in DevTools Coverage tab. Confirms unused-CSS estimates and shows exactly which rules never fired.
- Apply build-time tree-shaking (PurgeCSS, UnusedCSS) for first-party CSS.
- Defer third-party CSS loads that don’t need to render at first paint.
| Time horizon | Action |
|---|---|
| First 1 hour | Identify top 3 entries. |
| First 7 days | Configure PurgeCSS in build pipeline; tree-shake theme bundle. |
| First 14 days | Defer third-party widget CSS (Klaviyo, chat). |
| Re-audit | Confirm CSS bytes dropped as expected. |
Sibling cards merchants should reference together
| Card | Why merchants reach for it |
|---|---|
psi_unused_js | Same pattern for JavaScript; higher impact, prioritise first. |
psi_render_blocking | Render-blocking CSS overlaps with unused CSS; deferring helps both. |
psi_total_weight | Total page weight; CSS bytes contribute. |
psi_top_opportunities_bytes | Composite byte-savings ranking. |
psi_top_opportunities_ms | Composite millisecond-savings ranking. |
psi_perf_score_summary | Composite score; CSS work indirectly supports LCP improvement. |
crux_lcp_p75 | LCP improves when render-blocking CSS is deferred. |
psi_third_party_cost | Third-party widgets ship CSS that often dominates the unused-CSS list. |
Reconciling against the vendor’s own dashboard
Where to look:- PageSpeed Insights, “Reduce unused CSS” opportunity in the Diagnostics section.
- Chrome DevTools → Coverage tab, line-level visibility of which CSS rules matched DOM elements; the most useful diagnostic tool.
- PurgeCSS / UnusedCSS / CleanCSS, build-time tools that automate the cleanup.
| Reason | Direction | What to do |
|---|---|---|
| Audit vs interaction. Lighthouse measures unused CSS during synthetic page-load; pages with rich interactivity (hover states, modal opens, dynamic states) may match more rules in real use. | Vortex IQ may over-report | Use DevTools Coverage with manual interaction for fairer measurement. |
| Detection threshold. Lighthouse drops opportunities under a few KB; very small unused-CSS savings don’t appear. | Vortex IQ matches Lighthouse | Add manual checks for small-CSS patterns if needed. |
psi_render_blocking, psi_total_weight, psi_third_party_cost).
Quick rule for support tickets: if a merchant says “I removed unused rules from my CSS but Vortex IQ still shows the same unused bytes”, the most common cause is build/bundle caching, the published bundle still contains the old rules. Verify the deployed CSS file matches the source.