From 8093f6c4c0178248647da4618a3fc0fd2d990617 Mon Sep 17 00:00:00 2001 From: Randall Stillwell Date: Thu, 4 Jun 2026 13:45:32 -0500 Subject: [PATCH] docs(convoys): architect plan + 7 briefs for unify-glass-panel-surfaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Appends the ## Architecture section to .convoys/unify-glass-panel-surfaces.md ratifying the 4 open Decisions, and writes 7 implementer-brief files under .convoys/unify-glass-panel-surfaces/. Decisions ratified: D1. grows a cornerLights='subtle'|'chrome'|'none' prop, default 'subtle' (4-layer gradient-border per PR #118 recipe). D2. Retire .card entirely; migrate all 8 consumers to .glass-panel. D3. Mobile drawer uses subtle tier (.glass-panel-strong). D4. CI gate scopes to JSX inline-style usage only; 3-entry allowlist for documented chrome exceptions. Briefs (1-7, all <100 LOC each, disjoint files): 1. Upgrade primitive (no deps, blocks 3+4) 2. Migrate auth form cards (parallel-safe) 3. Migrate floating popovers (sidebar dropdown, mobile drawer, TopSearchBar UserMenu) 4. Migrate BulkSelectionToolbar + interior token sweep 5. Retire .card class; migrate 8 consumers 6. Migrate landing nav bar to .page-header-glass 7. forbidden-bespoke-glass-surface CI gate (runs LAST) Boot-the-brief check: all 4-layer recipes verified against post-PR-#118 styles/globals.css; all box-shadow preservation chains verified against current Layout.js + TopSearchBar.js; .card deletion verified clean (single rule at L729-733); CI gate shape modeled on existing forbidden-modal-shell-without-primitive job. Architecture is read-only output — no application code touched. Awaiting human gate 1 (plan approval) before implementers run. Co-authored-by: Cursor --- .convoys/unify-glass-panel-surfaces.md | 281 ++++++++++++++++++ ...brief-1-upgrade-glass-surface-primitive.md | 82 +++++ .../brief-2-auth-form-cards.md | 79 +++++ .../brief-3-floating-popovers.md | 154 ++++++++++ .../brief-4-bulk-selection-toolbar.md | 123 ++++++++ .../brief-5-retire-card-class.md | 87 ++++++ .../brief-6-landing-nav-bar.md | 91 ++++++ ...forbidden-bespoke-glass-surface-ci-gate.md | 123 ++++++++ 8 files changed, 1020 insertions(+) create mode 100644 .convoys/unify-glass-panel-surfaces/brief-1-upgrade-glass-surface-primitive.md create mode 100644 .convoys/unify-glass-panel-surfaces/brief-2-auth-form-cards.md create mode 100644 .convoys/unify-glass-panel-surfaces/brief-3-floating-popovers.md create mode 100644 .convoys/unify-glass-panel-surfaces/brief-4-bulk-selection-toolbar.md create mode 100644 .convoys/unify-glass-panel-surfaces/brief-5-retire-card-class.md create mode 100644 .convoys/unify-glass-panel-surfaces/brief-6-landing-nav-bar.md create mode 100644 .convoys/unify-glass-panel-surfaces/brief-7-forbidden-bespoke-glass-surface-ci-gate.md 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 `