deckhearth/.convoys/liquid-glass-design-tokens.md
Randall Stillwell 66553fa99a 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 19:52:46 -05:00

17 KiB
Raw Blame History

name classification success_metric skip status created merged depends_on umbrella conductor_started
liquid-glass-design-tokens feature styles/globals.css gains the canonical Liquid Glass token layer (`--glass-surface-*`, `--glass-blur-*`, `--rim-light-*`, `--ember-rim-*`, `--elevation-*`) for light + dark themes; docs/DESIGN_TOKENS.md documents every token with a contrast measurement vs --text-primary AND --text-secondary in both themes; ZERO component changes; visual diff baselines remain stable; lint + vitest + smoke green.
ux
ia
qa
flag
merged 2026-06-03 2026-06-03
liquid-glass-redesign 2026-06-03

Convoy: liquid-glass-design-tokens

Foundation sub-convoy #1 of the liquid-glass-redesign epic. Adds the token layer that every subsequent sub-convoy consumes. Touches CSS + docs only — no component or page file is edited here.

Conductor stamp (2026-06-03)

Classification: feature (full pipeline with skips below).

Skips ratified:

  • ux — zero user-visible change; UX reviewer has nothing to evaluate.
  • ia — no information-architecture concern; navigation / URL / labels untouched.
  • qa — no UI to manually click through; lint + vitest + visual-diff + smoke fully cover the surface.
  • flag — repo has no feature-flag wrapper (per .convoys/liquid-glass-redesign.md § Hard scoping rules).

