diff --git a/.convoys/unify-glass-panel-surfaces.md b/.convoys/unify-glass-panel-surfaces.md index b7a8675..8edeb8b 100644 --- a/.convoys/unify-glass-panel-surfaces.md +++ b/.convoys/unify-glass-panel-surfaces.md @@ -361,3 +361,284 @@ build on in-flight migrations. - **`storybook-adoption-for-glass-surface`** — once the primitive has the full prop matrix (`tint`, `blur`, `rim`, `elevation`, `cornerLights`), it's a natural Storybook candidate. P2 DX. + +## Architecture + +Run date: 2026-06-04. Architect: role-architect (this convoy). +Reads: this convoy file, `AGENTS.md`, `.cursor/rules/*.mdc`, +`components/ui/GlassSurface.js`, `components/ui/Modal.js`, +`components/ui/StatCard.js`, `components/Layout.js`, +`components/ui/TopSearchBar.js`, `components/BulkSelectionToolbar.js`, +`pages/login.js`, `pages/signup.js`, `pages/index.js`, +`pages/profile.js`, `pages/settings.js`, +`pages/community/collections.js`, +`components/CollectionsPageView.js`, `styles/globals.css` +(`.card`, `.glass-panel`, `.glass-panel-strong`, `.page-header-glass`, +the `--corner-light-*-subtle` tokens added in PR #118, the +`--bg-secondary-rgb` token used by today's auth cards), +`.github/workflows/ci.yml`, `test/components/ui-primitives.test.js`, +`test/components/Layout.test.js`. + +### Decisions ratified + +The convoy file's four open Decisions are resolved as follows. +Implementer briefs cite these and MUST NOT renegotiate them mid-flight +(if a brief discovers a reason to revisit, escalate via the mid-convoy +scope-expansion process in `role-architect.md`). + +**D1. `` `cornerLights` prop shape.** Add +`cornerLights = 'subtle' | 'chrome' | 'none'`, **default `'subtle'`**. +- `'subtle'` → applies the 4-layer gradient with + `--corner-light-warm-subtle` / `--corner-light-cool-subtle` (matches + what `.glass-panel-strong` ships today; PR #118). +- `'chrome'` → applies the 4-layer gradient with full-intensity + `--corner-light-warm` / `--corner-light-cool`. Used by the Layout + sidebar nav-chip and TopSearchBar header — those keep their inline + styles for now; this prop value exists so future floating chrome + doesn't have to re-handroll the gradient. +- `'none'` → no transparent border, no radials, no `--chip-border-base` + layer. Identical to today's primitive output. Used for the CardItem + grid path and any other GPU-budget-constrained tile that legitimately + must skip the gradient-border treatment. + +The gradient-border technique is the verbatim 4-layer recipe from +`.glass-panel-strong` (`styles/globals.css` post-PR-#118): a +padding-box solid linear-gradient of the fill, two border-box radial +gradients for the corner catch-lights (warm 0%↔100%, cool 100%↔0%), +and a border-box `var(--chip-border-base)` base layer. Combined with +`border: 1px solid transparent` so the gradient renders through the +border. + +**D2. `.card` class retire-or-keep.** **Retire the class entirely.** +All 8 consumers migrate to `glass-panel rounded-3xl p-{6|4}`. The +`.card` block in `styles/globals.css` is deleted. Rationale: a single +glass vocabulary across the app is the convoy's success metric; +keeping `.card` as a documented opaque fallback creates two parallel +panel languages for future agents to choose between, which is exactly +the kind of design-system bifurcation the convoy was scoped to +eliminate. The GPU-budget concern that motivated keeping `.card` is +specific to `` grid-mode (a non-`.card` consumer) — none +of the 8 `.card` sites face that constraint (they're profile cards, +settings panels, and a single community-page list tile, not a +multiplied-per-thumbnail surface). + +Aggressive path; **risk** = one of the 8 sites has a hidden reason +to be opaque that the architect didn't catch. Mitigation: Brief 5 +includes a "if any consumer breaks visually post-migration, revert +that one consumer to inline `style={{ background: +'var(--bg-secondary)' }}` and document the exception in the brief's +post-merge note" escape hatch. That keeps the class deletion in the +PR while leaving an explicit per-site fallback. If that escape hatch +fires for more than 1 of the 8 consumers, the brief's reviewer is +expected to push back and propose holding `.card` after all. + +**D3. Mobile-drawer corner-light intensity.** Use +`.glass-panel-strong` (subtle tier). The drawer is a ~256px-wide +fixed-position surface that covers ~1/3 of a mobile viewport. At +full-intensity (`'chrome'`) the corner radials would dominate the +drawer's interior nav text; at subtle they read as "elevated panel" +without competing with content. The drawer is also a hidden surface +most of the time — full intensity buys nothing for the rare moments +it's open. + +**D4. CI gate scope.** Gate **inline-style usage only** — +`var(--glass-surface-(low|mid|high))` appearing inside a JSX +`style={{ background: ... }}` (or `backgroundColor:`, `background:` +in template-string form) under `components/**` or `pages/**`. CSS +class definitions in `styles/globals.css` (where the tokens are +LEGITIMATELY chained to compose `.glass-panel` / `.glass-panel-strong` +/ `.page-header-glass`) are not gated. Allowlist entries: the +Layout sidebar nav-chip block (`components/Layout.js` L858-861) and +the TopSearchBar header block (the equivalent inline-style block in +`components/ui/TopSearchBar.js`) — both intentionally retain +`'chrome'`-tier handrolled gradients and were ratified as such in +PR #116. The `` primitive itself is also allowlisted +(it sets the background internally; that's its job). Brief 7 ships +this gate as a separate `forbidden-bespoke-glass-surface` job in +`ci.yml`, modeled on the existing `forbidden-modal-shell-without-primitive` +gate's grep-and-allowlist shape. + +### File plan + +| File | Action | Purpose | Brief | +| --- | --- | --- | --- | +| `components/ui/GlassSurface.js` | modified | Add `cornerLights` prop (`'subtle' \| 'chrome' \| 'none'`, default `'subtle'`); compose 4-layer gradient + `border: 1px solid transparent` when not `'none'` | 1 | +| `test/components/ui-primitives.test.js` | modified | Add 3 corner-light assertions covering the 3 `cornerLights` values | 1 | +| `pages/login.js` | modified | Replace L82-88 inline glass imitation with `
` | 2 | +| `pages/signup.js` | modified | Replace L227-232 inline glass imitation with `
` | 2 | +| `components/Layout.js` | modified | Sidebar profile dropdown (L88-96): drop inline background+blur+shadow, add `className="glass-panel-strong rounded-xl"`. Mobile drawer (L698-709): drop inline background+blur+shadow, add `className="glass-panel-strong"` and keep the slide-in transform classes. Sidebar nav-chip block (L858-861) untouched (chrome tier). | 3 | +| `components/ui/TopSearchBar.js` | modified | UserMenu dropdown (L214-222): drop inline background+blur+shadow, add `className="glass-panel-strong rounded-xl"`. Header block (chrome tier) untouched. | 3 | +| `test/components/Layout.test.js` | modified | Add regression-lock assertion: sidebar profile dropdown and mobile drawer carry `.glass-panel-strong` class. | 3 | +| `components/BulkSelectionToolbar.js` | modified | L47 toolbar: replace `bg-white border-gray-200` with `glass-panel-strong rounded-2xl`. L117 dropdown: same swap on `rounded-lg` → `rounded-xl glass-panel-strong`. Sweep interior `text-gray-{600,700}` to `var(--text-primary/secondary)`; `hover:bg-gray-{50,100}` to `nav-item-hover`; `text-red-600 hover:bg-red-50` to `var(--accent-danger)` + ember-tinted hover. | 4 | +| `pages/profile.js` | modified | 3 `.card` → `glass-panel rounded-3xl p-6` swaps. | 5 | +| `pages/settings.js` | modified | 2 `.card` → `glass-panel rounded-3xl` swaps (one `p-4`, one `p-6`). | 5 | +| `pages/community/collections.js` | modified | 1 `.card` → `glass-panel rounded-3xl` swap (preserve `group cursor-pointer hover:shadow-lg transition-all`). | 5 | +| `components/CollectionsPageView.js` | modified | 1 `.card` → `glass-panel rounded-3xl` swap (preserve `hover:shadow-xl transition-all cursor-pointer group`). | 5 | +| `styles/globals.css` | modified | **Delete** the `.card { ... }` rule (the class block that lives near L757). No other CSS changes. | 5 | +| `pages/index.js` | modified | Landing nav bar (L61-72): replace handrolled inline style with `