413 lines
21 KiB
Markdown
413 lines
21 KiB
Markdown
|
|
# 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 30–40% 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 815–819 (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:
|
|||
|
|
|
|||
|
|
```css
|
|||
|
|
@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 9–17 | `-webkit-backdrop-filter` prefix needed | No (covered) |
|
|||
|
|
| Chrome 17–75, 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 |
|
|||
|
|
| --- | --- | --- | --- |
|
|||
|
|
| ~116–118 (light), ~146–149 (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 |
|
|||
|
|
| ~292–301 | `[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 |
|
|||
|
|
| ~304–318 | `.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 1–7 (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:
|
|||
|
|
|
|||
|
|
- [x] AC #2 satisfied: every token in § 2 documented with both-theme
|
|||
|
|
values + contrast measurement.
|
|||
|
|
- [x] 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.
|