Skip to content
 
 

Repository files navigation

StyleGrabber Logo

StyleGrabber

Instant CSS & HTML Style Extraction • Visual Element Inspector • DOM Subtree Capture

Manifest V3 WXT + React TypeScript Vitest Chrome Web Store Firefox Add-on Edge Add-on License MIT


📌 Overview

StyleGrabber is a high-performance, cross-browser web extension (built with WXT, React, TypeScript, and Vite) that allows developers, designers, and UI engineers to instantly inspect, diff, extract, and copy element styles and HTML subtrees directly from any webpage.

Unlike default browser DevTools which require inspecting deep DOM trees and sifting through raw computed style lists, StyleGrabber provides an interactive on-page hover overlay, dynamic computed style diffing against browser defaults, authoring stylesheet cascade resolution, CSS custom property (var()) resolution, pseudo-element (::before/::after) extraction, tree-mode DOM sub-tree capturing, and auto-copying formatted code directly to your clipboard.


🖼️ Visual Tour & Screenshots

1. Interactive Inspection Overlay & Floating Tooltip

Hover over any web element to see a GPU-accelerated bounding box, tag/class badge, and live floating preview panel displaying non-default CSS computed properties, color swatches, resolved variables, pseudo-elements, and matched rule counts.

StyleGrabber Hover Inspection Overlay

2. Extension Settings & Options Popup

Easily toggle extension state, pin overlays for detailed examination, enable DOM Tree Subtree Mode, toggle Auto-Copy on hover dwell, select clipboard output templates (HTML + CSS, CSS Only, HTML Only), customize keyboard shortcuts (Alt+Shift+P / ^+Shift+P), adjust inactivity timeouts, and switch theme modes (Light, Dark).

StyleGrabber Settings Popup UI      StyleGrabber Options & Keyboard Shortcuts Settings

✨ Key Features

  • 60fps GPU-Composited Mouse Tracking: Repositioning uses requestAnimationFrame batching and CSS translate3d() transforms for jitter-free performance on heavy pages.
  • 🎨 Dynamic Baseline Computed Style Diffing: Compares target element styles against an isolated tag default iframe baseline table to extract only authored/custom styles, omitting default browser styles and redundant parent inherited values.
  • 🔍 Authoring Rule Cascade & CSS Variables: Walks accessible document.styleSheets in cascade order, resolves nested var(--x) chains up to depth 10 with circular reference protection, and displays exact CSS declarations.
  • Pseudo-Element Extraction: Automatically detects and diffs non-default styles for ::before and ::after pseudo-elements.
  • 🌲 Single Element & Tree Mode Traversal: Generator-based non-blocking DOM walker with recursion depth limit (maxDepth: 15) and node cap (maxNodes: 5000) to snapshot entire component subtrees smoothly on complex SPAs.
  • 📋 Formatted Clipboard Export: Generates clean, pretty-printed HTML structure and categorized CSS declarations grouped by Layout, Typography, Box Model, Background, Border, and Effects.
  • 🛡️ Shadow DOM UI Shell: Complete CSS isolation using WXT's createShadowRootUi() ensures target page styles never leak into the extension overlay and vice versa.
  • 📌 Pinning & Inactivity Fade: Press shortcut or click to pin overlay target for inspection, or allow auto-fade after 5 seconds of cursor inactivity.
  • 🎯 Viewport Collision Avoidance: 4-way boundary math shifts floating tooltips dynamically to remain fully visible near screen edges.
  • 🔒 Context Invalidation Guards: Robust error handling prevents runtime crashes when extension is updated or reloaded while tabs remain open.
  • 🧹 WeakMap Memory Hardening: Uses WeakMap caches and element connection checks (isConnected) to ensure unmounted DOM nodes are immediately garbage collected.

🛠️ Architecture & Module Structure

StyleGrabber follows WXT's convention-over-configuration architecture with strict module boundary rules:

