Skip to content

feat(landing): answer the pointer on the builders' band - #253

Open
wondopamine wants to merge 4 commits into
mainfrom
cursor-interaction
Open

feat(landing): answer the pointer on the builders' band#253
wondopamine wants to merge 4 commits into
mainfrom
cursor-interaction

Conversation

@wondopamine

Copy link
Copy Markdown
Collaborator

What this is

The builders' band — the one that says the harness was built for how it feels to use — now answers your hand. Move the pointer across it and drafting marks bloom on a fixed grid where it passes, then unwind and fade behind it. It is the only interactive decoration on the page, and it is on that band because a claim about how something feels is better shown once than asserted twice.

The interaction

Ported from the reference the builder chose (ga-hero-ink), read rather than guessed at, so the mechanic is theirs: a fixed cell grid, a band painted along the pointer's path, cells that accumulate so a pause saturates a patch while a sweep only tints it, and one decay that unwinds each cell back down a light-to-heavy ramp. A ragged deposit edge keeps the band's outline from reading as machine-drawn. The ramp is pre-drawn into an atlas once per resize, so a frame is N blits rather than N paths, and the loop exists only while marks do — an idle band costs no frames.

The words paint over the field rather than clearing a hole in it, which is what the reference does and what makes the field read as depth instead of damage.

The one thing not ported

The reference sets its ramp in ASCII glyphs on a monospace stack. A third typeface in product UI is a TYP-1 (L1) finding, and this site's only mono waiver is written to be confined to the builder's-note postcard. So the ramp is drawn instead, in the marks the sheet already uses:

Level Mark Where it comes from
Lightest construction point the hero blueprint's crossing dots
scale division the ground's tick ruler
registration cross the sheet's corner crosses
circled crossing how the drawing calls a point out
Heaviest snap handle the sheet ground's square handles

Same field, the drawing's own vocabulary, and no waiver needed. Say the word if you would rather have the literal ASCII version — that is a small change plus a widened TYP-1 waiver, and it is your call to make.

How it stays decoration

  • Tokens or nothing. Ink (--blueprint-ink), grid pitch (--ground-pitch, quartered, so every fourth mark lands on a division the sheet already draws) and decay (--motion-trail, new) are read at runtime. If any is missing the field declines to draw, because nothing is lost when decoration says no.
  • It never reaches the text. The marks pass behind the band's words, so the alpha ceiling is set where the darkest mark still leaves the statement and the action above 8:1 on the wash — nowhere near the A11Y-1 floor.
  • It withdraws when it is not wanted. No server render. No mount under prefers-reduced-motion (A11Y-5) or for a coarse pointer, hidden under forced-colors: active, no pointer events, aria-hidden (A11Y-6), and no information carried (MOT-3).
  • --motion-trail: 2400ms joins the motion token set. It is a decorative decay, not an interface duration, so MOT-1's 300ms bound governs it no more than it governs the landing reveal — the token comment says so, at the definition, where a reviewer can find it.

Related tickets

Relates to #201 (decorative layers are ungoverned). This is a second instance of that class, and it moves one of the three candidates along: the ground layer answered "no graphic behind a glyph" by staying outside every text container, and this one takes the other branch — it passes behind text deliberately, with the contrast contribution measured and stated. Two surfaces now, two different answers, both defensible. Whichever way the control lands, it should cover both. The forced-colours candidate from the same issue is followed here as a habit, and the third (a withdrawal breakpoint) is answered by pointer type rather than width.

Relates to #108 (Design: /) — same surface, same landing run.

Verification

pnpm build with its standards, Python and notices gates, typecheck, lint, token-audit, type-scan, a11y-static, 93 unit tests, and 52 end-to-end tests — all pass.

Three contract tests are new. One sweeps the pointer across the band, counts painted pixels, and waits for the band to return to blank paper (the decay is a decay, not a state). One asserts the canvas is absent under reduced motion — not paused, not shortened, absent. One asserts it is hidden under forced colours.

