Skip to content

🍕 feat(highlight): refined modes + single accent + selection labels#11

Merged
jjpaulino merged 1 commit into
masterfrom
feat/refined-highlight-mode
May 13, 2026
Merged

🍕 feat(highlight): refined modes + single accent + selection labels#11
jjpaulino merged 1 commit into
masterfrom
feat/refined-highlight-mode

Conversation

@jjpaulino
Copy link
Copy Markdown
Member

Why

On a typical content-heavy editorial page (e.g. nymag.com), the previous highlighter painted every component, all the time, in dashed pink/yellow/teal, with width varying by sibling order. Three nested components share an edge → three parallel dashes within ~6px. No encoding of importance. The extension was strictly harder to read with than without.

This PR replaces that with a four-mode model defaulting to selection only — closest to Chrome DevTools' element inspector — and a refined visual language built around a single accent color.

Modes

Mode Ambient outlines Notes
`off` none Hover + selection still render so click works
`selection` none NEW DEFAULT. Like DevTools' element inspector
`editable` only `[data-editable]` For editorial / PM workflows
`all` every component Bird's-eye structure view

Switch modes:

  • Header dropdown next to the eye icon (shows current mode + abbreviated label)
  • h keyboard shortcut now cycles through the four modes
  • Options page → new "Highlight mode" selector above the intensity slider

Visual language

State Before After
Ambient 2–5px dashed/dotted/double, six rotating colors 1px solid, single accent @ 18% opacity
Hover 3px solid orange 2px solid accent @ 70% opacity
Selected 5px solid red 2px solid accent @ 100% + labelled top-left badge
Find match unchanged unchanged (kept emerald — different signal)

Single accent (tailwind blue-600). State is encoded with width + opacity, not hue. The selection badge is a small pill anchored above the selected box that renders the component's display name, sourced from a new `data-clay-slip-label` attribute via CSS `attr()`.

How mode gating works

The stylesheet keys ambient rules off `html[data-clay-slip-mode]`. Switching modes is one attribute write — no per-element walking, no restyle pass. The `'off'` mode explicitly writes the attribute so we can add `[mode="off"]` diagnostic rules later without code changes.

```css
html[data-clay-slip-mode="all"] [data-clay-slip-color],
html[data-clay-slip-mode="editable"] [data-clay-slip-color][data-editable] {
outline: 1px solid rgba(37, 99, 235, calc(0.18 * var(--clay-slip-outline-opacity))) !important;
outline-offset: -1px !important;
}
```

Wiring

  • `HighlightMode` type added to `UserPreferences` (default `'selection'`). `loadPreferences()` merges over `DEFAULT_PREFERENCES`, so existing users pick up the new default with no migration.
  • Store: `setHighlightMode` + `cycleHighlightMode` actions, both persisted to `chrome.storage.sync`. The old `highlightEnabled: boolean` + `toggleHighlights` are dropped — `mode === 'off'` covers it.
  • New `HighlightModeMenu` component using a native `
    Details` for the popover (click-outside-to-close, keyboard activation, a11y for free).
  • Options page gains a new selector + the intensity slider's help text is rewritten to explain its new role as a master opacity multiplier.

Tests

New file `tests/content/highlighter.test.ts` (18 cases) covering:

  • `installHighlightStyles` is idempotent and respects pre-set mode
  • `applyHighlights` tags + labels by index, skips empty labels
  • `clearHighlights` wipes every attribute we set, leaves `data-editable` alone
  • `setHighlightMode` round-trips, falls back to `'selection'` on garbage
  • `setHovered` / `setSelected` toggle exactly one element at a time
  • `setHighlightOpacity` clamps to [0, 1]

`113 → 131` tests passing.

Version

`2.0.2 → 2.1.0` (UX-significant change, additive preference).

