Clients:

lukeduff.co.uk

Category:

Bespoke Tool & Chrome Extension

Link:

See The Result

Teardown — Chrome Extension for SEO & Page Auditing

Client: lukeduff.co.uk · Sector: SEO & Developer Tooling · United Kingdom

1 | Brief

Teardown is a Chrome extension that tears any web page apart in one click and reports what is actually wrong with it. It was built for the kind of work where you are sat on a client site, a competitor's site or a staging build and need the answer immediately, not after a crawler has finished its run. Most heading tools stop at printing an H1-H6 list. Teardown opens with the question that comes before that: can this page rank at all? It reads the robots meta tag, canonical, hreflang and page language first, then works down through structure, metadata, images, content and design.

The project started life as a heading grabber, outgrew that remit, and got a new name. Luke was sole designer, developer and publisher throughout, from the initial concept through to the Chrome Web Store listing at version 1.7. There is no framework, no build step for the extension itself, no runtime dependencies, and no account required to use it. The packaged extension is 52KB across 11 files, written in around 5,500 lines of vanilla JavaScript across six source files.

Everything is local. Scan history is stored in chrome.storage.local on the user's own machine and deleted on uninstall. The only outbound requests beyond the audited page's own sitemap, robots.txt and llms.txt are an optional webhook the user points at their own endpoint. No developer-operated backend exists.

2 | Key Goals

  • Answer the indexability question first, before printing any heading list, so the audit starts with whether the page can actually rank.
  • Lint heading structure against the full markup while only listing headings a user can physically scroll to, because a crawler and a reader can see quite different pages.
  • Scope keyword analysis to the page's main content so navigation and footer text stops polluting the results.
  • Surface images that CSS backgrounds, picture srcset markup and video poster attributes hide from a normal right-click inspection.
  • Score a page out of 100 in a way that can be explained to a client, not just quoted at them. The model covers four weighted categories: indexability 30 points, structure 25, metadata 25 and image alt text 20. A page carrying a noindex is capped at 40 regardless of how well it scores elsewhere.
  • Keep the whole thing account-free and local, with the privacy claim enforced by automated tests rather than asserted in a policy document.
  • Ship end to end: extension, landing site, accessibility and privacy documentation, store assets and the Chrome Web Store submission.

3 | Solution Snapshot

Problem Solution Implemented
Split-text animation libraries rewrite headings into character-level inline blocks with no whitespace, so reading textContent returned run-together strings like "HelpingSunderlandBusinesses". Heading text is now read from innerText, which returns the rendered text a reader and a crawler both see rather than the raw structural markup.
The original keyword pass used a tag allowlist and read only direct child text nodes, so words inside links, strong elements, table cells and figcaptions were invisible. On one test page it saw two instances of a phrase that appeared six times and missed the top commercial term entirely. Keyword extraction now walks text nodes with a TreeWalker, because link and emphasis text is precisely where commercial keywords tend to live.
Whitespace handling pulled in two opposite directions. Character-split animations needed adjacent inline text concatenated raw, while ordinary inline markup needed the gap between elements preserved. Fixing either case alone broke the other. The extractor was reworked to satisfy both cases simultaneously: split-character headings join cleanly while genuinely separate inline elements keep the space between them.
Tokenising with \w treats every accented or non-Latin character as a word separator. "Reparation" with an accent came back as "paration", and Japanese and Arabic pages returned nothing at all. Tokenising moved to Unicode property escapes, with Intl.Segmenter for scripts that do not use spaces between words, and stop word lists merged in from the page's html lang attribute.
WordPress and card-grid layouts emit one article per item. On a real client site the first was an inactive hero carousel slide holding 53 words, so scoping content to it reported a word count of zero on a page of roughly 2,000 words. A lone article now has to hold at least a third of the page's total text before it is trusted as the content root; otherwise the scope falls back to the wider page.
The stylesheet originally loaded a typeface from Google Fonts, which sent every user's IP address and user agent to Google on every popup open, while the privacy policy stated that no third party received anything. The font is now self-hosted inside the package. A build-time test scans every shipped file for third-party hosts and fails the build if one is found, so the privacy claim is enforced rather than asserted. The landing site's three CDNs were removed for the same reason.
The previous layout check was "does the page scroll sideways" plus "are there console errors", which passed while a screenshot sat 671px wide inside a 737px cell, hanging 99px over its neighbour. Nothing was logged and the page never scrolled, because the overlap was contained inside the grid. A geometry suite was added that measures elements against their containers across four pages at eight viewport widths, because a page-level check cannot see a component-level break.

4 | Impact

  • 240 automated checks across five suites, all green: 22 jsdom unit tests for storage and audit logic, 37 tests in real Chromium for computed styles, paint and canonical resolution, 61 invariant tests, 97 landing page geometry checks across four pages at eight widths, and 23 popup rendering tests.
  • The invariant suite asserts no expected values at all, only relationships that must hold for any page on the web. A reported heading must exist on the page. A reported phrase must appear verbatim in the page's text. A keyword cannot be counted more often than it occurs. The same page scanned twice must return the same answer. It caught a bug in the very fix that shipped alongside it.
  • The extension is Manifest V3, targets Chrome 96 and above, and requests activeTab, scripting, storage and downloads plus host access for http and https. Content scripts are injected only after the user clicks Scan, so the extension cannot know a page exists until the popup is opened on it.
  • Scoring is kept honest by two rules: nothing scores for merely existing, and nothing is scored off an unreliable measurement. Page weight is reported as a diagnostic rather than scored, because the browser's resource timing API reports zero transfer size for cross-origin responses without Timing-Allow-Origin.
  • Scan comparison uses a longest common subsequence diff for the heading outline, because comparing index by index reports every heading after a single insertion as changed.
  • The landing site is fully self-hosted and static, deployed on Vercel from a git push, with Tailwind compiled ahead of time, fonts served locally, and built to WCAG 2.2 AA including the 24px minimum tap target rule.

What are you waiting waiting for?

Start Your Project Today
Book a FREE Call
footer shape