124 lines
7.7 KiB
Markdown
124 lines
7.7 KiB
Markdown
|
|
---
|
||
|
|
convoy: unify-glass-panel-surfaces
|
||
|
|
brief_number: 4
|
||
|
|
depends_on: [1]
|
||
|
|
files:
|
||
|
|
- components/BulkSelectionToolbar.js
|
||
|
|
---
|
||
|
|
|
||
|
|
# Brief 4: Migrate `BulkSelectionToolbar` to `.glass-panel-strong` + token sweep
|
||
|
|
|
||
|
|
## Goal (1 sentence)
|
||
|
|
|
||
|
|
Replace the bulk-select toolbar's `bg-white border-gray-200` Tailwind shape with `.glass-panel-strong rounded-2xl` so the floating toolbar is legible in dark mode, and sweep its interior hardcoded gray/red Tailwind classes (`text-gray-700`, `hover:bg-gray-100`, `text-red-600 hover:bg-red-50`) to token-driven inline styles + `nav-item-hover` for consistency with the rest of the post-PR-#117 surfaces.
|
||
|
|
|
||
|
|
## Files in scope (do not edit anything else)
|
||
|
|
|
||
|
|
- `components/BulkSelectionToolbar.js` — the entire file. Specifically:
|
||
|
|
- L47 outer toolbar `<div>` — swap `bg-white rounded-xl shadow-2xl border border-gray-200` to `glass-panel-strong rounded-2xl shadow-2xl`. (Keep `shadow-2xl` for now — the toolbar floats over arbitrary content and the extra weight is intentional. `.glass-panel-strong`'s `var(--rim-light-inner), var(--elevation-ambient)` chain doesn't include it; chain it via inline override.)
|
||
|
|
- L60 divider — swap `bg-gray-300` to a CSS-var-driven divider color (`backgroundColor: 'var(--border)'` inline, since `bg-gray-300` doesn't theme).
|
||
|
|
- L53 selection-count label — swap `text-gray-700` to `style={{ color: 'var(--text-primary)' }}`.
|
||
|
|
- L108 more-actions trigger — swap `text-gray-600 hover:text-gray-800 hover:bg-gray-100` to use `var(--text-secondary)` + `nav-item-hover`, with `rounded-lg` upgraded to `rounded-xl`.
|
||
|
|
- L117 more-actions dropdown — swap `bg-white rounded-lg shadow-xl border border-gray-200` to `glass-panel-strong rounded-xl`. (Drop `shadow-xl`; the class provides shadow. Keep `border` semantics out — the class's gradient border is the new look.)
|
||
|
|
- L118-141 menu items — swap `text-gray-700 hover:bg-gray-100` to `var(--text-primary)` + `nav-item-hover` className.
|
||
|
|
- L144 menu divider — swap `border-gray-200` to `borderColor: 'var(--border)'`.
|
||
|
|
- L146-157 destructive menu item — swap `text-red-600 hover:bg-red-50` to `color: 'var(--accent-danger)'` (verify the token exists in `globals.css`; if not, fall back to `rgb(239, 68, 68)` and document) + `nav-item-hover` className.
|
||
|
|
- L160-end "Clear Selection" button — sweep any remaining `text-gray-*` / `bg-gray-*` hardcodes the same way.
|
||
|
|
|
||
|
|
**Out of scope:** the parent components that consume `BulkSelectionToolbar` (e.g. `pages/my-cards.js`, `components/CollectionPageView.js`); the props contract (`selectedCount`, `selectedCards`, all the `onBulk*` callbacks); any of the SVG icon paths.
|
||
|
|
|
||
|
|
## Conventions to follow
|
||
|
|
|
||
|
|
- **Outer toolbar:**
|
||
|
|
```jsx
|
||
|
|
// Before:
|
||
|
|
<div className="bg-white rounded-xl shadow-2xl border border-gray-200 px-3 sm:px-6 py-3 sm:py-4 flex items-center justify-between sm:justify-start sm:space-x-4 sm:min-w-96">
|
||
|
|
|
||
|
|
// After:
|
||
|
|
<div
|
||
|
|
className="glass-panel-strong rounded-2xl px-3 sm:px-6 py-3 sm:py-4 flex items-center justify-between sm:justify-start sm:space-x-4 sm:min-w-96"
|
||
|
|
style={{
|
||
|
|
// Chain shadow-2xl-equivalent depth onto the class's existing
|
||
|
|
// rim+ambient stack so the floating toolbar still reads as
|
||
|
|
// elevated over arbitrary page content.
|
||
|
|
boxShadow:
|
||
|
|
'var(--rim-light-inner), var(--elevation-pronounced)',
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
```
|
||
|
|
Rationale: `shadow-2xl` is a hardcoded RGB; `var(--elevation-pronounced)` is the system equivalent and respects the theme.
|
||
|
|
|
||
|
|
- **More-actions dropdown:**
|
||
|
|
```jsx
|
||
|
|
// Before:
|
||
|
|
<div className="absolute bottom-full right-0 mb-2 bg-white rounded-lg shadow-xl border border-gray-200 py-2 min-w-48">
|
||
|
|
|
||
|
|
// After:
|
||
|
|
<div className="glass-panel-strong absolute bottom-full right-0 mb-2 rounded-xl py-2 min-w-48">
|
||
|
|
```
|
||
|
|
No inline-style override needed — the class's default
|
||
|
|
`var(--rim-light-inner), var(--elevation-ambient)` is the right
|
||
|
|
weight for an inner dropdown.
|
||
|
|
|
||
|
|
- **Menu items** (the `<button>` rows inside the dropdown):
|
||
|
|
```jsx
|
||
|
|
// Before (non-destructive item):
|
||
|
|
<button className="w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100 flex items-center space-x-2">
|
||
|
|
|
||
|
|
// After:
|
||
|
|
<button
|
||
|
|
className="nav-item-hover w-full px-4 py-2 text-left text-sm flex items-center space-x-2"
|
||
|
|
style={{ color: 'var(--text-primary)' }}
|
||
|
|
>
|
||
|
|
|
||
|
|
// Before (destructive item):
|
||
|
|
<button className="w-full px-4 py-2 text-left text-sm text-red-600 hover:bg-red-50 flex items-center space-x-2">
|
||
|
|
|
||
|
|
// After:
|
||
|
|
<button
|
||
|
|
className="nav-item-hover w-full px-4 py-2 text-left text-sm flex items-center space-x-2"
|
||
|
|
style={{ color: 'var(--accent-danger)' }}
|
||
|
|
>
|
||
|
|
```
|
||
|
|
|
||
|
|
- **Verify `--accent-danger` exists** before using it. Quick check:
|
||
|
|
search `styles/globals.css` for `--accent-danger`. If absent, the
|
||
|
|
PR #117 sweep likely defines it; if it's still absent post-#117,
|
||
|
|
use `color: 'rgb(239, 68, 68)'` (the literal Tailwind `red-500`
|
||
|
|
RGB) and add a note in the PR description requesting follow-up to
|
||
|
|
introduce the token in a separate PR. Do NOT add the token in this
|
||
|
|
brief — token additions belong in a design-system PR.
|
||
|
|
|
||
|
|
- **Verify `nav-item-hover` exists** — it's defined in
|
||
|
|
`styles/globals.css` and used widely post-PR #117. If for some
|
||
|
|
reason this brief dispatches before PR #117 lands, escalate to the
|
||
|
|
conductor — that's a dependency violation (Brief 4 depends_on: [1]
|
||
|
|
but transitively depends on the post-PR-#117 token tier).
|
||
|
|
|
||
|
|
- **Verbatim children:** SVG icon paths, button text labels, click
|
||
|
|
handler bindings, the `VOCAB.MY_COLLECTION` / `VOCAB.REMOVE_FROM_MY_COLLECTION`
|
||
|
|
imports, the `setShowActions(false)` flow — all stay byte-identical.
|
||
|
|
|
||
|
|
- **`px-2 sm:px-3 py-2` action buttons** (the 3 colored quick-action
|
||
|
|
buttons: Collection / Deck / My Collection): leave them alone. They
|
||
|
|
use `--accent-flame`, `--accent-gold`, `--accent-wood` already and
|
||
|
|
the `onMouseEnter`/`onMouseLeave` swap is a known pattern. Touching
|
||
|
|
them is scope expansion.
|
||
|
|
|
||
|
|
## Acceptance criteria
|
||
|
|
|
||
|
|
- [ ] Outer toolbar `<div>` uses `glass-panel-strong rounded-2xl` and an inline `boxShadow` matching the verbatim shape above.
|
||
|
|
- [ ] More-actions dropdown `<div>` uses `glass-panel-strong rounded-xl` and no inline `background` / `border` / `shadow` style.
|
||
|
|
- [ ] All `text-gray-{600,700,800}` classes within the toolbar are replaced with `style={{ color: 'var(--text-{primary,secondary}) ' }}` or `nav-item-hover` className.
|
||
|
|
- [ ] All `hover:bg-gray-{50,100}` classes are replaced with `nav-item-hover` className.
|
||
|
|
- [ ] The destructive "Delete Selected" item uses `var(--accent-danger)` (or the documented `rgb(239,68,68)` fallback) — NOT `text-red-600`.
|
||
|
|
- [ ] The L60 divider uses `style={{ backgroundColor: 'var(--border)' }}` — NOT `bg-gray-300`.
|
||
|
|
- [ ] The 3 quick-action buttons (Collection / Deck / My Collection at L64-101) are UNCHANGED. The "Clear Selection" button at the end may need a small text-color swap; that's in scope. Everything else is left alone.
|
||
|
|
- [ ] **Dark-mode verification**: open the bulk-select toolbar (select 2+ cards on `/my-cards` or `/collection/[id]`), confirm it renders legibly with corner catch-lights and proper text contrast in BOTH light and dark themes. Attach before/after dark-mode screenshots to the PR description.
|
||
|
|
- [ ] `npm run lint` + `npm run test:run` (23+/23+, post Brief 1) pass.
|
||
|
|
- [ ] No edits to files outside `components/BulkSelectionToolbar.js`.
|
||
|
|
|
||
|
|
## Rationale (≤3 sentences)
|
||
|
|
|
||
|
|
The toolbar is currently invisible in dark mode because `bg-white` doesn't theme — this PR is a correctness fix as much as a design unification. Sweeping the interior hardcoded grays + reds in the same PR is cheap (the file is small, ~200 lines) and prevents a follow-up convoy from having to revisit the file. The 3 colored quick-action buttons stay as-is because they use the right tokens already and a token sweep there is scope expansion.
|