ADR-064: Hero Eye Swirl
Sanctions one decorative graphic on the site, a slow ink-and-teal swirl that rises from the portrait's eye in the homepage hero, as a named exception to the accent-as-cursor, flat-surface and no-infinite-loops rules of ADR-062. The motion is one slow, continuous, CSS-only rotation. Amended 2026-10-24: the swirl turns behind the head, centred behind the far eye, and never paints over the face.
Last updated
Status
Accepted (2026-10-12 — the owner chose the design, directed the build and merged PR #126; amended the same day to allow continuous rotation, see Decision; amended 2026-10-24 to move the swirl behind the head, see Amendment)
Context
ADR-062 binds the site to the Pezza design system. Three of its rules bear on the homepage hero:
- The Crossfade Cyan accent is a cursor (links, focus rings, the reading playhead) and never decoration.
- Surfaces are flat and photography carries the texture; no decorative graphics.
- No infinite loops in product UI; the sanctioned motions are finite or scroll-driven.
The hero is the one place the site introduces a person rather than a document. The owner explored a graphic that makes the portrait less literal: a swirl that starts at the eye nearest the copy column and dissolves leftward into the page ground, leaving the rest of the face untouched. Four directions were mocked on a design canvas on 2026-10-12; the owner chose the variant recorded here (“D3”) and asked for it to be built.
The chosen variant uses three dark ink ribbons, a deep-teal band (accent-700) and a
single bright cyan edge (accent-500). That is the accent used as decoration, on a
decorative graphic. An agent following ADR-062 as written would have to refuse it, so
the exception needs a record.
Decision Drivers
- One expressive moment, clearly bounded: the exception must not read as licence for decoration elsewhere.
- Brand rules that still hold should keep holding: colour stays on the token ramp, the face stays readable, reduced-motion is honoured.
- Zero JavaScript: constitution rule 7; the hero ships no client script today.
- No new text-over-graphic risk: copy keeps its protection gradient.
Considered Options
Option 1: Do not build it
Pros: ADR-062 stays unamended.
Cons: discards a design the owner reviewed and chose.
Option 2: Monochrome line-art only (canvas variant “B”)
Pros: needs no accent exception if the cyan line is dropped.
Cons: the owner preferred the ribbon variant and asked for more colour, not less.
Option 3: Ink ribbons with a teal band, as a named exception (chosen)
Pros: matches the reviewed design; every colour is an existing token; the exception is one component on one page.
Cons: ADR-062’s accent rule gains its first exception.
Option 4: The same graphic with a continuous rotation
Pros: identical to the canvas mock, which looped.
Cons: breaks the no-infinite-loops rule for no visible gain; one slow revolution outlasts almost every visit.
This option was first rejected and then adopted on the same day. Three finite versions shipped (one eased revolution, one five-minute revolution, three revolutions) and the owner reported each as stalled or stuck: a decorative motion that comes to rest reads as a fault. See the amendment under Decision.
Decision
Adopt Option 3. The homepage hero may carry one decorative graphic, the eye
swirl, implemented as src/components/atoms/HeroSwirl.astro. It amends ADR-062 as
follows, and nowhere else:
| ADR-062 rule | Exception |
|---|---|
Accent surfaces only through link and ring | HeroSwirl may fill one ribbon per repeat with accent-700 and stroke one edge with accent-500 |
| Surfaces are flat; no decoration | HeroSwirl is the single sanctioned decorative graphic, on the homepage hero only |
| No infinite loops in product UI | HeroSwirl turns continuously, one revolution every 110 seconds (amended 2026-10-12) |
Rules that are not relaxed:
- Reduced motion: the swirl does not animate at all under
prefers-reduced-motion: reduce. - Slow and singular: one transform on one element, no faster than a revolution every 110 seconds. No second loop anywhere, and the hero cursor stays finite.
- Tokens only: ribbon colours come from the ink and accent ramps. No new hues, no colour gradients. Transparency is carried by masks.
- The face stays clear (amended 2026-10-24): the swirl never paints over the head. See Amendment.
- Copy protection: the swirl sits beneath the hero’s protection gradient.
- Decorative means hidden: the graphic is
aria-hiddenand takes no pointer events. - No JavaScript: geometry is computed at build time by
src/utils/swirl.ts; motion and placement are CSS.
The swirl renders at every width. From the lg breakpoint up the portrait has the
hero’s right half to itself and the swirl runs beneath the protection gradient.
Below lg the portrait is a band above the copy, and the swirl is confined to
that band and fades out with it. Between md and lg the portrait used to sit
beside and behind the copy, which left the swirl no ground clear of the text; the
band layout was extended up to lg so the swirl could appear there.
Any second decorative graphic, or any other decorative use of the accent, needs its own ADR.
Amendment (2026-10-24): behind the head
Feedback on the live site was that a swirl rising over the eye read as gratuitous. The owner wanted it kept as a background element: “keep the spiral behind the head, behind the right eye”. Options were mocked on the real hero (behind the head, a faint ghost, a small iris swirl, linework, and a transparent pupil showing the swirl or a miniature of it); the owner chose the swirl behind the head with a normal pupil.
- Centre: behind the far eye (
--eye-x: 0.575,--eye-y: 0.421in the uncropped photograph), set on the swirl stage only. - Behind the head: the swirl sits in
.hero-swirl-clip, whose mask knocks out the portrait’s silhouette (src/assets/img/portrait-silhouette.png, 512px, alpha only, 11 KB), placed with the same cover-fit and zoom as the photograph. The face is the original photograph and keeps its own dissolve. - No seam: the silhouette’s knock-out fades out across the portrait box’s outer sixth, where the photograph has already dissolved, so the head’s cut edge at the box boundary does not show. The mask is not clipped to the box, so the swirl still sweeps across the ground to the left.
- Strength: the diagonal cut-away mask is removed (the silhouette replaces it).
The swirl is strongest at its centre and a quiet backdrop beyond (
--halo). In dark mode the ribbons use lighter inks (ink-800, ink-600, ink-500) and stronger halo on the black ground left of the head, easing off toward the bright wall. - Regenerating:
pnpm portrait:silhouetterebuilds the silhouette with Apple Vision (scripts/swift/segment-subject.swift, macOS 14+, on-device). Re-run it after replacing or reframing the portrait, along with the eye position.
Consequences
Positive
- The hero gains a distinctive moment without a new colour, font, dependency or script.
- The exception is one component; removing it restores ADR-062 exactly.
Negative
- ADR-062 is no longer exception-free, and the
pezza-designskill’s hard rules now differ from this site in one named place. - The swirl’s position is derived from the portrait’s framing values. Reframing or
replacing the photograph means re-measuring the eye position and re-running
pnpm portrait:silhouette(macOS only).
Neutral
- Inline SVG adds a few kilobytes of markup to the homepage; the Lighthouse and bundle gates apply unchanged.
Validation
src/utils/__tests__/swirl.test.tspins the generated geometry.src/components/atoms/__tests__/HeroSwirl.test.tspins: decorative semantics, no script, no literal colours.e2e/index.spec.tspins: the swirl is present in the hero at desktop, tablet and phone widths, the tablet portrait sits above the copy, nothing adds horizontal scroll, and the graphic is hidden from assistive technology.e2e/hero-swirl-behind.spec.tspins (2026-10-24): the clip masks out the silhouette, the swirl is centred behind the far eye, and the middle of the face renders pixel-identical with the swirl shown and hidden at 1440 and 1024px.pnpm quality:cipasses; Lighthouse budgets unchanged.
References
- ADR-062 — the rules this record amends
- ADR-048 — CSS-native motion
- Design canvas “Hero Eye Swirl Options”, variant D3 (2026-10-12)
Date: 2026-10-12
Participants: Chris Pezza
Outcome: Accepted