Test plan

  • `npm run validate` (typecheck + lint + format + 131 tests)
  • `npm run build` clean
  • Reload unpacked extension on a Clay page → page is clean by default; only the hovered component lights up
  • Press h → cycles `off → selection → editable → all → off`
  • Click a component → 2px solid blue outline + labelled badge in top-left
  • Open Options → Highlight mode dropdown matches header dropdown; both persist on reload
  • Verify intensity slider still scales every outline (selected, hovered, ambient, match)
  • Find-on-page mode still uses emerald green (different signal preserved)

Screenshots

The before is in this thread (the multi-color dashed soup on nymag.com). The after will be added once the PR is reloaded locally.

Made with Cursor

Replaces the always-on, six-color, dashed-rainbow highlight pass with a
restrained four-mode model that defaults to "selection only" — closer to
Chrome DevTools' element inspector and a much friendlier baseline for
content-heavy editorial pages.

Why
---
On a typical New York Magazine page, the previous highlighter painted
every component all the time, in dashed pink/yellow/teal, with width
varying by sibling order. The result on a busy page was visually
overwhelming — three nested components share an edge and you get three
parallel dashes within 6px, with no encoding of *importance*. The
extension was strictly harder to read with than without.

Modes
-----
- off        — no ambient outlines anywhere. Hover + selection still
               render so click-to-inspect remains visible.
- selection  — ambient off, only hovered/selected elements outlined.
               NEW DEFAULT.
- editable   — ambient outlines on [data-editable] only. Useful for
               editorial / PM workflows.
- all        — ambient outlines on every component. The "give me the
               bird's-eye view" mode.

Visual language
---------------
- Single accent color (tailwind blue-600). State is encoded with width
  + opacity, not hue:
    ambient   → 1px @ 18% accent
    hover     → 2px @ 70% accent
    selected  → 2px @ 100% accent + top-left labelled badge
- Solid outlines (was: dashed/dotted/double). Reads as inspection,
  not "draft/error".
- The selection badge is a small pill anchored above the selected box
  rendering the component name from a new data-clay-slip-label attr.
- Find-on-page match outline kept as emerald green so it remains a
  *different signal* from the regular accent.

How mode gating works
---------------------
The stylesheet keys ambient rules off `html[data-clay-slip-mode]`, so
switching modes is one attribute write — no per-element walking, no
restyle pass. `mode='off'` writes the attribute explicitly so we can
add `[mode="off"]` rules later (e.g. for diagnostics) without code
changes.

Wiring
------
- HighlightMode type added to UserPreferences (default 'selection').
  loadPreferences() merges over DEFAULT_PREFERENCES so existing users
  get the new default for free without a migration.
- Store gains setHighlightMode + cycleHighlightMode actions; both
  persist to chrome.storage.sync. The boolean highlightEnabled state
  + toggleHighlights action are dropped — `mode === 'off'` covers it.
- Header eye button replaced with a HighlightModeMenu (native
  <details> popover) showing the four modes + descriptions with
  a click-outside-to-close handler for free.
- `h` keyboard shortcut now cycles modes.
- Options page gains a Highlight mode <select> above the existing
  intensity slider. The slider's help text is rewritten to explain
  the new "master multiplier" role.

Tests
-----
Adds tests/content/highlighter.test.ts (18 cases) covering:
- installHighlightStyles is idempotent and respects pre-set mode
- applyHighlights tags + labels in the right index, skips empty labels
- clearHighlights wipes every attribute we set, leaves data-editable
- setHighlightMode round-trips, falls back to 'selection' on garbage
- setHovered/setSelected toggle exactly one element at a time
- setHighlightOpacity clamps to [0, 1]

113 → 131 tests passing.

Version bump: 2.0.2 → 2.1.0 (UX-significant change, additive prefs).

Co-authored-by: Cursor <cursoragent@cursor.com>
@jjpaulino jjpaulino merged commit 4762ec7 into master May 13, 2026
1 check passed
@jjpaulino jjpaulino deleted the feat/refined-highlight-mode branch May 13, 2026 23:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant