--- convoy: unify-glass-panel-surfaces brief_number: 3 depends_on: [1] files: - components/Layout.js - components/ui/TopSearchBar.js - test/components/Layout.test.js --- # Brief 3: Migrate floating popovers to `.glass-panel-strong` ## Goal (1 sentence) Migrate three floating surfaces — the sidebar profile dropdown, the mobile drawer, and the TopSearchBar `` dropdown — from inline `var(--glass-surface-*)` + `backdropFilter` styles to the canonical `.glass-panel-strong` className, while preserving their existing box-shadow chains (ember rim for the profile dropdown; pronounced elevation for the drawer) via inline override. ## Files in scope (do not edit anything else) - `components/Layout.js` - Sidebar profile dropdown panel (currently around L88-96 inside ``). - Mobile drawer slide-in panel (currently around L698-709 — the `
` that takes `${isMobileMenuOpen ? 'translate-x-0' : '-translate-x-full'}` and the `style={{ background: 'var(--glass-surface-mid)', backdropFilter: '...', ... }}` block). - **Do NOT touch** the sidebar nav-chip block (around L858-861); that block intentionally uses full-intensity chrome corner lights and stays handrolled — Decision 4 of this convoy's Architecture allowlists it explicitly. - `components/ui/TopSearchBar.js` - `` dropdown panel (around L214-222 — the absolutely-positioned `
` with the `background: 'var(--glass-surface-high)'` + `backdropFilter` block). - **Do NOT touch** the TopSearchBar `
` block itself; that block also intentionally uses full-intensity chrome corner lights and is allowlisted. - `test/components/Layout.test.js` - Add regression-lock assertions that the sidebar profile dropdown and mobile drawer render with `.glass-panel-strong` in their className. **Out of scope:** the rest of `Layout.js` (``, ``, `` trigger button, ``, etc.), the rest of `TopSearchBar.js` (search input, `` link, etc.), and any deeper menu-item styling (e.g. `hover:bg-gray-50 dark:hover:bg-gray-700` cleanup — that's owned by PR #117 / `design-sweep-pass`). ## Conventions to follow - **Verbatim replacement** for each floating-surface `
`: ### A. Sidebar profile dropdown panel (Layout.js) ```jsx // Before:
// After:
``` The `background` + `backdropFilter` + `WebkitBackdropFilter` inline styles are removed (the class provides them). The `boxShadow` is **preserved as an inline override** because `.glass-panel-strong` ships only `var(--rim-light-inner), var(--elevation-ambient)` — we need the `var(--ember-rim-subtle)` middle entry to keep the ember emphasis the dropdown is known for. ### B. Mobile drawer panel (Layout.js) ```jsx // Before:
// After:
``` Same pattern. `boxShadow` is preserved as an inline override so the drawer keeps its `var(--elevation-pronounced)` (heavier) elevation + `var(--rim-light-outer)` (which `.glass-panel-strong` doesn't ship by default). ### C. TopSearchBar `` dropdown panel ```jsx // Before — the absolutely-positioned
nested inside UserMenu, // around L214-222 of TopSearchBar.js:
// After:
``` **NOTE:** confirm the verbatim shape of the current TopSearchBar UserMenu dropdown against L214-222 before editing — the inline className/style block might already have minor differences from the "before" sample above (e.g. different `w-` width, different `mt-` margin). Migrate the actual block, not this sample; the sample only shows the shape of the swap. - **Preserve all children verbatim.** Menu items, dividers, icons, click handlers — all stay byte-identical. - **Do not touch the existing `hover:bg-gray-50 dark:hover:bg-gray-700` classes on individual menu items.** PR #117 (`design-sweep-pass`) is already migrating those to `nav-item-hover`; this brief MUST NOT duplicate that work or it'll merge-conflict. If PR #117 has already merged when this brief dispatches, the relevant lines may already read `nav-item-hover` — that's fine; just leave them alone. - **Test additions in `test/components/Layout.test.js`:** - One assertion for the sidebar profile dropdown: render ``, simulate clicking the user-profile trigger to open the dropdown, assert the dropdown panel `className` contains `'glass-panel-strong'`. - One assertion for the mobile drawer: render ``, simulate opening the drawer (today the test file already has a way to set `isMobileMenuOpen`; if not, render with the relevant prop / state directly), assert the drawer panel `className` contains `'glass-panel-strong'`. - Use the existing test file's patterns (jsdom env, `cleanup()` afterEach, `render` + `screen` from `@testing-library/react`). Do NOT add new test dependencies. Do NOT lower any existing assertion in the file. ## Acceptance criteria - [ ] Sidebar profile dropdown panel uses `className="glass-panel-strong ..."` and carries an inline `boxShadow` with `var(--rim-light-inner), var(--ember-rim-subtle), var(--elevation-ambient)`. - [ ] Mobile drawer panel uses `className="glass-panel-strong ..."` and carries an inline `boxShadow` with `var(--rim-light-inner), var(--rim-light-outer), var(--elevation-pronounced)`. - [ ] TopSearchBar `` dropdown panel uses `className="glass-panel-strong ..."` and carries an inline `boxShadow` with `var(--rim-light-inner), var(--ember-rim-subtle), var(--elevation-ambient)`. - [ ] No inline `background: 'var(--glass-surface-*)'` or `backdropFilter` style remains on any of the three migrated `
`s. - [ ] Sidebar nav-chip block (Layout.js ~L858-861) UNCHANGED. TopSearchBar `
` block UNCHANGED. (Architect's allowlist for chrome-tier surfaces.) - [ ] `test/components/Layout.test.js` has at least 2 new assertions (sidebar dropdown + mobile drawer carry `glass-panel-strong`). - [ ] `npm run lint` + `npm run test:run` (23+/23+, post Brief 1) pass. - [ ] Manual: open sidebar profile dropdown, open mobile drawer, open TopSearchBar UserMenu dropdown in light + dark mode — all three render with visible corner catch-lights and the appropriate shadow elevation. - [ ] No edits to files outside the 3 listed in `files:` above. ## Rationale (≤3 sentences) These three popovers are the most visible floating surfaces in the authenticated app and they predate the gradient-border system; migrating them is the largest visual-correctness win of the convoy after Brief 1. Preserving the existing box-shadow chains via inline override (rather than letting them snap to the class default) is non-negotiable — the `var(--ember-rim-subtle)` ember emphasis on the profile menu and the `var(--elevation-pronounced)` weight on the drawer are deliberate design choices that must survive the class swap.