Abdul Ahad QureshiFullstack Engineer
← Index Case 03 / 05 · 2026 · Live on the Chrome Web Store

Naked CSS.

Role

Designed, built and launched it
Extension, landing site, growth

Used by

209 people, rated 5.0 on the Chrome Web Store

Stack

TypeScript, Chrome Manifest V3, Vite, SVG overlay, PostHog, Next.js on Vercel

Live

naked-css.vercel.app ↗
Add it to Chrome ↓

Naked CSS banner: See every pixel, with a sample pricing page where a selected card shows its selector and size and a 24 pixel distance to the next card.
Fig. 05Measuring on a live page
Overview

A Chrome extension that turns any live page into something you can measure and inspect, so designers and developers can audit layouts without opening DevTools.

Chapter 01 / 09The start

A 4px padding, one DevTools panel at a time

Design QA means checking a live build against the Figma file. Is this gap 24 or 20? Is this the brand purple, or a hex someone pasted? Chrome DevTools answers one element at a time, in a side panel, in computed values.

Nothing showed the relationship between two elements, which is what a designer actually asks about, and nothing said which design token a colour came from.

Chapter 02 / 09The goal

Answer design questions on the page itself

I wanted a tool a designer could use without ever opening DevTools, and wrote down what it had to do.

  1. Goal 1Measure the distance between any two elements with one click and one hover.
  2. Goal 2Show which design token a value comes from, not just the resolved number.
  3. Goal 3Never break, slow down or watch the page it inspects.
Chapter 03 / 09Measuring

Distance is a relationship, not a property

Click to lock one element, hover another, and the extension classifies the pair: stacked, side by side, diagonal or nested. Each case draws something different, from a single labelled gap to an L of two distances with dashed guides.

Measurements start from what the eye sees. When a child sits flush against its parent’s border, its box is pushed out to that border edge, so the number matches the designer’s ruler instead of the inside of a border.

Trade‑offTwo elements at a time, by design. Multi‑select would have made every overlay harder to read.

See the codecontent-script/geometry · computeMeasurement

Two overlap tests decide which of four measurement cases a pair falls into.

export function computeMeasurement(a: ElementRect, b: ElementRect): MeasurementResult {
  const xOverlap = a.left < b.right && b.left < a.right;
  const yOverlap = a.top < b.bottom && b.top < a.bottom;

  if (xOverlap && yOverlap) return computeOverlapping(a, b);
  if (xOverlap && !yOverlap) return computeVertical(a, b);
  if (!xOverlap && yOverlap) return computeHorizontal(a, b);
  return computeDiagonal(a, b);
}
Measure any two elements banner: a locked button and the navigation button on a sample pricing page, with labelled horizontal and vertical distances of 508 and 428 pixels.
Fig. 01Diagonal distance between two elements
Chapter 04 / 09Tokens

Tokens, not just hex

The browser’s computed style only ever returns the resolved colour. To find the token behind it, the extension walks the page’s stylesheets, recursing into @layer, @media and @supports, finds the rules that match the element and reads the var(--name) out of the declaration. Inline styles are checked first.

The same pass labels padding in purple, margin in orange and every flex or grid gap with its value, so a 20 that should be 24 stands out at a glance.

Trade‑offBrowsers block reading stylesheets served from another domain, so colours from that CSS show as hex only.

See the codecontent-script/geometry · searchRules

A matching rule’s declaration still holds the var() the author wrote, long after the computed value has resolved it away.

for (const rule of rules) {
  if (rule instanceof CSSStyleRule) {
    try {
      if (!el.matches(rule.selectorText)) continue;
    } catch {
      continue;
    }
    for (const prop of propsToCheck) {
      const v = extractVar(rule.style.getPropertyValue(prop));
      if (v) return v;
    }
  }
  if ("cssRules" in rule && (rule as CSSGroupingRule).cssRules) {
    const found = searchRules((rule as CSSGroupingRule).cssRules, el, propsToCheck);
    if (found) return found;
  }
}
Tokens, not just hex banner: a selected purple button with an info panel listing size, font, layout, radius and colours with their CSS variable names.
Fig. 02Colours with their design tokens
Every gap labelled banner: a row of pricing cards with hatched gaps labelled 24 and 20, and the section padding labelled 32.
Fig. 03Flex gaps and padding, labelled
Chapter 05 / 09States

:hover without hovering

Interactive styles vanish the moment the cursor moves to inspect them. Naked CSS collects the :hover and :active rules that would match the selected element, applies them inline for an instant, reads the result and restores the original values.

Chrome’s DevTools protocol can force states properly, but an extension needs the debugger permission for that, and Chrome then shows a warning bar on the page. I took the lighter route.