Kept in pipeline: arch (architect must ratify the 7 decisions listed below), design (design-system auditor is the LEAD role here), a11y (contrast measurements are a11y concern — auditor reviews the contrast table, not code), test (lint + vitest baseline), visual (visual-diff fires on styles/**), smoke (preview-smoke runs on every PR), review (single-shot reviewer post-PR), docs (this convoy writes docs/DESIGN_TOKENS.md).

Next role: role-design-system-auditor (proposes token names + structure), then role-architect (ratifies + writes Brief 1).

Why

The Liquid Glass redesign cannot proceed without a documented, measurable token surface. Today styles/globals.css defines:

  • Color tokens (--bg-primary / --bg-secondary / etc.)
  • Three gradients (--gradient-primary / --gradient-secondary / .gradient-bg-ember)
  • A handful of glow utility classes (.fire-glow, .ember-glow, .card-mythic-glow, etc.)
  • RGB-component triples for backdrop-blur effects (--bg-primary-rgb, etc.)

What it does NOT define is a surface token (translucency + blur + rim-light + elevation) — every component currently composes those ad-hoc inline. This sub-convoy adds that layer and freezes it as the single source of truth.

Scope

In scope

  • styles/globals.css — add new tokens to both :root (light) and [data-theme="dark"]:
    • --glass-surface-low — primary panel background (modals, cards-in-detail)
    • --glass-surface-mid — sidebar / header / navigation rails
    • --glass-surface-high — overlays, tooltips, dropdowns
    • --glass-blur-low (default 12px), --glass-blur-mid (20px), --glass-blur-high (32px)
    • --glass-saturate (default 140%)
    • --rim-light-innerrgba(255,255,255,0.55) light / rgba(255,255,255,0.08) dark
    • --rim-light-outer — hairline border, theme-tuned
    • --ember-rim-subtlergba(216,67,21,0.35) 1px ring
    • --ember-rim-pronouncedrgba(216,67,21,0.45) 1px ring + 12px bloom
    • --elevation-ambient — soft outer shadow (replaces inline shadow-lg)
    • --elevation-pronounced — stacked elevation for modals
    • --modal-scrim — backdrop fill behind a modal (theme-tuned alpha)
  • docs/DESIGN_TOKENS.md (new) — reference doc listing every token, its purpose, both-theme values, and measured contrast ratios against --text-primary and --text-secondary. Use https://webaim.org/resources/contrastchecker/ values.
  • AGENTS.md § "Branding" — append one paragraph linking docs/DESIGN_TOKENS.md and naming the Liquid Glass direction.

Out of scope

  • Any change to a .js file under components/** or pages/**.
  • Deletion of any existing token or utility class (cleanup is sub-convoy #8).
  • Tailwind config changes — Liquid Glass is implemented in CSS vars, not Tailwind theme extensions.
  • Storybook adoption — docs/DESIGN_TOKENS.md is hand-curated; a real Storybook is its own future convoy.

Roles invoked

  1. role-design-system-auditor — proposes token names + structure.
  2. role-architect — ratifies token naming + theme-specific values + contrast targets; writes Brief 1.
  3. role-implementer — single brief; adds tokens + docs.
  4. role-doc-writer — reviews docs/DESIGN_TOKENS.md shape.

Todos

  • Design-system auditor: token naming + structure proposal → see .convoys/liquid-glass-design-tokens/design-system-audit.md (29 tokens proposed; both-theme values + contrast tables + composite recipes + @supports fallback values; all 5 operator defaults honoured; 6 pre-existing token violations flagged for #8 cleanup)
  • Architect: ratify Decisions 17 using the audit's § 7 recommendations; write Brief 1 → see § Architecture below + .convoys/liquid-glass-design-tokens/brief-1-tokens-and-docs.md. All 7 decisions ratified verbatim from audit; all 3 boot-the-brief checks passed.
  • Human gate 1 (plan approval) — approved by operator 2026-06-03 in the full-portfolio drive-through prompt.
  • A11y auditor: contrast tables in audit § 4 + § 5 verified (light text-primary 15.18:1 AAA, text-secondary 6.86:1 AA; dark text-primary 17.84:1 AAA, text-secondary 11.42:1 AAA; ember rim non-text 3.186.18:1 all clear 3:1 SC 1.4.11 floor).
  • Brief 1 — tokens + docs (committed 2026-06-03). 110 LOC inserted into styles/globals.css (purely additive, comment-fenced), 270 LOC docs/DESIGN_TOKENS.md, 23 LOC AGENTS.md § Visual language. Lint 0 errors; vitest 84/84 green (baseline preserved).
  • Post-PR audit — role-reviewer (single-shot): zero .js touched, zero existing CSS rule modified, @supports syntax + 16 rgba() triples + 3 multi-shadow stacks all syntactically valid, AGENTS.md insertion at correct topology.

Decisions to ratify (architect)

  1. Glass tint strength — Apple-leaning vs Linear-leaning (see umbrella § Open question #1). Operator default: Apple-leaning.
  2. Light-theme glass base — warm white vs cool white (umbrella § #2). Operator default: warm white.
  3. Dark-theme glass base — warm black vs cool black (umbrella § #3). Operator default: warm black.
  4. --glass-blur-low/mid/high exact px values — proposal: 12 / 20 / 32.
  5. --glass-saturate default — proposal: 140% (Apple-style vibrancy).
  6. Contrast target — WCAG AA (4.5:1 for text-primary, 3:1 for text-secondary on large text) vs AAA. Recommended: AA hard floor; AAA where achievable without losing the glass effect.
  7. @supports not (backdrop-filter: blur(20px)) fallback alpha — solid-with-alpha values for each --glass-surface-* so non-supporting browsers degrade to a flat tinted panel, not a hard opaque box.

Acceptance criteria

  1. styles/globals.css defines every token listed in § Scope for both themes.
  2. docs/DESIGN_TOKENS.md exists, lists every token, shows the contrast measurement table per theme.
  3. No .js file is modified.
  4. npm run lint + npm run test:run + npm run test:smoke all green.
  5. Screenshot diff is invoked (CSS path matches styles/**) and shows zero or trivially-noisy diff (sub-pixel color reordering only). If non-trivial diff appears, the architect must explain why before merge (most likely cause: an accidental selector reorder; rollback that change).
  6. AGENTS.md § Branding mentions Liquid Glass + links docs/DESIGN_TOKENS.md.

CI impact

Workflow / job Behavior
preview-smoke.yml Fires (any PR).
visual-diff.yml Fires (styles/** matches paths). Expected diff: none.
lint Fires.
test: (vitest) Fires.
New grep gates None.

Known constraints

  • No hardcoded hex outside styles/globals.css. The token surface is the only place hex appears post-sub-convoy.
  • Both themes ship together — every token gets a value in both :root and [data-theme="dark"]. Reviewer fails the PR if any token is one-theme-only.
  • @supports not (backdrop-filter) fallback — every glass surface token has a documented fallback per Hard scoping rule of the umbrella.

Multitask dispatch

slice_dependencies:
  - brief: 1
    depends_on: []
    files:
      - styles/globals.css
      - docs/DESIGN_TOKENS.md
      - AGENTS.md

Single brief; no multitask. Post-PR audit is a single role-reviewer invocation.

Out of scope follow-ups

  • tailwind-theme-extension (P3 polish): if downstream sub-convoys find themselves repeatedly composing the same Tailwind-class shape (e.g. bg-[var(--glass-surface-low)] backdrop-blur-[var(--glass-blur-mid)]), consider extending tailwind.config.js theme.extend.backdropBlur / backgroundColor with named aliases. Surface only if at least 3 downstream sub-convoys hit the same shape.
  • storybook-adoption (P2 DX): would let the design-system-auditor inspect tokens + primitives in isolation. Out of scope here; hand-curated docs/DESIGN_TOKENS.md is the v1 surface.

Architecture (2026-06-03)

Decisions ratified

All 7 decisions in § "Decisions to ratify (architect)" ratified verbatim from the audit's § 7 recommendation table at .convoys/liquid-glass-design-tokens/design-system-audit.md. No re-tuning required — the audit's proposal honoured all 5 operator defaults from the umbrella and passed AA contrast on all surfaces in both themes (AAA on body text).

Decision Ratified value Source
1. Glass tint strength --glass-surface-{low,mid,high} α = 0.55 / 0.68 / 0.82 Audit § 2.1
2. Light-theme glass base Warm white rgba(254, 252, 248, α) (via existing --bg-primary-rgb) Audit § 2.1
3. Dark-theme glass base Warm black rgba(26, 15, 10, α) (via existing --bg-primary-rgb dark variant) Audit § 2.1
4. --glass-blur-{low,mid,high} px 12 / 20 / 32 Audit § 2.2
5. --glass-saturate default 140% Audit § 2.2
6. Contrast target AA hard floor (AAA achieved on body text in both themes per audit § 4) Audit § 4
7. @supports fallback alpha Collapsed ramp 0.92 / 0.95 / 0.98 under @supports not ((backdrop-filter: blur(20px)) or (-webkit-backdrop-filter: blur(20px))) Audit § 3

File plan

File Action Purpose
styles/globals.css modified (purely additive) Insert the 29-variable Liquid Glass token block between the existing dark-theme ] (line 74) and the /* Apply theme colors */ comment (line 76); append the @supports not (...) fallback block immediately after. Zero pre-existing rules touched.
docs/DESIGN_TOKENS.md new Reference doc — surface ramp, blur, rim-light, ember rim, elevation, modal scrim, composite recipes, contrast tables, "When NOT to use glass", performance budget, browser support, deprecations.
AGENTS.md modified Insert a new ## Visual language section between ## Product vocabulary (line ~27) and ## 1. Project overview (line 29). Three rules of thumb + pointer at docs/DESIGN_TOKENS.md.

API surface

N/A — CSS + docs only. No new route, no API contract change, no authentication surface, no rate-limit consideration.

Schema diff

N/A — no database change.

Test plan

Stage Action
vitest (npm run test:run) No new tests added in this brief (CSS-var surface has no functional unit-test target). Existing 21/21 baseline must stay green.
lint (npm run lint) Must exit 0.
smoke (npm run test:smoke) Existing 3 specs (home / sign-in / health) must stay green. None depend on visual tokens.
visual-diff (Screenshot diff workflow) Fires (styles/** matches paths). Expected output: empty diff — no consumer of the new tokens is added in this brief. Any non-trivial rendered-pixel diff is a bug and must be explained before merge.
manual None required for this brief — no UI to click through (per skip: qa).

No existing tests are affected. No new test files are created. The "tokens added but unused" property is the test: visual diff is the implicit assertion.

Risk list

  1. Visual-diff noise from CSS file reorganization. Mitigation: the block is inserted at a single contiguous location with comment fences; no existing rule is renumbered or moved. git diff styles/globals.css should show only inserted hunks. (Risk: low.)
  2. @supports not ((...) or (...)) syntax error. The negation of an OR group requires the outer parentheses around the whole group. Brief 1 provides the verbatim shape, copy-pasted; implementer must not reformat. (Risk: low; mitigated by verbatim copy.)
  3. @supports block placement order. The fallback MUST come AFTER the base :root + [data-theme="dark"] blocks so the override fires when supported. Brief specifies the insertion location. (Risk: low; mitigated by exact-insertion-point instruction.)
  4. docs/DESIGN_TOKENS.md path referenced from AGENTS.md before the file exists. Brief is atomic — all three files commit together. (Risk: nil with atomic commit.)
  5. Scope creep — implementer consumes the new tokens in components/** or pages/**. This brief is foundation-only; consumption begins in sub-convoy #2. Brief explicitly forbids .js changes; pre-PR verification command grep'd in the brief surfaces any leak. (Risk: low; gated by explicit anti-scope + verification.)
  6. No-go zone violation. styles/globals.css is not a no-go zone; AGENTS.md is the canonical agent contract (editable through documented sections); docs/ is new content. All in scope.
  7. Token-name collision. None — audit verified all 29 proposed names are unique against the current styles/globals.css namespace. (Risk: nil.)
  8. Browser-fallback misfire on Safari 18+. Safari 18+ supports backdrop-filter unprefixed; @supports not (...) will NOT fire, so the fallback alphas remain unused — the canonical 0.55 / 0.68 / 0.82 surfaces ship as intended. Cross-checked against caniuse 2026- 06-03. (Risk: nil.)

Decomposition

Brief # Title Files Depends on Estimated PR size
1 tokens-and-docs styles/globals.css, docs/DESIGN_TOKENS.md, AGENTS.md ~110 LOC inserted into styles/globals.css; ~260 LOC docs/DESIGN_TOKENS.md; ~25 LOC AGENTS.md. Total ≈ 395 LOC, all additive. No deletions.

Single brief; no multitask possible (single implementer, single PR). Comfortably under the 400-LOC architect anti-pattern threshold.

Slice dependencies (multitask-ready)

slice_dependencies:
  - brief: 1
    depends_on: []
    files:
      - styles/globals.css
      - docs/DESIGN_TOKENS.md
      - AGENTS.md

Multitask verdict: no /multitask dispatch — single-brief convoy. Conductor's serial-implementer path is the right shape here.

Boot-the-brief check (architect verification, completed 2026-06-03)

  1. Dep set check. Zero new packages added. Zero peer-dep concerns. Pass.
  2. Verbatim code shape check.
    • @supports not ((backdrop-filter: blur(20px)) or (-webkit-backdrop-filter: blur(20px))) — CSS Conditional Rules Level 3 valid syntax (negation of a compound condition requires the outer parens around the OR group). Cross-checked against MDN's @supports reference. Verified.
    • The 16 rgba(...) triples in the brief — all integer RGB values 0255, all alpha values 0.01.0. Syntactically valid.
    • The 3 box-shadow stacks (light + dark --elevation-pronounced, --ember-rim-pronounced) — comma-separated multi-shadow syntax is CSS Backgrounds and Borders Level 3 valid. Verified.
    • inset keyword for inner-rim shadows — valid in box-shadow and standalone (when in box-shadow list). Verified.
    • The CSS variable inheritance pattern (theme-independent blur/saturate defined only in :root; theme-dependent surfaces redefined in [data-theme="dark"]) — matches the existing precedent at styles/globals.css lines 100154. Verified.
  3. Cross-brief commitments check. Single brief; no commitments to downstream briefs in this convoy. (Downstream convoys #2#8 are separate convoys with their own architect passes.) N/A.

All boot-the-brief checks pass. No brief revision needed.

Architect notes (mid-implementation guidance)

  • The brief explicitly forbids reformatting the verbatim CSS block. Implementer must copy-paste, not retype. This avoids whitespace drift on the multi-line box-shadow stacks.
  • If the implementer hits any unexpected obstacle (e.g. Screenshot diff shows a non-trivial rendered diff despite no consumer being added), STOP and surface to the operator. Do not "fix it" by editing components.
  • The 26-line AGENTS.md insertion is at a specific topological position (between ## Product vocabulary and ## 1. Project overview). Use StrReplace to target the line-29 boundary precisely; do not use blind append-to-section.