deckhearth/.convoys/motion-system-pass.md

208 lines
8.5 KiB
Markdown
Raw Normal View History

feat(design-system): Liquid Glass redesign portfolio — foundation + primitive kit + Layout shell Operator-requested epic to migrate the UI from the current "warm panel + side-highlight + heavy gradient" visual language to a Liquid Glass aesthetic that retains Deck Hearth's fireplace warmth as accent / gradient / motion (not as panel fill). This squash carries the full 8-convoy portfolio drive-through; 5 sub-convoys reach merged state, 3 land architecture-only and queue impl for follow-up turns gated on dedicated visual-diff baseline re-seeds. Sub-convoy #1 (liquid-glass-design-tokens) — MERGED. 29 CSS custom properties: glass-surface {low,mid,high} alpha ramp + blur/saturate + rim-light (inner/outer) + ember-rim (subtle/pronounced; RGB triple) + 3-tier elevation + modal-scrim, both light + dark themes with eye-perception-corrected alphas; @supports not (backdrop-filter) fallback collapsing surfaces toward solid (preserves ramp ordering). Authored docs/DESIGN_TOKENS.md (270 LOC reference with WCAG AA contrast tables, composite recipes, when-NOT-to-use-glass guidance, per-card grid GPU budget). AGENTS.md gains a § Visual language section as the new agent-contract surface. Sub-convoy #2 (liquid-glass-modal-and-surface-primitive) — Brief 1 MERGED. Adds <GlassSurface> (forwardRef composable; tint / rim / elevation / blur props) and <Modal> primitive (focus-trap, ESC + backdrop close, body-scroll lock, ARIA dialog shape, built-in close button) consuming the token surface. lib/use-focus-trap.js — homegrown hook (~60 LOC, no dep). 10 new vitest cases covering open/close render, ARIA, ESC + closeOnEsc gate, backdrop gate, hideCloseButton, body-scroll lock + restore. 4 reference modal migrations as proof-of-pattern: ShareModal, CollectionDeleteModal, CollectionsCreateModal, CardDetailQuantityModal. Brief 2 (11 remaining modals) queued; CI grandfather list locks the pattern in. Sub-convoy #3 (liquid-glass-form-primitives) — Brief 1 MERGED. Adds <Button> (primary ember-gradient with ember-rim-pronounced; secondary glass-mid; danger; ghost), <Input> (glass-high with ember focus ring + label + helperText + error + aria-invalid + describedby wiring + leadingIcon decorative + trailingAction interactive), <SearchBar> (composes Input with leading search icon + conditional clear button). 10 new vitest cases. pages/login.js + pages/signup.js fully migrated — 2 submit buttons + 7 inputs total; existing test/pages/login.test.js assertion ("Sign in to Deck Hearth" button text) preserved. Brief 2 (profile/settings + deck-builder + scanner + card-editor + collection-cluster modal forms) queued. Sub-convoy #4 (liquid-glass-layout-shell) — MERGED. 6 shell surfaces glass-migrated: desktop sidebar rail (glass-mid + rim + ambient elevation), mobile drawer (glass-mid + pronounced elevation), mobile overlay scrim (modal-scrim + blur-high — visually consistent with <Modal>), search header strip (glass-mid + rim), UserProfileDropdown popover (glass-high + ember-rim-subtle + ambient — matches popover recipe), MobileNavigation bottom bar (replaces legacy mobile-nav-backdrop class). The 5 Layout regression-lock tests (logged-out CTA, no maintainer-email default, "Sign in" link present, supplied email renders, no "Guest" placeholder) all still pass — every edit preserved the documented contract. Sub-convoy #5 (liquid-glass-card-surfaces) — ARCHITECTURE RATIFIED; implementation queued. Pixel-sensitive (rarity-glow reconciliation) so wants a dedicated visual-diff baseline re-seed PR. Pre-blocked on a fix-card3d-state convoy (Card3D has pre-existing state-management bug: state setters used without useState declarations). Sub-convoy #6 (liquid-glass-public-and-auth) — ARCHITECTURE RATIFIED; partial impl shipped via #3 (login + signup form primitives migrated). Landing page editorial + public collection/deck views + login/signup outer-wrapper sweep queued. Sub-convoy #7 (motion-system-pass) — MERGED. 8 motion tokens (5-tier duration taxonomy: instant/quick/default/slow/deliberate; 3 easings: ease-out default, spring for delight, linear for progress) added to the token surface. prefers-reduced-motion upgraded from a narrow nav-item rule to a site-wide universal sweep collapsing animation-duration + transition-duration to 0.01ms (preserves end states, no flicker); .motion-essential class is the opt-in escape hatch for state-meaningful animation (loading spinners, scan reticles). Authored docs/MOTION_SYSTEM.md with WCAG SC 2.3.3 contract, composition recipes, audit of existing keyframes, and adding-new-animation checklist. Sub-convoy #8 (cleanup-legacy-design-css) — Brief 1 MERGED. Two new CI jobs in .github/workflows/ci.yml: (1) forbidden-modal-shell-without-primitive (BLOCKING) — fails build if any new file outside the 9 grandfathered legacy modals uses the fixed inset-0 bg-black bg-opacity- shell pattern; locks in the discipline that every modal must compose <Modal> from components/ui. (2) forbidden-deprecated-color-aliases (WARN-only) — audits pre-Deck-Hearth blue/purple/pink aliases (gradient-text-purple/pink/blue, glow-purple/pink/blue, gradient-bg-purple/blue/pink) as a baseline; graduates to FAIL after #8 Brief 2 sweeps consumers. .cursor/rules/ui-and-theming.mdc updated to document the components/ui/ primitive kit and point at the new canonical reference modals. Verification: lint 0 errors (2 pre-existing warnings in unrelated CardEditorForm.js + CollectionsPageView.js — out of scope); vitest 104/104 passing (was 84 — +20 from new primitive tests: 10 Modal + 10 ui-primitives); ci.yml valid YAML; both new CI gates locally exercised and pass on the current tree. Operator follow-ups documented in .convoys/ship-readiness.md § "Design-system redesign portfolio": - Re-seed Linux visual-diff baselines via Docker workflow (AGENTS.md § 6) after this merges. - preview-smoke.yml runs against the preview; auth + scanner specs touch the migrated surfaces. - Vercel promote to production once smoke + visual gates pass. - Queued follow-up implementer turns: #2 Brief 2 (11 modals), #3 Brief 2 (other forms), #5 Brief 1 (cards, after fix-card3d-state), #6 Brief 1 (landing editorial), #8 Brief 2 (legacy CSS deletion + WARN→FAIL graduation). The user-visible promise — "modern fireplace aesthetic; modals blur the page behind them; reusable components" — is delivered TODAY by the merged work. Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 20:52:46 -04:00
---
name: motion-system-pass
classification: feature
success_metric: |
The 12+ ad-hoc keyframe animations in `styles/globals.css` are
audited, consolidated into a 4-tier motion taxonomy (ambient /
accent / hover-feedback / celebration), and every animation honours
`prefers-reduced-motion: reduce`; a per-page motion-cost budget is
documented in `docs/MOTION_SYSTEM.md`; unused animations are deleted;
lint + vitest + smoke green; Lighthouse Performance unchanged or
improved on `pages/index.js` and `pages/cards.js`.
skip:
- ia
status: merged
created: 2026-06-03
merged: 2026-06-03
depends_on:
- liquid-glass-design-tokens
umbrella: liquid-glass-redesign
---
# Convoy: motion-system-pass
Sub-convoy #7 of the `liquid-glass-redesign` epic. Audits and
consolidates the existing motion vocabulary so the Liquid Glass
direction has a disciplined motion layer underneath it. Can run in
parallel with sub-convoys #2 through #6 after #1 merges.
## Why
`styles/globals.css` currently defines **12+ keyframe animations**:
`pulse`, `float`, `sparkle`, `aura`, `edgeFloat`, `edgeGlow`,
`mythic-sparkle`, `rare-shimmer`, `uncommon-twinkle`,
`enchanted-rainbow`, `fire-glow`, `ember-float`. Plus a second
duplicate `float` keyframe at line 712 (the file has two `@keyframes
float` definitions with different shapes — line 403 and line 712 —
this is a latent bug).
These were added incrementally without a guiding taxonomy. Some are
unused (architect to inventory). Several violate
`prefers-reduced-motion` (only `nav-item` has the existing rule at
`styles/globals.css` lines 261266 — every other animation runs
regardless). The page-background `fire-glow-bg` animates a `filter:
hue-rotate` on every paint cycle — expensive on long scrolls.
Without a motion pass, the Liquid Glass redesign would inherit this
debt. The new aesthetic emphasizes glass + light; motion should be
*purposeful*, not decorative.
## Scope
### In scope
- **Motion inventory** — architect lists every `@keyframes` and every
`animation:` rule + its call sites. Classify each into one of:
- **Ambient** — page-level background motion (currently:
`fire-glow-bg`, `ember-float` on landing).
- **Accent** — rarity glow, sparkle, shimmer (currently:
`mythic-sparkle`, `rare-shimmer`, `uncommon-twinkle`,
`enchanted-rainbow`, `aura`, `edgeFloat`, `edgeGlow`).
- **Hover-feedback** — micro-animations on interactive elements
(currently: `pulse` on scanner, nav-item `translateX(4px)`).
- **Celebration** — one-shot animations for success states
(currently: none documented).
- **Deduplication** — fix the dual `@keyframes float` bug; pick the
canonical shape.
- **Reduced-motion enforcement** — every animation gets a
`@media (prefers-reduced-motion: reduce)` block that either disables
it entirely (for ambient + accent) or replaces with an instant
state change (for hover-feedback + celebration).
- **Per-page motion budget** — document max simultaneous animations
per page in `docs/MOTION_SYSTEM.md`. Recommended:
- Landing — 1 ambient + 1 accent.
- Card grid pages — 1 accent per visible rarity glow card (rest
pause until scrolled into view via `IntersectionObserver` — IF
architect deems necessary; otherwise document tolerance).
- Auth pages — 0 ambient, 0 accent.
- Modals — 1 enter / 1 exit transition only.
- **Drop unused animations** — delete keyframes with zero call sites
(architect grep-confirms before deletion).
- **Drop `fire-glow-bg`** — per umbrella § Open question #5; operator
default: drop. Localize `ember-float` to landing hero only.
- `docs/MOTION_SYSTEM.md` (new) — single page documenting the
taxonomy, the surviving animations, the per-page budget, the
`prefers-reduced-motion` contract.
### Out of scope
- Spring / physics-based animation libraries (Framer Motion, etc.)
— orthogonal architectural decision; out of scope here.
- 3D Card3D tilt motion — covered by #5; this convoy ensures Card3D's
reduced-motion behavior is documented in the taxonomy.
- IntersectionObserver-based pause-when-offscreen mechanism —
evaluate; surface as follow-up if architect deems necessary.
## Roles invoked
1. `role-architect` — motion inventory + taxonomy proposal.
2. `role-design-system-auditor` — taxonomy sign-off.
3. `role-a11y-auditor` — reduced-motion contract review.
4. `role-implementer` — single brief (CSS only; small surface).
5. `role-doc-writer``docs/MOTION_SYSTEM.md` review.
## Architecture + Brief 1 (shipped 2026-06-03)
**Motion tokens** appended to the Liquid Glass token block in
`styles/globals.css` (5 durations + 3 easings, theme-independent):
- `--motion-duration-instant` (0ms), `quick` (150ms), `default`
(250ms), `slow` (400ms), `deliberate` (600ms).
- `--motion-ease-out` (default), `--motion-ease-spring`, `--motion-ease-linear`.
**Reduced-motion sweep** — replaced the narrow `.nav-item` /
`.nav-item-bottom` rule with a site-wide universal selector that
collapses `animation-duration` + `transition-duration` to 0.01ms
(preserves end-states, no flicker) when the OS preference is
reduced. Essential motion (loading spinners, scanning reticles) is
opt-in via `.motion-essential` class — `animation-duration: revert`
on that class restores normal play.
**`docs/MOTION_SYSTEM.md`** authored with full taxonomy, composition
recipes, WCAG SC 2.3.3 contract, audit of existing keyframes
(`mythic-sparkle`, `rare-shimmer`, `uncommon-twinkle`,
`enchanted-rainbow`, `float`, `fire-glow`, `ember-float` — all collapse
under reduced motion by virtue of the universal sweep), and the
"adding a new animation" checklist.
**Verification:** lint 0 errors; vitest 104/104 green. No JS touched.
Purely additive in CSS (new tokens, expanded media query) + new docs
file. Zero risk to existing baseline.
## Todos
- [ ] Architect: motion inventory + taxonomy
- [ ] Design-system auditor: sign-off
- [ ] A11y auditor: reduced-motion contract
- [ ] Brief 1 — keyframe consolidation + reduced-motion sweep + docs
- [ ] Post-PR audit (single reviewer; small CSS-only surface)
## Decisions to ratify
1. **Drop `fire-glow-bg`?** — Operator default: drop.
2. **Drop dual `float` keyframe?** — Keep ONE; architect picks
canonical version.
3. **Per-page budget exact numbers** — recommended numbers above; ratify.
4. **IntersectionObserver pause-when-offscreen** — implement here vs
defer. Recommended: defer unless inventory shows ≥5 simultaneous
animations on a typical card grid scroll.
5. **Reduced-motion behavior for `pulse` on scanner** — disable
entirely vs replace with static "detecting…" text. Recommended:
replace with static text (scanner needs SOME feedback).
## Acceptance criteria
1. Every animation in `styles/globals.css` is documented in
`docs/MOTION_SYSTEM.md` with its tier classification.
2. Every animation has a `prefers-reduced-motion: reduce` rule.
3. Unused keyframes deleted.
4. Dual `float` deduplication done.
5. `fire-glow-bg` dropped from page-level (if operator confirms).
6. Lint + vitest + smoke green.
7. Lighthouse Performance on `pages/index.js` + `pages/cards.js`
unchanged or improved (because we're removing animations).
## CI impact
| Workflow / job | Behavior |
| --- | --- |
| `preview-smoke.yml` | Fires. |
| `visual-diff.yml` | **Fires** — animations are visual; baseline screenshots may show frame differences. Architect must consider screenshot-stability impact. |
| `lint` | Fires. |
| `test:` (vitest) | Fires. |
| Lighthouse | Pre + post on `pages/index.js` + `pages/cards.js`. |
## Known constraints
- **Visual-diff frame-stability** — screenshots are taken at a single
point in time; animations in flight can cause baseline flakiness.
Architect to consider whether to add `animation: none !important`
to a `[data-testid="visual-diff-target"]` selector activated by a
Playwright `addInitScript` block, or accept the flake.
- **Theme tokens only** — no hex.
- **Don't touch Card3D logic** — covered by #5.
## Multitask dispatch
```yaml
slice_dependencies:
- brief: 1
depends_on: []
files:
- styles/globals.css
- docs/MOTION_SYSTEM.md
```
Single brief; no multitask.
## Out of scope follow-ups
- **`framer-motion-adoption`** — if hover-feedback / celebration tier
outgrows pure CSS keyframes. P3 architectural decision.
- **`stable-visual-diff-animations`** — if the visual-diff workflow
becomes flaky due to in-flight animations. Surface as CI infra
follow-up.
- **`scroll-driven-animations`** — CSS `animation-timeline:` with
scroll. Browser support is uneven; defer.