* 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> * fix(use-focus-trap): preserve named useFocusTrap export for ScannerPageView The portfolio squash inadvertently overwrote the pre-existing lib/use-focus-trap.js (named `export function useFocusTrap(active)` returning a ref — used by ScannerPageView, line 21) with a default- only export shaped for the new `<Modal>` primitive. Vercel build failed: "Export useFocusTrap doesn't exist in target module". Fix: the file now exports BOTH — - `useFocusTrap(active)` (named, original) — returns a ref; pre-Liquid-Glass call sites (ScannerPageView) keep working. - `useFocusTrapContainer({ active, containerRef, ... })` (default, new) — takes a caller-owned ref so panel refs can forward through forwardRef chains (Modal.js consumes this shape). Both hooks are commented to document which to use when. Modal.js imports default already, so no change needed there. Verified: npm run build passes (was failing in CI); lint 0 errors; vitest 104/104 still green. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
294 lines
12 KiB
Markdown
294 lines
12 KiB
Markdown
---
|
||
name: cleanup-legacy-design-css
|
||
classification: feature
|
||
success_metric: |
|
||
The legacy gradient-text / glow / accent-blue/purple/pink utility
|
||
surface is deleted from `styles/globals.css`; no consumer remains
|
||
(verified by `rg`); hardcoded hex sweep across `components/**` +
|
||
`pages/**` complete; `.cursor/rules/ui-and-theming.mdc` is
|
||
updated to make Liquid Glass tokens + primitives the canonical
|
||
pattern; `forbidden-legacy-design-css` lint / grep gates are wired
|
||
to prevent regression; lint + vitest + smoke green.
|
||
skip:
|
||
- ia
|
||
- ux
|
||
status: ci-gates-shipped-deletion-queued
|
||
created: 2026-06-03
|
||
ci_gates_shipped: 2026-06-03
|
||
depends_on:
|
||
- liquid-glass-design-tokens
|
||
- liquid-glass-modal-and-surface-primitive
|
||
- liquid-glass-form-primitives
|
||
- liquid-glass-layout-shell
|
||
- liquid-glass-card-surfaces
|
||
- liquid-glass-public-and-auth
|
||
- motion-system-pass
|
||
umbrella: liquid-glass-redesign
|
||
---
|
||
|
||
# Convoy: cleanup-legacy-design-css
|
||
|
||
Sub-convoy #8 of the `liquid-glass-redesign` epic. Strict-deletion
|
||
convoy — ships last, after every other sub-convoy has migrated off the
|
||
legacy surface. No new styling. No new components. Only deletions and
|
||
CI gates to prevent re-introduction.
|
||
|
||
## Why
|
||
|
||
Without an enforced cleanup at the end, the legacy utility classes
|
||
(`gradient-text-blue`, `glow-blue`, `accent-purple`, etc. — every one
|
||
inherited from a pre-Liquid-Glass era) will quietly reappear in future
|
||
PRs as developers' muscle memory pastes the old patterns. The way to
|
||
prevent that is:
|
||
|
||
1. Delete the legacy surface from `styles/globals.css`.
|
||
2. Sweep any remaining hex colors that ought to be tokens.
|
||
3. Update `.cursor/rules/ui-and-theming.mdc` to make the Liquid Glass
|
||
primitives the canonical pattern.
|
||
4. Wire `forbidden-*` grep gates in CI so the legacy patterns can't
|
||
land again.
|
||
|
||
This convoy is **predicated on every other sub-convoy having shipped
|
||
first**. If any sub-convoy is still in flight, this convoy waits.
|
||
|
||
## Scope
|
||
|
||
### In scope — deletions from `styles/globals.css`
|
||
|
||
- Legacy gradient-text utility classes:
|
||
- `.gradient-text-blue` (lines ~304–310)
|
||
- `.gradient-text-purple` (lines ~312–318)
|
||
- `.gradient-text-pink` — does it exist? `rg` to confirm.
|
||
- `.gradient-text-gold` — keep ONLY if still consumed.
|
||
- `.gradient-text-flame` / `.gradient-text-ember` — keep ONLY if
|
||
still consumed; these are brand-aligned.
|
||
- Legacy glow utility classes:
|
||
- `[data-theme="dark"] .glow-blue` (line ~292)
|
||
- `[data-theme="dark"] .glow-purple` (line ~296)
|
||
- `[data-theme="dark"] .glow-pink` (line ~300)
|
||
- Legacy ad-hoc glow utilities (replaced by `--ember-rim-*` /
|
||
`--rim-light-*` tokens):
|
||
- `.fire-glow` (line ~209) — confirmed dropped by #5.
|
||
- `.ember-glow` (line ~213) — confirmed dropped by #5.
|
||
- Legacy gradient-bg utility classes — drop if unused post-migration:
|
||
- `.gradient-bg-fire` (line ~196)
|
||
- `.gradient-bg-golden` (line ~200)
|
||
- `.gradient-bg-ember` (line ~204)
|
||
- Legacy accent-color mappings:
|
||
- In `:root` (line ~115–118): `--accent-blue`, `--accent-purple`,
|
||
`--accent-pink` — delete; no consumer should remain.
|
||
- In `[data-theme="dark"]` (line ~146–149): same three vars.
|
||
- Legacy `.btn-*` utility classes — drop ONLY if #3 migrated every
|
||
consumer and operator confirms. Conservative default: keep `.btn-*`
|
||
as thin aliases of `<Button>` styling for backward compatibility;
|
||
delete in a future polish convoy.
|
||
- Legacy `.input-field` / `.search-bar` — same treatment as `.btn-*`.
|
||
- Legacy `.card` (line ~285) — verify usage; likely dropped (replaced
|
||
by `<GlassSurface>`).
|
||
- Card-hover-panel ad-hoc rules:
|
||
- `.card-side-panel` / `.card-panel-enter` / `.card-panel-enter-active`
|
||
(lines ~530–565) — drop ONLY if #5 migrated the hover panel to
|
||
`<GlassSurface>`.
|
||
|
||
### In scope — hex sweep
|
||
|
||
`rg "#[0-9a-fA-F]{3,6}" components/ pages/ --type js` — every match
|
||
that ISN'T a deliberate brand color in `styles/globals.css` (i.e.
|
||
every hex in `.js` files) must be converted to a theme token. Common
|
||
culprits:
|
||
- `bg-[#xxx]` Tailwind arbitrary-value classes.
|
||
- `style={{ backgroundColor: '#xxx' }}` inline.
|
||
- `stroke="#xxx"` / `fill="#xxx"` on SVG paths (these may be
|
||
intentional and untokened — architect's call per-SVG).
|
||
|
||
### In scope — rule + doc updates
|
||
|
||
- `.cursor/rules/ui-and-theming.mdc`:
|
||
- Drop the "Two systems coexist" warning paragraph (no longer true
|
||
post-migration).
|
||
- Make `<GlassSurface>`, `<Modal>`, `<Button>`, `<Input>`,
|
||
`<SearchBar>` the canonical primitives in the "Common UI patterns
|
||
to reuse" table.
|
||
- Add a "Forbidden patterns" section listing the deleted utility
|
||
classes + the new grep gate names.
|
||
- `AGENTS.md` § "Branding":
|
||
- Update the Liquid Glass paragraph from #1 with the as-shipped
|
||
convoy series + pointers at `docs/DESIGN_TOKENS.md` and
|
||
`docs/MOTION_SYSTEM.md`.
|
||
- `docs/DESIGN_TOKENS.md`:
|
||
- Final audit — every token documented; every contrast measurement
|
||
re-checked.
|
||
- `docs/MOTION_SYSTEM.md`:
|
||
- Final audit.
|
||
|
||
### In scope — CI gates
|
||
|
||
Add grep gates to `.github/workflows/ci.yml` mirroring the existing
|
||
`forbidden-endpoints` + `forbidden-stale-strings` + `forbidden-client-
|
||
side-llm-keys` pattern:
|
||
|
||
- `forbidden-legacy-color-tokens` — fails the build if any of
|
||
`--accent-blue`, `--accent-purple`, `--accent-pink`,
|
||
`gradient-text-(blue|purple|pink)`, `glow-(blue|purple|pink)` appear
|
||
in `components/**`, `pages/**`, `styles/**`.
|
||
- `forbidden-hex-in-jsx` — fails the build if `#[0-9a-fA-F]{3,6}`
|
||
appears in `components/**/*.js` or `pages/**/*.js` (with a curated
|
||
allowlist for legitimate SVG paths if any remain).
|
||
- `forbidden-legacy-utility-classes` (optional, architect ratifies) —
|
||
fails the build if `btn-(flame|ember|gold)` / `input-field` /
|
||
`search-bar` classNames appear post-migration.
|
||
|
||
### Out of scope
|
||
|
||
- Any new design work.
|
||
- Any new component.
|
||
- Any structural change.
|
||
|
||
## Roles invoked
|
||
|
||
1. `role-architect` — sweep inventory + brief.
|
||
2. `role-design-system-auditor` — verify zero design regressions.
|
||
3. `role-doc-writer` — `.cursor/rules/ui-and-theming.mdc`, AGENTS.md.
|
||
4. `role-implementer` — single brief; cleanup-only.
|
||
5. `role-reviewer` — single post-PR review.
|
||
|
||
## Brief 1 (shipped 2026-06-03) — CI grep gates only
|
||
|
||
The disciplined-discipline work: lock in the design-system rules
|
||
that the sub-convoys established, so future PRs can't regress.
|
||
|
||
**Two new CI jobs** added to `.github/workflows/ci.yml`:
|
||
|
||
1. **`forbidden-modal-shell-without-primitive`** — FAIL (blocking).
|
||
Greps `pages/` + `components/` for `fixed inset-0 bg-black
|
||
bg-opacity-` and FAILS if any match is found outside the
|
||
grandfathered legacy list (the 9 modals still queued for #2
|
||
Brief 2: CollectionsSuccessModal, CollectionsEditModal,
|
||
CollectionEditModal, CardDetailDeckModal, ScannerPageView,
|
||
UploadImageModal, CollectionSelectionModal, OCRSettings,
|
||
pages/decks.js). New modal files MUST use the `<Modal>`
|
||
primitive from `components/ui/`. As sub-convoy #2 Brief 2 lands
|
||
migrations, entries delete from the grandfathered list — never
|
||
silently.
|
||
2. **`forbidden-deprecated-color-aliases`** — WARN-only (audit
|
||
baseline). Greps `pages/` + `components/` for the legacy
|
||
pre-Deck-Hearth aliases (`gradient-text-purple/pink/blue`,
|
||
`glow-purple/pink/blue`, `gradient-bg-purple/blue/pink`).
|
||
Currently warning-only with a baseline count; graduates to
|
||
FAIL once #8 Brief 2 sweeps all known consumers (see § Brief
|
||
2 below).
|
||
|
||
**Rule updates** in `.cursor/rules/ui-and-theming.mdc`:
|
||
- Documented the `components/ui/` primitive kit (`<GlassSurface>`,
|
||
`<Modal>`, `<Button>`, `<Input>`, `<SearchBar>`).
|
||
- Pointed at `docs/DESIGN_TOKENS.md` + `docs/MOTION_SYSTEM.md` as
|
||
canonical surfaces.
|
||
- Updated "Common UI patterns to reuse" table — Modal row now
|
||
points at the primitive + canonical references.
|
||
|
||
## Brief 2 (queued for follow-up) — actual deletion
|
||
|
||
Only run AFTER:
|
||
1. `liquid-glass-modal-and-surface-primitive` Brief 2 (the 11
|
||
remaining modal migrations) is merged.
|
||
2. `liquid-glass-form-primitives` Brief 2 (form sweep) is merged.
|
||
3. `liquid-glass-card-surfaces` Brief 1 (card surface migration)
|
||
is merged.
|
||
4. `liquid-glass-public-and-auth` Brief 1 (public-view editorial)
|
||
is merged.
|
||
|
||
**Targets:**
|
||
- Delete `--accent-blue` / `--accent-purple` / `--accent-pink`
|
||
aliases from `styles/globals.css`.
|
||
- Delete `.gradient-text-blue` / `.gradient-text-purple` /
|
||
`.gradient-text-pink` / `.glow-blue` / `.glow-purple` /
|
||
`.glow-pink` / `.gradient-bg-purple` / `.gradient-bg-blue` /
|
||
`.gradient-bg-pink` utility classes.
|
||
- Delete `.fire-glow` / `.ember-glow` utility classes (replaced
|
||
by `--ember-rim-{subtle,pronounced}`).
|
||
- Delete the page-level `fire-glow-bg` background animation
|
||
(replaced by localized `ember-float` on landing hero only via #6).
|
||
- Delete `mobile-nav-backdrop` legacy class (Layout's
|
||
MobileNavigation now uses `--glass-surface-mid` directly via #4).
|
||
- Graduate `forbidden-deprecated-color-aliases` job from WARN to
|
||
FAIL (exit 1 instead of exit 0).
|
||
|
||
## Todos
|
||
|
||
- [ ] Architect: full deletion inventory + grep-confirm zero consumers
|
||
- [ ] Design-system auditor: zero-regression sign-off
|
||
- [ ] Doc-writer: rule + AGENTS.md + token-doc updates
|
||
- [ ] Brief 1 — deletions + sweep + CI gates + doc updates
|
||
- [ ] Post-PR review
|
||
|
||
## Decisions to ratify
|
||
|
||
1. **Keep `.btn-*` and `.input-field` / `.search-bar` as thin aliases
|
||
or delete?** Conservative: keep as aliases; delete in a future
|
||
polish convoy. Aggressive: delete now (cleaner end state). Operator
|
||
ratifies.
|
||
2. **`forbidden-hex-in-jsx` SVG allowlist** — how to handle legitimate
|
||
inline SVG hex (e.g. mana-symbol SVGs in `components/ManaSymbols.js`).
|
||
Recommended: per-file allowlist via `// eslint-disable-next-line`
|
||
or a path-based exclusion in the grep gate.
|
||
3. **Should `gradient-text-gold`, `gradient-text-flame`,
|
||
`gradient-text-ember` survive?** These are brand-aligned. Likely:
|
||
keep, but document in `docs/DESIGN_TOKENS.md`.
|
||
4. **`.fire-glow-bg` already dropped by #7** — confirm.
|
||
|
||
## Acceptance criteria
|
||
|
||
1. Every deletion in § Scope is executed; no consumer remains
|
||
(`rg` verifies).
|
||
2. Hex sweep complete; `forbidden-hex-in-jsx` passes.
|
||
3. Rule + AGENTS.md + docs updated.
|
||
4. CI gates added and verified by negative test (introduce a forbidden
|
||
pattern in a scratch commit; verify CI fails; revert).
|
||
5. Lint + vitest + smoke green.
|
||
6. Visual-diff baselines unchanged (cleanup should not affect render).
|
||
|
||
## CI impact
|
||
|
||
| Workflow / job | Behavior |
|
||
| --- | --- |
|
||
| `preview-smoke.yml` | Fires. |
|
||
| `visual-diff.yml` | **Fires** — should show zero diff (cleanup is non-visual). Any diff is a bug. |
|
||
| `lint` | Fires + new grep gates. |
|
||
| `test:` (vitest) | Fires. |
|
||
| New grep gates | `forbidden-legacy-color-tokens`, `forbidden-hex-in-jsx`, optionally `forbidden-legacy-utility-classes`. |
|
||
|
||
## Known constraints
|
||
|
||
- **Zero net design change.** This convoy is deletion-only. Visual
|
||
diff MUST be empty. Any pixel change is a sign that #1–#7 left work
|
||
on the table; back out and address.
|
||
- **No-go zones honoured** — `components/Layout.js.backup`,
|
||
`scripts/add-*.js` / `fix-*.js` / `seed-*.js` graveyard untouched.
|
||
|
||
## Multitask dispatch
|
||
|
||
```yaml
|
||
slice_dependencies:
|
||
- brief: 1
|
||
depends_on: []
|
||
files:
|
||
- styles/globals.css
|
||
- .github/workflows/ci.yml
|
||
- .cursor/rules/ui-and-theming.mdc
|
||
- AGENTS.md
|
||
- docs/DESIGN_TOKENS.md
|
||
- docs/MOTION_SYSTEM.md
|
||
# ... and any .js files where hex sweep finds consumers
|
||
```
|
||
|
||
Single brief. No multitask.
|
||
|
||
## Out of scope follow-ups
|
||
|
||
- **`delete-btn-utility-aliases`** — if Decision 1 keeps the legacy
|
||
`.btn-*` aliases here, delete them in a small polish convoy 3-6
|
||
months post-redesign.
|
||
- **`storybook-adoption`** — natural next step once the primitive set
|
||
is stable.
|
||
- **`design-tokens-as-tailwind-theme`** — if Tailwind composition
|
||
shape converges on the same patterns repeatedly. P3 DX.
|