Trade‑offIt’s a close approximation, not the browser’s own state: rules in unreadable stylesheets and hover effects driven by JavaScript aren’t included.

See the codecontent-script/geometry · withForcedState

Overrides are applied as inline !important styles, read, then put back exactly as they were.

const saved = new Map<string, string>();
for (const [prop, val] of overrides) {
  saved.set(prop, htmlEl.style.getPropertyValue(prop));
  htmlEl.style.setProperty(prop, val, "important");
}

const result = fn(el);

for (const [prop, orig] of saved) {
  if (orig) htmlEl.style.setProperty(prop, orig);
  else htmlEl.style.removeProperty(prop);
}
Hover without hovering banner: a button previewed in its hover state with a Default, Hover and Active switch, and a panel showing the hover background token.
Fig. 04Previewing a :hover state
Chapter 06 / 09Out of the way

Inspect without touching

Everything is drawn in one SVG layer, fixed over the viewport with pointer events switched off, so the page keeps its own clicks and scrolling. Mouse moves are batched to one update per animation frame, so measuring never thrashes layout.

The panel’s Inter font ships inside the extension, so it looks the same on every site whatever CSS the page loads. It asks only for activeTab, scripting and storage, and analytics are anonymous feature events with no URLs and no page content.

Trade‑offEmbedding three font weights is more than half of the 175 KiB package. A panel that looks the same everywhere was worth it.

Chapter 07 / 09Getting found

Built to be found

A Chrome extension lives or dies by search, so I researched the store listing like a landing page. The title leads with what people actually type: CSS inspector, pixel ruler, layout debugger.

An earlier title led with “CSS Debugger”, until a search audit showed four extensions literally named that holding the top results. I moved it into the description and aimed for queries I could win.

The landing site is Next.js on Vercel, with comparison pages against VisBug, CSS Peeper, Page Ruler and Inspect, an llms.txt for AI answers, and launch posts where the audience already is, starting with r/css.

Trade‑offRanking for winnable queries means not chasing the biggest search term.

Chapter 08 / 09The system

What it became

A Manifest V3 extension built with Vite and TypeScript: a content script that does all the reading and drawing, a small service worker that keeps track of tabs, and nothing that ever leaves the browser except anonymous counts.

Trigger Toolbar or ⌘ I

One click or one shortcut, on any tab.

Chapters0306
Settings Options page

Rebind shortcuts, synced across devices.

Chapters06
→Message
+ inject
In the page Content script

Geometry, token lookup and state preview, drawn as one SVG overlay.

Chapters03040506
Background Service worker

Per‑tab on and off, injection into older tabs, analytics relay.

Chapters06
→Read
only
Inspected The live page

Styles and stylesheets are read, never kept changed.

Chapters0405
Analytics PostHog

Anonymous feature events only.

Chapters06
Vercel Landing site

Next.js, comparison pages and llms.txt.

Chapters07

Numbers mark the chapters behind each part. Point at one to see what it touches.

Chapter 09 / 09The end

Where it landed

Naked CSS is live on the Chrome Web Store, free for anyone. Back to the three goals:

209People using it, on the Chrome Web Store
5.0Average rating from its first reviews
175KiB package, fonts included
0Accounts, paywalls or page data collected
  1. ✓ Goal 1
    Two elements, one hover.Pixel distance, padding, margin and gaps are drawn on the page, with no panel to dock.
  2. ✓ Goal 2
    Tokens, not guesses.Colours come with the CSS variable behind them, so a mismatch names the token that drifted.
  3. ✓ Goal 3
    Read‑only and private.One overlay that never takes a click, three permissions and no page content sent anywhere.

Trade‑offs I live with

  • Colours from cross‑origin stylesheets show as hex, without their token.
  • The :hover and :active preview is an approximation of the browser’s own state.
  • Chrome only, and pages Chrome protects, like the Web Store, can’t be inspected.

I built it alone, from the geometry to the growth plan, and it stays free.

Try Naked CSS

Stop opening DevTools to check a 4px padding.

Free, no account, and no page content ever leaves your browser. Install it, open any page and press ⌘ I (Ctrl I on Windows).

  1. 01Add to ChromeOne click from the Chrome Web Store.
  2. 02Press ⌘ IOn any page. Hover anything to see its details.
  3. 03Click, then hoverLock one element and measure everything else against it.

Chrome and other Chromium browsers that install from the Chrome Web Store

(06) Contact Open to new work

Let’s talk.

Need someone to own a feature from the database to the UI? I’m open to new roles and collaborations.

© 2026 Abdul Ahad Qureshi Set in Geist on a twelve‑column grid