StyleGrabber/
├── entrypoints/
│   ├── background.ts            # MV3 Service worker (lifecycle & storage init)
│   ├── content/
│   │   ├── index.ts             # Content script entry (defineContentScript)
│   │   └── mountUi.tsx          # Shadow DOM mount point (createShadowRootUi)
│   └── popup/
│       ├── App.tsx              # Popup options & settings UI (React)
│       └── style.css            # Popup styling & theme custom properties
│
├── components/                  # Shadow-DOM isolated UI components
│   ├── ContentApp.tsx           # Main content script overlay orchestration
│   ├── HoverBox.tsx             # Animated bounding box overlay
│   ├── Badge.tsx                # Tag + class label pill with copied toast feedback
│   ├── Tooltip.tsx              # Syntax-highlighted CSS preview panel
│   └── shared/
│       ├── theme.css            # Scoped CSS custom properties (:host)
│       └── Icon.tsx             # Inline SVG icons (Copy, Check, Pin, etc.)
│
├── utils/
│   ├── style-engine/            # ⚙️ CORE MODULE 1 — Style Extraction
│   │   ├── computeDiff.ts       # Computed style vs UA baseline table diffing
│   │   ├── baseline.ts          # Detached iframe tag baseline generator
│   │   ├── ruleMatcher.ts       # Stylesheet cascade rule & @media matching
│   │   ├── pseudoElements.ts    # ::before / ::after style extraction
│   │   ├── cssVariableResolver.ts # Resolves nested var(--x) chains & circular refs
│   │   └── properties.ts        # Categorized CSS property matrices
│   │
│   ├── dom-traversal/           # ⚙️ CORE MODULE 2 — DOM Walking
│   │   ├── traverse.ts          # Single node vs recursive generator walk
│   │   ├── elementFilter.ts     # Filters script/style/hidden/overlay elements
│   │   └── domSnapshot.ts       # Builds serializable DOM node snapshots
│   │
│   ├── formatter/               # ⚙️ CORE MODULE 3 — Output Formatting
│   │   ├── htmlFormatter.ts     # Pretty-prints HTML structure block
│   │   ├── cssFormatter.ts      # Indents/groups CSS declarations & deduplicates
│   │   └── clipboardComposer.ts # Assembles HTML + CSS export payloads
│   │
│   ├── shadow-dom/              # ⚙️ CORE MODULE 4 — UI Isolation
│   │   ├── mountUi.tsx          # Shadow root launcher & React renderer
│   │   └── positioning.ts       # Viewport-aware 4-way collision positioning
│   │
│   ├── storage/                 # ⚙️ STORAGE & SETTINGS
│   │   ├── schema.ts            # Typed Zod/Valibot-like settings schema
│   │   └── settings.ts          # browser.storage.local wrapper w/ live sync
│   │
│   └── perf/                    # ⚙️ PERFORMANCE & HARDENING
│       └── raf-throttle.ts      # requestAnimationFrame event throttling
│
├── public/
│   ├── icon/                    # Extension icons (16, 32, 48, 96, 128 px)
│   └── icon.png                 # Main high-res extension icon
│
├── wxt.config.ts                # WXT configuration & manifest declaration
├── package.json
└── tsconfig.json

Module Boundary Restrictions

Module Responsibility Must NOT Depend On
style-engine/ Pure style extraction logic (element → resolved styles) DOM mounting, React, Storage
dom-traversal/ Pure DOM tree walking & node filtering style-engine, formatter
formatter/ Pure string formatting & payload assembly Live DOM APIs directly
shadow-dom/ Mounting overlay & viewport positioning style-engine internals
components/ Presentation UI rendered inside Shadow Root Direct document.styleSheets calls

🚀 Getting Started

Prerequisites

  • Node.js: ≥ 18.0.0
  • Package Manager: pnpm (recommended), npm, or yarn

Installation

  1. Clone the repository:

    git clone https://github.com/kapaggar/StyleGrabber.git
    cd StyleGrabber
  2. Install dependencies:

    pnpm install

💻 Development & Building

Run in Development Mode

Launch the dev server with automatic hot-reloading and automatic browser instance launch:

# Launch Chrome / Edge dev environment
pnpm dev

# Launch Firefox dev environment
pnpm dev:firefox

Load Unpacked Extension Manually

