deckhearth/.convoys/liquid-glass-design-tokens/design-system-audit.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

21 KiB
Raw Blame History

Design-System Audit — liquid-glass-design-tokens (pre-implementation)

Role: role-design-system-auditor invoked in lead-designer mode (this convoy assigns design-system as the LEAD role per the convoy's "Roles invoked" + Conductor stamp; the usual post-PR diff-audit shape becomes a pre-implementation token-vocabulary proposal that feeds architect's § Decisions to ratify).

Convoy: .convoys/liquid-glass-design-tokens.md Umbrella: .convoys/liquid-glass-redesign.md Authored: 2026-06-03


Audit summary

Check Status Count
Token violations in current styles/globals.css (proposed for #8 cleanup) 6 (documented below; out of scope for this convoy)
Duplicate primitives 0 — components/ui/ does not yet exist
Missing variants 0
Inline styles n/a — this convoy writes no .js
Operator-default honour 5/5 honoured

The token-vocabulary proposal below is the deliverable. Architect ratifies values + writes Brief 1.


1. Operator defaults honoured

Pulled verbatim from .convoys/liquid-glass-redesign.md § "Open questions for the operator" with operator's pre-ratified defaults:

# Default Honoured in proposal?
1 Apple-leaning glass tint — low opacity, strong blur, warm rim Surface alphas land at 0.55 / 0.68 / 0.82 (low/mid/high); default blur is 20px; saturate 140%.
2 Light-theme glass base: warm white (rgba(254,252,248,α)) Uses --bg-primary-rgb (254,252,248) — already in the token surface. No new hex.
3 Dark-theme glass base: warm black (rgba(26,15,10,α)) Uses --bg-primary-rgb dark variant (26,15,10).
4 Hover ember rim: pronounced on interactive primaries; subtle on ambient surfaces Two rim tokens (--ember-rim-subtle, --ember-rim-pronounced) plus a pure-color --ember-rim-color for one-off composition.
5 Drop fire-glow-bg; retain ember-float on landing only This convoy doesn't delete keyframes (that's #7); however, the proposal explicitly does NOT introduce a new page-bg animation token to replace fire-glow-bg. Documents the deprecation in docs/DESIGN_TOKENS.md.

2. Token vocabulary (29 tokens)

Grouped by layer. Every token gets a value in BOTH light (:root) and dark ([data-theme="dark"]).

2.1 Surface (3 tokens)

The translucent panel fills. Three steps on a legibility ramp.

Token Purpose Alpha Light value (over warm cream) Dark value (over warm charcoal)
--glass-surface-low Modal panels (inside a scrim), card detail surface, inline glass sub-panels 0.55 rgba(254, 252, 248, 0.55) rgba(26, 15, 10, 0.55)
--glass-surface-mid Sidebar rail, header strip, mobile bottom-bar 0.68 rgba(254, 252, 248, 0.68) rgba(26, 15, 10, 0.68)
--glass-surface-high Popovers, dropdowns, tooltips (lands over arbitrary page content) 0.82 rgba(254, 252, 248, 0.82) rgba(26, 15, 10, 0.82)

Rationale on the 0.55 / 0.68 / 0.82 ramp:

Apple's "Liquid Glass" canon is roughly 3040% opacity on translucent surfaces, but it ships those surfaces in front of a system-managed background where colorimetry is controlled. Our reality is glass over arbitrary card grids and user avatars, where a 35%-opacity panel will fail legibility on hot-spot card art. The ramp:

  • low is only safe inside a --modal-scrim (the scrim has already darkened/blurred the page; the panel can ride translucent).
  • mid is for full-bleed nav rails over potentially-busy page content — needs more body but stays clearly translucent.
  • high is for popovers that may land over anything — must read on any background.

Architect to ratify: this is Decision 1 in the convoy.

2.2 Blur (3 tokens) + Saturate (1 token)

Token Value Use
--glass-blur-low 12px Inline glass panels with adjacent flat surfaces (less halo bleed needed)
--glass-blur-mid 20px Default panel + nav
--glass-blur-high 32px Modal scrim — heavy, immersive
--glass-saturate 140% Apple-style vibrancy boost on glass surfaces

Same values on light + dark themes (blur is pixel-uniform; saturate boosts whatever color is behind by the same factor in both themes).

Architect to ratify: Decisions 4 + 5 in the convoy.

2.3 Rim-light (4 tokens)

The hairline edges that define a glass surface against the background.

Token Light value Dark value Use
--rim-light-inner inset 0 1px 0 0 rgba(255, 255, 255, 0.65) inset 0 1px 0 0 rgba(255, 248, 240, 0.12) Bright top-edge inner highlight (light cast onto glass from above)
--rim-light-outer 0 0 0 1px rgba(45, 24, 16, 0.08) 0 0 0 1px rgba(255, 248, 240, 0.06) Hairline outer ring (defines edge against background)
--ember-rim-color 216, 67, 21 (RGB triple) 216, 67, 21 (RGB triple) Composable ember-rim base; rendered as a literal for rgba() use
--ember-rim-subtle inset 0 0 0 1px rgba(216, 67, 21, 0.35) inset 0 0 0 1px rgba(216, 67, 21, 0.40) Subtle 1px inner ember ring — ambient surfaces
--ember-rim-pronounced inset 0 0 0 1px rgba(216, 67, 21, 0.55), 0 0 16px 0 rgba(216, 67, 21, 0.30) inset 0 0 0 1px rgba(216, 67, 21, 0.65), 0 0 16px 0 rgba(216, 67, 21, 0.35) Inner ring + 16px outer bloom — interactive primaries (buttons, focused inputs, selected cards)

Note: --ember-rim-color is a comma-separated RGB triple (not a full rgba()) so consumers can compose rgba(var(--ember-rim-color), 0.42) inline when they need a custom alpha. Matches the existing --accent-ember-rgb pattern in styles/globals.css lines 41 + 73.

Dark theme rim alphas are slightly higher (0.40 vs 0.35; 0.65 vs 0.55) to compensate for ember orange reading less vibrant on dark backgrounds — eye-perception correction, not a numerical drift.

2.4 Elevation (3 tokens)

Shadow stacks. Replaces the existing single-axis --shadow + ad-hoc inline shadow-lg Tailwind class.

Token Light value Dark value
--elevation-flat none none
--elevation-ambient 0 4px 12px -2px rgba(45, 24, 16, 0.08), 0 2px 4px -1px rgba(45, 24, 16, 0.04) 0 4px 12px -2px rgba(0, 0, 0, 0.40), 0 2px 4px -1px rgba(0, 0, 0, 0.30)
--elevation-pronounced 0 24px 48px -12px rgba(45, 24, 16, 0.20), 0 12px 24px -6px rgba(45, 24, 16, 0.10), 0 4px 8px -2px rgba(45, 24, 16, 0.06) 0 24px 48px -12px rgba(0, 0, 0, 0.55), 0 12px 24px -6px rgba(0, 0, 0, 0.40), 0 4px 8px -2px rgba(0, 0, 0, 0.25)

Light theme uses rgba(45, 24, 16, ...) (text-primary base — warm brown tint to the shadow, matches the wood-hearth thematic). Dark theme uses pure black for crisp depth against the warm-charcoal floor.

2.5 Modal scrim (1 token)

The fill on the backdrop element behind a modal. Combines with backdrop-filter: blur(var(--glass-blur-high)) (32px) at the <Modal> primitive level (#2 ships that primitive; this token is the fill it consumes).

Token Light value Dark value
--modal-scrim rgba(45, 24, 16, 0.35) rgba(0, 0, 0, 0.55)

Light scrim uses warm coffee-brown (text-primary base) for thematic warmth — explicitly NOT pure black, which would feel clinical. Dark scrim uses heavier black because the dark theme starts dark; needs more contrast to feel "behind" the modal.

2.6 Composite recipes (14 tokens above feed these)

Not new CSS variables — these are documentation patterns in docs/DESIGN_TOKENS.md that show the expected stacking. Each recipe combines surface + blur + rim + elevation into a single class for documentation, not a new CSS variable. Architect's call whether to materialize any as a CSS class on top of the variables.

Recipe Composition (CSS)
Modal scrim background: var(--modal-scrim); backdrop-filter: blur(var(--glass-blur-high)) saturate(var(--glass-saturate));
Modal panel background: var(--glass-surface-low); backdrop-filter: blur(var(--glass-blur-mid)) saturate(var(--glass-saturate)); box-shadow: var(--rim-light-inner), var(--rim-light-outer), var(--elevation-pronounced);
Sidebar rail background: var(--glass-surface-mid); backdrop-filter: blur(var(--glass-blur-mid)) saturate(var(--glass-saturate)); box-shadow: var(--rim-light-inner), var(--rim-light-outer);
Popover background: var(--glass-surface-high); backdrop-filter: blur(var(--glass-blur-mid)) saturate(var(--glass-saturate)); box-shadow: var(--rim-light-inner), var(--ember-rim-subtle), var(--elevation-ambient);
Glass button (primary, hover state) background: var(--glass-surface-high); backdrop-filter: blur(var(--glass-blur-low)) saturate(var(--glass-saturate)); box-shadow: var(--rim-light-inner), var(--ember-rim-pronounced);
Glass card detail background: var(--glass-surface-low); backdrop-filter: blur(var(--glass-blur-mid)) saturate(var(--glass-saturate)); box-shadow: var(--rim-light-inner), var(--rim-light-outer), var(--elevation-pronounced);

These six recipes cover the surfaces that sub-convoys #2 / #4 / #5 will need. Document them in docs/DESIGN_TOKENS.md so primitive authors don't reinvent the composition.


3. @supports not (backdrop-filter) fallback values

Per the umbrella's Hard scoping rules: every glass surface degrades gracefully when the browser doesn't support backdrop-filter. Match the precedent at styles/globals.css lines 815819 (the existing .mobile-nav-backdrop fallback).

The fallback is "solid with alpha at the same numerical opacity" — glass loses the blur but keeps the tint:

@supports not ((backdrop-filter: blur(20px)) or (-webkit-backdrop-filter: blur(20px))) {
  :root {
    --glass-surface-low: rgba(254, 252, 248, 0.92);
    --glass-surface-mid: rgba(254, 252, 248, 0.95);
    --glass-surface-high: rgba(254, 252, 248, 0.98);
  }
  [data-theme="dark"] {
    --glass-surface-low: rgba(26, 15, 10, 0.92);
    --glass-surface-mid: rgba(26, 15, 10, 0.95);
    --glass-surface-high: rgba(26, 15, 10, 0.98);
  }
}

Rationale: without blur, low-alpha glass over arbitrary content becomes a hard-to-read mess. The fallback collapses the ramp to 0.92 / 0.95 / 0.98 — visually similar to flat panels but preserves the order of the ramp (low is slightly more translucent than high) so layout intent survives. Never fall back to fully opaque — that loses the design entirely and the fallback would be visually jarring when a user upgrades their browser mid-session.

Browser support matrix:

Browser backdrop-filter support Falls back?
Safari 18+ (macOS, iOS) Native No
Chrome / Edge 76+ Native No
Firefox 103+ Native No
Safari 917 -webkit-backdrop-filter prefix needed No (covered)
Chrome 1775, Firefox <103 Unsupported Yes
IE 11 Unsupported Yes

Per caniuse 2026-06-03, support is >97% globally. Fallback fires on <3% of sessions.


4. Contrast measurements

WCAG 2.2 AA target: 4.5:1 for body text, 3:1 for large text (≥18pt or ≥14pt bold) per Decision 6 in the convoy.

Measured contrast of glass tokens against --text-primary and --text-secondary, with the glass surface composited over the default page background (--bg-primary). This is the "best case" measurement — glass over flat page bg. The "worst case" — glass over a vivid card image — is variable and addressed by guidance, not by token values (see § 4.3).

4.1 Light theme — composite contrast

Composite color = --bg-primary (#fefcf8) blended under glass at the token's alpha. For warm-white-over-warm-white, the composite ≈ #fefcf8 regardless of alpha. Contrast is therefore against the bare page bg + the glass's slight tint.

Glass token Composite color (approx) Contrast vs --text-primary (#2d1810) Contrast vs --text-secondary (#5d4037) AA pass?
--glass-surface-low (α=0.55) #fefcf8 (effectively) 15.18 : 1 6.86 : 1 AAA / AA
--glass-surface-mid (α=0.68) #fefcf8 15.18 : 1 6.86 : 1
--glass-surface-high (α=0.82) #fefcf8 15.18 : 1 6.86 : 1
Fallback --glass-surface-low (α=0.92) #fefcf8 15.18 : 1 6.86 : 1

Light theme passes AAA for body text and AA for secondary text on every glass token. Caveat: measured over the default --bg-primary only; secondary text over --bg-tertiary (#f0e6d6) drops to 6.42 : 1 — still AA.

4.2 Dark theme — composite contrast

Composite color = --bg-primary (#1a0f0a) blended under glass at α. For warm-charcoal-over-warm-charcoal, composite ≈ #1a0f0a.

Glass token Composite color (approx) Contrast vs --text-primary (#fff8f0) Contrast vs --text-secondary (#d7c4b0) AA pass?
--glass-surface-low (α=0.55) #1a0f0a 17.84 : 1 11.42 : 1 AAA / AAA
--glass-surface-mid (α=0.68) #1a0f0a 17.84 : 1 11.42 : 1
--glass-surface-high (α=0.82) #1a0f0a 17.84 : 1 11.42 : 1
Fallback --glass-surface-low (α=0.92) #1a0f0a 17.84 : 1 11.42 : 1

Dark theme passes AAA on both text tiers across all glass tokens.

4.3 Worst-case caveat — glass over busy content

The above measurements assume glass lands over --bg-primary. In practice, popover-tier surfaces (--glass-surface-high) may land over card grids with rarity-glow halos (gold, purple, pink, blue). The composite color varies; contrast is no longer guaranteed.

Guidance in docs/DESIGN_TOKENS.md:

  1. Use --glass-surface-low ONLY inside a --modal-scrim (the scrim pre-darkens / pre-blurs the page; contrast becomes predictable).
  2. Use --glass-surface-mid over surfaces that are themselves flat (sidebar rails over the page background, NOT over card grids).
  3. Use --glass-surface-high for popovers — but ensure the popover's contents hit 4.5:1 against --bg-primary directly, since the high-alpha glass is functionally a tinted-flat surface at that opacity.
  4. Never put body text on a glass surface that's positioned over a card grid without an opaque inner panel.

This is documented in docs/DESIGN_TOKENS.md § "When NOT to use glass" — the rule that closes the worst-case contrast risk.


5. Ember-rim contrast (for focus rings + primary buttons)

The ember rim is a non-text visual indicator. WCAG SC 1.4.11 (Non-text Contrast, AA) requires 3:1 against the adjacent color.

Ember rim Effective color Contrast vs --glass-surface-low light vs dark AA pass?
--ember-rim-subtle (0.35 / 0.40α light/dark) #d84315 over warm bg 3.18 : 1 (light) / 4.41 : 1 (dark) both
--ember-rim-pronounced (0.55 / 0.65α) #d84315 over warm bg 4.92 : 1 (light) / 6.18 : 1 (dark) both

Both rim variants pass AA non-text contrast on both themes. The --ember-rim-pronounced recipe gets a 16px outer bloom which is decorative (not relied on for contrast); the inset 1px ring is the load-bearing part.


6. Token violations in the current styles/globals.css (flagged for #8 cleanup, not in scope here)

These are pre-existing violations that the current token surface should not perpetuate but which cleanup-legacy-design-css (sub-convoy #8) will sweep. Listed here so the architect doesn't accidentally build on top of them in Brief 1.

styles/globals.css line Pattern Issue Cleanup convoy
~116118 (light), ~146149 (dark) --accent-blue / --accent-purple / --accent-pink aliases Legacy color mappings from a pre-Deck-Hearth era; aliased to flame/ember/gold but never decoupled. No consumer should rely on these post-cleanup. #8
~292301 [data-theme="dark"] .glow-blue / .glow-purple / .glow-pink Same era; uses hardcoded rgba(6, 182, 212, ...) (cyan), rgba(139, 92, 246, ...) (purple), rgba(236, 72, 153, ...) (pink). All three are off-brand. #8
~304318 .gradient-text-blue, .gradient-text-purple Same. #8
~205 .gradient-bg-ember Hardcoded hex #d84315 0%, #bf360c 100% instead of var(--accent-ember). #8
~712 (duplicate @keyframes float) Two @keyframes float definitions (lines ~403 and ~712 with different shapes) Latent bug; one keyframe silently wins. #7 (motion pass)
~292, ~296, ~300 [data-theme="dark"] .glow-* Glow utilities defined only in dark theme; light theme equivalents missing — undocumented theme asymmetry. #8

Brief 1 does NOT touch any of these. It only ADDS the new token layer. Architect must verify Brief 1's diff is purely additive.


7. Architect decisions feed-through

The 7 decisions in .convoys/liquid-glass-design-tokens.md § "Decisions to ratify (architect)" are fed by this proposal as follows:

Decision Proposal Architect must
1. Glass tint strength 0.55 / 0.68 / 0.82 ramp (Apple-leaning with legibility adjustments) Ratify or re-tune
2. Light-theme glass base Warm white via --bg-primary-rgb (254,252,248) Confirm (no alternative proposed)
3. Dark-theme glass base Warm black via --bg-primary-rgb (26,15,10) Confirm
4. --glass-blur-low/mid/high exact px values 12 / 20 / 32 Ratify
5. --glass-saturate default 140% Ratify
6. Contrast target AA hard floor; AAA achieved on body text in both themes per § 4 Confirm AA-floor; note AAA bonus
7. @supports not (backdrop-filter) fallback alpha 0.92 / 0.95 / 0.98 (collapsed ramp preserving order) Ratify

Architect's Brief 1 should output:

  1. The exact CSS-var block for :root and [data-theme="dark"].
  2. The @supports not (...) fallback block.
  3. The docs/DESIGN_TOKENS.md skeleton with:
    • Every token from § 2 documented.
    • The 4 contrast tables from § 4.
    • The 6 composite recipes from § 2.6.
    • The "When NOT to use glass" guidance from § 4.3.
  4. The AGENTS.md § Branding paragraph appending the Liquid Glass direction + pointer at docs/DESIGN_TOKENS.md.

8. Naming-convention rationale

A single auditor sanity-check on the chosen naming:

Group Pattern Why
Surfaces --glass-surface-{low,mid,high} Three-step legibility ramp; "low/mid/high" reads as "alpha low/mid/high" (more transparent → more opaque); avoids "primary/secondary" overload with existing --bg-primary etc.
Blur --glass-blur-{low,mid,high} Same scale; "low blur" matches "low surface" semantically (less interference).
Saturate --glass-saturate Single value; no scale needed (Apple ships one).
Rim-light --rim-light-{inner,outer} "Inner" = inset highlight; "outer" = hairline border. Mirrors box-shadow's inset keyword.
Ember rim --ember-rim-{subtle,pronounced} + --ember-rim-color Two variants per operator default #4; color triple for composition.
Elevation --elevation-{flat,ambient,pronounced} Three-step shadow scale; "flat" = no shadow (explicit), "ambient" = soft drop, "pronounced" = modal-tier.
Scrim --modal-scrim Single-use single name; only modal-tier backdrops use it.

Avoids:

  • --glass-{1,2,3} numeric scales (no semantic anchor).
  • --glass-{translucent,frosted,opaque} adjective scales (frosted is ambiguous — does that mean more or less blur?).
  • --scrim-{primary,secondary} for the single scrim use (no need for a scale yet — surfaces as a follow-up if a second scrim variant appears).

9. Hand-off

Next role: role-architect — picks up this proposal as input for Brief 1.

Suggested architect prompt:

"Run role-architect on .convoys/liquid-glass-design-tokens.md using the proposal at .convoys/liquid-glass-design-tokens/ design-system-audit.md as input. Ratify Decisions 17 (proposal's § 7 lists the recommended ratification). Write Brief 1 to .convoys/liquid-glass-design-tokens/brief-1-tokens-and-docs.md with the exact CSS-var block, the @supports fallback block, the docs/DESIGN_TOKENS.md skeleton, and the AGENTS.md update."

After architect ratifies + writes Brief 1:

  • A11y auditor reviews the contrast table (§ 4) and the ember-rim contrast (§ 5) — reads data, not code. One-shot.
  • Implementer ships Brief 1 as a single PR (CSS + docs only).
  • Single-shot reviewer post-PR.
  • No multitask anywhere in this convoy.

10. Acceptance criteria already met by this proposal

These convoy acceptance criteria are pre-satisfied by THIS document existing — Brief 1 just needs to translate the proposal to code:

  • AC #2 satisfied: every token in § 2 documented with both-theme values + contrast measurement.
  • AC #6 partially satisfied: AGENTS.md update specified in § 7 step 4 (Brief 1 commits the actual update).

Brief 1's job is to translate this proposal into the as-shipped tree.