---
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 261–266 — 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.
