Skip to content

🍕 Subtle ambient highlight: corner accents in 'all' mode#14

Merged
jjpaulino merged 1 commit into
masterfrom
feat/ambient-corner-ticks
May 14, 2026
Merged

🍕 Subtle ambient highlight: corner accents in 'all' mode#14
jjpaulino merged 1 commit into
masterfrom
feat/ambient-corner-ticks

Conversation

@jjpaulino
Copy link
Copy Markdown
Member

Summary

You asked: "Is there a way to highlight a little all the components on the page in a non-visually disruptive way but then keep the rest of the hover behavior as is? I'm doing it on the selection-only mode but maybe we do that for the `all` mode?"

This PR redesigns what `all` mode draws.

Before: every component got a 1px @ 18% blue outline on its full perimeter. Even at low opacity, full perimeters accumulate at shared edges — three nested cards stacked three parallel lines within ~6px — so the page still read as "every component is highlighted".

After: every component gets four small L-shaped accents (8px arms, 1px wide, blue @ 55%) — one at each corner. Corners don't accumulate at shared edges, so nested layouts stay quiet, while you can still tell at a glance which boxes are Clay components.

Hover and selection are completely unchanged — they still draw the rich 2px outlines and the selection label badge. The corner ticks just drop out the moment you start inspecting (`:not([hover]):not([selected])`) so they never compete with the inspection signal.

Visual model

State Treatment
Component (`all` / `editable` mode, idle) 4 corner L-marks, blue @ 55%, 8px arms
Hovered component 2px solid blue @ 70% full outline (corner ticks fade out)
Selected component 2px solid blue @ 100% full outline + label badge (corner ticks fade out)
Annotated component Existing amber ::after dot (independent of corner ticks; uses ::after vs ::before)
Find-on-page match Existing emerald 2px outline (unchanged)
`selection` mode No ambient paint at all (unchanged)
`off` mode No ambient paint at all (unchanged)

Why corners

  • No edge accumulation. Four L-marks don't compound at shared boundaries the way perimeter outlines do. Nested layouts stay readable.
  • Reads as "discrete thing" not "highlighted thing". Industry convention (Figma selection handles, Sketch transform handles, Photoshop crop overlays) — corners signal "this is a unit of layout" without bordering its content.
  • Visual handoff to hover/selected stays clean. When you mouse over a component, its corner ticks disappear and the 2px outline takes over — no double-paint, no competing signals.

Implementation

  • `src/content/highlighter.ts` — `buildStyleSheet()` now emits a `::before` pseudo-element with eight `linear-gradient` background layers (one per corner-arm) instead of the old single `outline`. The rule is gated by `html[data-clay-slip-mode='all']` / `'editable'` (selection / off still produce no paint at all).
  • `TOKENS.ambient.{width, alpha, offset}` → `TOKENS.ambient.{tick, alpha}` to reflect the new geometry.
  • Same opacity-slider story (CSS-var multiplier still scales every state).
  • Three new contract tests in `tests/content/highlighter.test.ts`:
    1. The corner-tick rule is gated by `mode='all'` and `mode='editable'` (not `'selection'` / `'off'`).
    2. Each corner-tick selector excludes `[data-clay-slip-hover]` and `[data-clay-slip-selected]`.
    3. The pseudo-element is `::before` so it stays independent from the annotation dot's `::after`.

Layout caveat

The `::before` needs a positioning context. The rule sets `position: relative` (no `!important`) on each component, so:

  • Host has no positioning → ours wins, pseudo positions correctly. ✓
  • Host has its own `position: relative`/`absolute`/`fixed`/`sticky` → ours loses, pseudo positions against the host's context. ✓
  • Host has `position: static` AND has an absolute-positioned descendant pinning to a farther ancestor → that descendant would now reparent to the component. Real but rare risk; `all` is opt-in and the user can dial back to `selection` if they hit it.

This matches what we already do for `[data-clay-slip-selected]` and `[data-clay-slip-annotated]` (also `position: relative;` no important) — same risk profile, just on more elements when `all` is active.

Test plan

  • Toggle highlight mode through `off` → `selection` → `editable` → `all` (header pill or `h` shortcut). Confirm `off` and `selection` produce no ambient paint, `editable` shows corner ticks only on `[data-editable]` boxes, `all` shows them on every component.
  • In `all` mode, hover a component: corner ticks disappear, full hover outline appears.
  • In `all` mode, click a component: corner ticks disappear, selected outline + label badge appear.
  • In `all` mode, annotate a component (sticky note): corner ticks AND amber dot both render (different pseudo-elements).
  • Open intensity slider in Options → drag from 1 → 0: corner ticks fade out smoothly along with hover/selected (single CSS var still drives all states).
  • Visit a deeply-nested layout (e.g. an article body with nested sidebars/promos). Confirm corners don't overlap into busy edge stacks.

Made with Cursor

The previous "all" mode painted a 1px @ 18% blue outline around every
component on the page. Even at low opacity, full-perimeter outlines
accumulate visual mass at shared edges — three nested cards produce
three parallel lines within ~6px of each other — so the result still
read as "every component is highlighted" rather than "I can faintly
perceive the structure".

This commit replaces that rendering with four short L-shaped corner
accents at each corner of every component (8px arms, 1px wide, blue
@ 55%). Corners don't accumulate at shared edges, so nested layouts
stay visually quiet, while you can still tell at a glance which boxes
are Clay components.

Other changes:
- Hover (2px @ 70%) and selected (2px @ 100%) outlines are unchanged
  and stay visually dominant — the corner ticks excluded themselves
  via :not([hover]):not([selected]) so they fade out the moment you
  start inspecting an element.
- The :not() exclusion also keeps the corner-tick ::before from
  colliding with the selection label badge (also ::before), and the
  annotation dot remains on ::after so it's independent.
- TOKENS.ambient.{tick, alpha} replace TOKENS.ambient.{width, alpha,
  offset}; same intensity-slider story (CSS var multiplier).
- Three new contract tests assert the gating-by-mode and the :not()
  exclusions so a future stylesheet edit that breaks the visual
  handoff fails loudly.

Layout note: the corner-tick rule needs position: relative on the
host element to anchor the ::before. We omit !important so a host's
own positioning rule always wins (we then position against that
context, which is correct). The only failure mode is host=static +
absolute-positioned descendant pinning to a farther ancestor — opt-in
'all' mode, dial back to 'selection' if it bites.

Co-authored-by: Cursor <cursoragent@cursor.com>
@jjpaulino jjpaulino self-assigned this May 14, 2026
@jjpaulino jjpaulino merged commit 419e165 into master May 14, 2026
1 check passed
@jjpaulino jjpaulino deleted the feat/ambient-corner-ticks branch May 14, 2026 18:14
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