Also checked by hand in a browser: the Quick start link stays clickable through the field, a touch device gets no canvas at all, and the trail follows the band correctly after scrolling.

One real bug surfaced while building it, and it is worth knowing about beyond this component: the build minifies 2400ms to 2.4s, so a parseFloat on a duration token that trusts the authored unit gets 2.4. The trail decayed inside a single frame and drew nothing. The token read now checks the unit, and both paths are exercised — dev serves 2400ms, the build serves 2.4s.

🤖 Generated with Claude Code

The band that claims the harness was built for how it feels to use is
now the one place on the page that answers your hand: drafting marks
bloom on a fixed grid where the pointer passes and unwind behind it
over --motion-trail.

Ported from the ga-hero-ink reference — same cell grid, same band
painted along the path, same accumulate-on-linger and decay-through-a-
ramp, same pre-rendered atlas so a frame is blits rather than draws,
same occlusion by the words rather than a hole cut around them. One
thing is not ported: the reference sets its ramp in ASCII glyphs, and a
third typeface in product UI is a TYP-1 finding, so the ramp is drawn
in the marks the sheet already uses — construction point, scale
division, registration cross, circled crossing, snap handle.

It stays decoration by construction. Ink, grid pitch and decay come
from tokens or it declines to draw. The alpha ceiling is set where the
darkest mark still leaves the band's text above 8:1 on the wash. It
never renders on the server, never mounts under prefers-reduced-motion
or for a coarse pointer, hides under forced colours, takes no pointer
events, and is hidden from assistive technology.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@wondopamine
wondopamine requested a review from rezailmi August 24, 2026 10:42
@natashaannn

Copy link
Copy Markdown
Collaborator

Fixed 4 issues, documented here for shared learning purposes!

  • Added an automated test that clicks the Quick start link while ink marks are painted over it to confirm it still works as intended
  • Added an automated test that confirms that the ink pointer effect does not turn up on a touch device (I also tested this manually, confirmed nothing happens on a touch device)
  • The same landing page has 3 duplicated checks for reduced motion (used to detect if a user has enabled a setting on their device to minimize the amount of non-essential motion) across three elements: this ink pointer feature, the illo-video animation, and the harness run feature. Extracted the same check into a shared check function so the code is not rewritten three times
  • Fixed an invisible bug for when canvas is rotated (like on a tablet)

Few things to confirm with you:

  • Do you want the ink trail to appear for hover-capable stylus devices, like a Surface Pro / Surface Laptop / other Windows 2-in-1 ? Currently it is a bit ambiguous in the way it is written in the code.

Adds the two contract tests PR #253 claimed as manual-only (the Quick
start link stays clickable through the trail, and a touch pointer gets
no canvas at all), extracts the prefers-reduced-motion matchMedia check
into hooks/use-reduced-motion.ts to stop it being duplicated a third
time, and prunes trail cells that fall outside the grid after a resize.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@wondopamine

Copy link
Copy Markdown
Collaborator Author

Thanks so much @natashaannn.
We can ignore other form factors like ipad for this. Let's prioritise the laptop using the cursor for now.

Your changes about avoiding the duplicated checks (reducing the motion), will it be applied for any follow up works? I wasn't aware of it and not sure my agent will do this again or not haha

@natashaannn

natashaannn commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

Thanks so much @natashaannn. We can ignore other form factors like ipad for this. Let's prioritise the laptop using the cursor for now.

Your changes about avoiding the duplicated checks (reducing the motion), will it be applied for any follow up works? I wasn't aware of it and not sure my agent will do this again or not haha

Good question.. It's usually caught in a review. Let me see what I can implement in the code base right now to prevent it! Also feel free to merge this if Reza is ok with it haha. Also @wondopamine this can be resolved by installing the dx harness plugin hehe noticed your claude wasn't using it!

@rezailmi rezailmi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok to merge, but note that will require another deploy to airbase separately. @wondopamine @natashaannn

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.

3 participants