Package Exports
- @aporialab/overdraw-audit
- @aporialab/overdraw-audit/browser
- @aporialab/overdraw-audit/heatmap
- @aporialab/overdraw-audit/site-awareness
- @aporialab/overdraw-audit/vite
- @aporialab/overdraw-audit/webpack
Readme
overdraw-audit
Built by Aporia Labs
Zero-dependency CLI that detects CSS glass effect overdraw — backdrop-filter: blur() stacking, opacity layering, and GPU cost estimation.
npx overdraw-audit https://linear.appNo browser installation required. Runs on Node 18+. Works in CI pipelines.
Usage
# Audit any URL
npx overdraw-audit https://your-site.com
# Simulate Retina display (DPR=2 → score ×4)
npx overdraw-audit https://your-site.com --dpr=2
# Simulate mobile viewport (375px wide)
npx overdraw-audit https://your-site.com --viewport=mobile --dpr=3
# CI mode — exits with code 1 if score ≥ 2.5 (CRITICAL)
npx overdraw-audit https://your-site.com --quiet
echo $? # 0 = OK, 1 = CRITICAL
# Machine-readable output
npx overdraw-audit https://your-site.com --json > report.json
# Compare multiple sites side-by-side
npx overdraw-audit linear.app vercel.com stripe.com
# Generate SVG badge for README
npx overdraw-audit https://your-site.com --badge > overdraw-badge.svg
# Generate full HTML report
npx overdraw-audit https://your-site.com --html=report.html
# Track changes over time
npx overdraw-audit https://your-site.com --save # save to .overdraw-history.json
npx overdraw-audit https://your-site.com --diff # save + show delta vs last run
npx overdraw-audit https://your-site.com --history # show all saved runs
# Get a browser console script for live analysis
npx overdraw-audit --bookmarkletExample output
🐙 Octopus Sentinel — Overdraw Audit
────────────────────────────────────────────────────────────────
URL: https://linear.app/
Title: Linear – The system for product development
CSS: 14 sheets + 1 inline → 24 glass rules found
Mode: static analysis (zero-dependency) · 1766ms
OVERDRAW SCORE
4.15 █████████████████░░░░░░░ EMERGENCY
TOP GLASS ELEMENTS
Selector Blur Opacity Risk
──────────────────────────────────────────────────────────────
.Tooltip_variant-glass__E2PFI 32px 0.90 2.34
.page_bar__2mvrR 12px 1.00 2.08 [overlay]
.Hero_PIWRapper__nI3Yf 10px 1.00 1.95 [overlay]
RECOMMENDATIONS
● 3 element(s) use blur ≥ 20px — consider reducing to 8–12px
● 9 overlapping glass layers detected — each one multiplies GPU costReal-world results
| Site | Glass Rules | Score | Level | Note |
|---|---|---|---|---|
| raycast.com | 18 | 17.86 | 🚨 EMERGENCY | blur up to 75px |
| github.com | 12 | 6.99 | 🚨 EMERGENCY | stacked overlays |
| linear.app | 24 | 4.15 | 🚨 EMERGENCY | hero + navbar blur |
| vercel.com | 8 | 8.02 | 🚨 EMERGENCY | blur 150px on decorative layers |
| stripe.com | 5 | 2.23 | ⚠️ WARN | moderate use |
| notion.so | 3 | 0.49 | ✅ OK | minimal glass |
| figma.com | 0 | 0.00 | ✅ OK | no blur effects |
Tested February 2026. DPR=1. Static CSS analysis.
Key finding: Sites using decorative blur layers (Vercel: 150px, Raycast: 75px) score
EMERGENCYeven at DPR=1. On a Retina MacBook Pro (DPR=2), Vercel's score reaches 32.08 — 13× above the critical threshold.
How it works
- Fetch — Downloads HTML + all linked CSS stylesheets (Node 18+ built-in
fetch) - Parse — Extracts CSS rules with
backdrop-filter: blur(),filter: blur(), andopacity- Resolves CSS custom properties (
var(--blur-heavy)→ actual px value) - Filters
@media printand mobile-only blocks (≤480px) to avoid false positives - Detects
@keyframesanimated blur — applies ×1.8 penalty (cannot be cached by GPU)
- Resolves CSS custom properties (
- Score — Maps each rule to a synthetic 32×32 grid using the overdraw formula:
weight = opacity × (1 + blur/20) [× 1.5 for will-change:filter or mix-blend-mode] weight ×= 1.8 for animated blur score = Σ(cell weights) / 1024 × DPR² - Report — Outputs severity level, top offenders, hot cells, and recommendations
Score levels
| Score | Level | Meaning |
|---|---|---|
| < 1.5 | ✅ OK | No action needed |
| 1.5–2.5 | ⚠️ WARN | Optimize heavy layers |
| 2.5–4.0 | 🔴 CRITICAL | Will cause jank on mobile (CI fails here) |
| ≥ 4.0 | 🚨 EMERGENCY | Severe GPU overdraw |
CI integration
# .github/workflows/perf.yml
- name: Overdraw Audit
run: npx overdraw-audit ${{ env.SITE_URL }} --quiet
# Exits 1 if CRITICAL or above → fails the step# Makefile
audit:
npx overdraw-audit https://staging.yoursite.com --dpr=2Options
| Flag | Default | Description |
|---|---|---|
--dpr=<n> |
1 |
Device pixel ratio (Retina = 2) |
--retina |
— | Shorthand for --dpr=2 |
--viewport=<v> |
desktop |
Viewport: mobile (375px) · tablet (768px) · desktop (1440px) |
--json |
— | JSON output for scripting |
--quiet |
— | Only score + exit code |
--badge |
— | Output SVG badge to stdout |
--html[=file] |
— | Save HTML report (default: overdraw-report.html) |
--save |
— | Append result to .overdraw-history.json |
--diff |
— | Save + show delta vs last saved run |
--history |
— | Show all saved runs for URL |
--compare |
— | Force comparison table (auto-enabled for multiple URLs) |
--bookmarklet |
— | Print browser console script |
--help |
— | Show help |
Live browser analysis
Static CSS analysis can't see computed layout. For accurate per-element results, use the browser console script:
npx overdraw-audit --bookmarklet
# → copy the script, paste in browser DevTools consoleThis runs the full OverdrawScanner in your browser with real computed styles
and getBoundingClientRect() geometry.
Limitations
- Static analysis: layout isn't computed, element positions are estimated from position/width CSS properties
- No JavaScript execution: dynamically injected styles (CSS-in-JS) may be missed
- CSS-only: inline styles on individual elements are not extracted
For precise results, use the --bookmarklet mode in your browser.
License
MIT — part of Octopus Sentinel