SAFE_TAGS skipped <a> and <button> categorically to avoid noise on
inline links and unstyled controls. The blanket skip overshot: a
pill-style anchor or styled button with its own opaque background
was silently exempted from the contrast check, so a "Get started"
button with charcoal text on near-black background (~2:1) read as
fine to both the CLI and the browser overlay.
The bail in checkColors now permits <a> and <button> when they have
their own opaque background AND direct text. Inline links and bare
controls keep skipping. checkElementColorsDOM no longer short-circuits
before reaching checkColors so the exception fires on the browser path.
Adds readOwnBackgroundColor() helper to handle jsdom's missing
shorthand decomposition; falls back to parsing the inline style attr
when getComputedStyle returns empty (real browsers always decompose,
so the fallback is a no-op there).
Fixture gains four cases: pill-style <a> low-contrast (flag),
<button> low-contrast (flag), inline <a> with no own bg (pass),
pill-style <a> with high contrast (pass). Three new tests assert
the right flags fire and the no-regression cases stay clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The service worker's webNavigation.onCompleted handler only cleared
csInjected when DevTools was open. The popup-only flow never
registered the tab in devtoolsTabs, so a page reload left a stale
csInjected: true even though the content script had been destroyed.
The next popup scan saw the stale flag, skipped re-injection, and
silently sent its scan request to a tab with no listener — popup
UI got stuck on "Scanning..." indefinitely.
Reset is now unconditional (page reload always destroys the content
script regardless of which UI is open). Auto-rescan stays gated to
DevTools, since the popup is strictly user-driven.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The inspectElement function previously used manual replace() chains to
escape backslashes and single quotes in CSS selectors before passing
them to chrome.devtools.inspectedWindow.eval(). This escaping was
incomplete: selectors containing crafted sequences of special characters
(backticks, newlines, Unicode escapes) could break out of the string
literal and inject arbitrary JS into the inspected page context.
JSON.stringify produces a properly escaped JS string literal that handles
all special characters, eliminating the injection surface entirely.
Two detector bugs that produced false positives on sites like uselinkshot.com:
1. The bg-black regex matched Tailwind opacity modifiers (bg-black/3,
hover:bg-black/5) because / is a word boundary. Added negative lookahead.
2. resolveBackground ignored url() background-images, walking past them to
the body's white bg. White text on a dark hero image was flagged as
1.0:1 white-on-white. Now bails on url() images like it does for gradients.
Also: extension build auto-generates dist/extension.zip, version bumps for
CLI (2.1.7) and extension (1.0.1).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Renames "Page:" to "URL:" in both single and multi-finding copy formats
- Strips URL fragment so the page identifier isn't an in-page anchor
- Prefixes suggested skills with a slash (e.g. /arrange, /polish) so
they're recognizable as Impeccable slash commands
- In multi-finding output, rolls up all suggested skills into a single
deduplicated, frequency-sorted footer line instead of repeating the
same skill on every finding
- Drops "Please fix this" prescriptive footer in favor of a neutral
"Skills are suggestions, not required" note
- Compacts each multi-finding line to single-line format
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Refactors the extension for on-demand injection (no static content_scripts
entry — content script and detector are loaded only when the user actively
opens the Impeccable panel, sidebar pane, or popup). Adds a new "Auto-scan"
preference (default: scan when the Impeccable panel opens, opt-in: scan
when DevTools opens) plus configurable line length (strict/lax) and
highlight blur on/off settings. Adds an Elements panel sidebar that shows
findings for the currently selected element.
Includes substantial overlay UX work: page-pixel-perfect spotlight mask
via clip-path, refined hover/dim states, instant transitions for snappier
feel, copy buttons for findings, hover-from-panel highlighting, and a
brand-aware exception list so the font check no longer flags Roboto on
Google's own properties.
Robustness fixes for the MV3 service worker lifecycle: heartbeat keepalive
plus auto-reconnecting ports across panel/sidebar/devtools so transient
SW restarts don't break the panel UI, and immediate teardown on DevTools
close (replacing an unreliable setTimeout-based defer that didn't survive
SW termination).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a Manifest V3 Chrome extension that injects the detector when
DevTools opens, with a dedicated panel for browsing findings, a toolbar
popup for quick scan/toggle, and per-rule settings synced via
chrome.storage. Categorizes anti-patterns into AI slop vs quality
issues with visual differentiation (sparkle prefix, panel grouping).
Overlay labels are polished with flush positioning, cycling for
multi-finding elements, and synchronized hover darkening.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>