Inspiration

Every front-end developer knows this ritual: you see a beautifully built component on a live site, open DevTools, and start the archaeology — digging through nested divs, copying CSS rules one by one, chasing down image URLs. DevTools shows you one element at a time, but it never shows the page the way a designer sees it: a hero, a nav, a card grid, a footer. We wanted a tool that reads any live page as a tree of components, and lets you pick up any one of them — fully packaged — with a single click.

What it does

Click the toolbar icon and the extension scans the page and builds a structure tree in a sidebar: sections and components, each with a bilingual type label (Hero, Nav, Card, …), media badges, and interactivity badges.

  • Hover anything on the page → a blue outline + label tells you what it is (tag.class · size · type).
  • Alt+click locks it (red outline) — normal clicks still pass through to the page, so the site keeps working while you inspect it.
  • ↑ / ↓ arrow keys walk up and down the DOM to adjust granularity; clicking a tree node scrolls to it and locks it.
  • The overlay repositions every frame, so it stays glued to its target even on smooth-scroll (Lenis) sites.

Two one-click exports:

  • Component export — a tiny self-contained zip (~24 KB for a typical card): component.json with a stable CSS selector, classification, matched CSS classes and asset URLs; component.html, an isolated render you can open directly; plus DOM snapshots of interaction states (default / hover / scrolled / clicked). Structured for both humans and AI coding agents to consume.
  • Mirror export — a browser-side snapshot of the whole page as you currently see it: post-hydration DOM, every loaded asset (cross-origin ones fetched through the extension's background worker), framework payloads like __NEXT_DATA__ / __NUXT__, with all links rewritten to local paths. Serve the folder with any static server and the page runs offline. Because it runs inside your logged-in browser, it captures authenticated and purely runtime pages that no crawler can reach.

How we built it

Vanilla JavaScript, zero dependencies — the entire tool is a self-contained IIFE injected as a Manifest V3 content script (isolated world, so strict page CSP can't break it). Cross-origin asset fetches go through the background service worker.

The real problem isn't drawing outlines — it's deciding where they go:

  • Sections come from landmarks (header / nav / main / footer / ARIA roles) plus heading hierarchy.
  • Components come from repeated-sibling detection: siblings sharing the same tag+class signature (≥2) are cards or list items, and each repeat becomes one selectable unit — a card's image, title, and button count as one box, not three.
  • Type classification is a heuristic rule set distilled from a corpus of 7 real production sites, with a built-in bilingual (EN/中文) dictionary. No LLM in the loop — labeling is instant, free, and deterministic.

Determinism was a hard requirement: one synchronous full-DOM scan, no scrolling, no timers, sizes measured with offsetWidth/Height so they don't change with scroll position. Scanning the same page at top, middle, and bottom scroll produces item-for-item identical trees.

Even the zip writer is handwritten (store-only ZIP format) to keep the dependency count at zero.

Challenges we ran into

  1. Granularity. Too fine and every <span> gets a box; too coarse and the whole page is one box. Repeated-sibling grouping plus minimum-area thresholds got us to "one card = one unit".
  2. Our first UI was wrong. v1 drew outlines on everything at once — on scroll-heavy marketing sites it was visual chaos. We threw it away and rebuilt around a "tree + spotlight" model: the full inventory lives in the sidebar; the page only highlights what you hover or lock.
  3. Smooth-scroll sites. Libraries like Lenis move content with transforms, so absolutely-positioned overlays drift. Per-frame repositioning fixed it.
  4. CSP and CORS. Our bookmarklet prototype died on strict-CSP sites → moved to an extension content script in an isolated world. Cross-origin assets are unfetchable from the page → routed through the background worker's host permissions.
  5. Export design. Early exports duplicated everything and ballooned. The final component zip references the page snapshot instead of re-bundling it, which is how it stays ~24 KB.

Accomplishments that we're proud of

  • Reproducible scans: identical component trees regardless of scroll position, run after run.
  • Snapshotted a real production site into a 61 MB, 375-file offline mirror that serves and browses locally with zero fixups.
  • The whole thing is plain JavaScript with no dependencies — no build step, no framework, loads instantly.

What we learned

  • In "outline every component", drawing is 5% of the work; deciding what counts as a component is the other 95%.
  • A small heuristic rule set distilled from real sites beats an LLM call for this job: instant, free, and deterministic.
  • The browser is a privileged vantage point: post-hydration DOM and logged-in sessions are things no server-side crawler will ever see.

What's next

  • Shadow DOM piercing (deep elementFromPoint) and iframe support.
  • Auto-scroll sweep to inventory lazily-mounted components that only appear after interaction.
  • Richer dynamics annotation — detecting GSAP / Swiper / Lottie usage per component, not just page-wide.
  • A Firefox port.

Built With

Share this project:

Updates