Chrome / Edge (Chromium):

  1. Run pnpm build (outputs to .output/chrome-mv3/).
  2. Open Chrome and navigate to chrome://extensions/.
  3. Enable Developer mode in the top right corner.
  4. Click Load unpacked and select the .output/chrome-mv3/ directory.

Firefox:

  1. Run pnpm build:firefox (outputs to .output/firefox-mv3/).
  2. Open Firefox and navigate to about:debugging#/runtime/this-firefox.
  3. Click Load Temporary Add-on... and select the manifest.json file inside .output/firefox-mv3/.

🧪 Testing & Verification

StyleGrabber maintains high code quality with automated unit testing:

# Run all unit tests with Vitest (jsdom environment)
pnpm test

# Run TypeScript type check
pnpm typecheck

# Run ESLint validation
pnpm lint

📦 Production Packaging

Generate store-ready .zip archives for Chrome Web Store and Firefox Add-ons (AMO):

# Package Chrome zip archive (.output/stylegrabber-<version>-chrome.zip)
pnpm zip

# Package Firefox zip archive (.output/stylegrabber-<version>-firefox.zip & sources.zip)
pnpm zip:firefox

# Full pre-publish gate (test + typecheck + lint + zip)
pnpm prepublish:check

Chrome Web Store publish

This repo includes listing copy, permission justifications, and a step-by-step checklist:

Resource Path
Publish checklist docs/CHROME_WEB_STORE.md
Store listing text store/listing.md
Permission justifications store/permissions.md
Screenshot size guide store/SCREENSHOTS.md
Privacy policy PRIVACY_POLICY.md

Before first CWS submit: capture screenshots at 1280×800 or 640×400 (see screenshot guide). Existing assets/images/* files are for the README and are not yet store-sized.


⚙️ Key NPM Scripts

Script Command Description
pnpm dev wxt Start WXT development server for Chromium
pnpm dev:firefox wxt -b firefox Start WXT development server for Firefox
pnpm build wxt build Build production Chromium MV3 bundle
pnpm build:firefox wxt build -b firefox Build production Firefox MV3 bundle
pnpm test vitest run --environment jsdom Execute Vitest unit test suite
pnpm typecheck tsc --noEmit Check TypeScript compilation without emitting
pnpm lint eslint . Lint TypeScript code against project rules
pnpm zip wxt zip Package Chrome zip archive for distribution
pnpm zip:firefox wxt zip -b firefox Package Firefox zip archive for distribution
pnpm prepublish:check tests + lint + zip Gate before Chrome Web Store upload

🛡️ Technical Risk & Edge Case Strategies

1. CORS on CDN Stylesheets

  • Problem: Reading sheet.cssRules on cross-origin <link> stylesheets (e.g., Google Fonts, CDN FontAwesome) throws a SecurityError.
  • Solution: StyleGrabber's computed style diffing engine reads the live render tree via getComputedStyle(), which is CORS-immune. CSS rule matching wraps sheet accesses in try/catch and displays a graceful notice when stylesheet rule source access is blocked.

2. Extension Context Invalidation

  • Problem: Updating/reloading an extension in development while web pages remain open invalidates the extension context, causing Extension context invalidated errors when calling storage APIs.
  • Solution: Guarded runtime checks (isExtensionContextValid()) and try/catch wrappers around all storage calls ensure smooth operation without unhandled promise rejections.

3. SPA Memory Leak Prevention

  • Problem: Inspecting dynamic single-page applications (React, Next.js, Vue) can leak DOM references.
  • Solution: Snapshot caches use WeakMap<Element, DomNodeSnapshot>, elements are verified for active document connection (isConnected), and keyboard/SPA route event listeners (popstate/hashchange) clean up unmounted nodes.

🤝 Contributing

See CONTRIBUTING.md. Agent-oriented project maps: CLAUDE.md, AGENTS.md.

🔒 Privacy

StyleGrabber processes styles locally only and does not collect personal data.

📄 License

This project is licensed under the MIT License. See the LICENSE file for details.

About

Lightweight browser extension to quick copy HTML and CSS styles.

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages