---
name: cleanup-card-item-list-and-share-modal-palette
classification: feature
success_metric: |
  No hardcoded Tailwind palette classes (`bg-purple-*`, `bg-blue-*`,
  `bg-gray-*` 100-900 range, `text-gray-{500,600,700,900}`,
  `bg-white`, `border-gray-200`, etc.) remain in the interior of
  `components/CardItem.js` list-mode or `components/ShareModal.js`.
  Both render correctly in dark theme. Verified by visual diff and
  a targeted lint sweep.
skip:
  - ia
  - ux
  - arch
status: closed
closed: 2026-06-04
prs:
  - 128  # Brief 1 — CardItem.js list-mode token sweep
  - 129  # Brief 2 — ShareModal.js interior token sweep
created: 2026-06-04
depends_on:
  - design-sweep-pass  # PR #117 — established the token migration pattern
---

# Convoy: cleanup-card-item-list-and-share-modal-palette

Targeted palette cleanup. Parallel companion to
`unify-glass-panel-surfaces` — that convoy migrates panel *surfaces*;
this one cleans up the *interior content* (rows, badges, text,
buttons) of two specific files whose interiors were not addressed
by PR #117 because they're not panel surfaces — they're nested
content inside surfaces that were already on the system.

## Why

Two files left over after the 2026-06-04 design sweep:

1. **`components/CardItem.js` list-mode** (L183–284) — the
   list-view layout (when `viewMode === 'list'`) is entirely
   hardcoded Tailwind palette. The grid-mode (L290+) was already
   migrated; list-mode wasn't. `border-gray-200`, `bg-purple-50`,
   `text-gray-900`, `text-gray-600`, `text-green-600`,
   `bg-blue-100 text-blue-800` — none of these have dark-mode
   variants. In dark theme, the list view renders unreadable
   (light text on light backgrounds) or off-brand (purple/blue
   badges in an ember palette app).

2. **`components/ShareModal.js`** interior — the modal SURFACE is
   correct (delegates to `<Modal>` → `<GlassSurface>`), and the
   user-search dropdown + email-invite card were updated in PR
   #117. But the interior rows still have:
   - `bg-purple-600` avatar circles for current user (L194, L240).
   - `bg-gray-50` current-user permission row (L238) — invisible
     in dark mode.
   - `bg-gray-100 rounded` permission badge (L272) — light-only.
   - `bg-gray-50 border-gray-300 text-gray-600` share-link input
     (L287) — invisible in dark mode.
   - `bg-blue-600 hover:bg-blue-700` Copy-link button (L294) —
     off-palette (should be ember).
   - `text-gray-{500,700,900}` text colors throughout — light-only.

   Note ShareModal is the primary collaboration entry point; the
   permission rows and Copy-link button are visited every time a
   user invites a collaborator.

Both files are token-only swaps. No structural changes, no
component swaps, no glass-panel migration.

## Scope

### In scope

#### Brief 1 — `CardItem.js` list-mode token sweep

Replace every hardcoded color in lines ~183–284 with the
appropriate `var(--*)` token via inline style. Categories:

- Background fills: `bg-purple-50` (selected row), `bg-white` →
  `var(--bg-secondary)` or `var(--bg-tertiary)` per role; selected
  state uses `var(--accent-ember)` + low alpha as background.
- Borders: `border-gray-200` / `border-gray-300` →
  `var(--border)`. Selected state border `border-purple-600` →
  `var(--accent-ember)`.
- Text: `text-gray-900` → `var(--text-primary)`,
  `text-gray-600` / `text-gray-500` → `var(--text-secondary)`,
  `text-green-600` (positive value indicators) →
  `var(--accent-flame)` or keep `#16a34a` if semantic green
  matters more than brand.
- Badges (`bg-blue-100 text-blue-800` etc.): convert to
  `var(--bg-tertiary)` + `var(--text-primary)` or
  `var(--accent-ember)` per badge role (game, rarity, type).
- Buttons / icons: `text-purple-600` (selection indicator) →
  `var(--accent-ember)`.

Acceptance: visually inspect `CardItem` list-mode in BOTH themes.
All text must be legible, all interactive states (hover, selected)
must have a visible delta against the row background.

**Files:** `components/CardItem.js`.
**Risk:** LOW — purely cosmetic token swaps; no layout changes.

#### Brief 2 — `ShareModal.js` interior token sweep

Lines roughly 230–310. Targets:

- **Avatar circles** (L194, L240): `bg-purple-600` →
  `linear-gradient(135deg, var(--accent-ember), var(--accent-flame))`
  (matches the UserMenu avatar gradient style from
  `components/ui/TopSearchBar.js`).
- **Current-user row container** (L238): `bg-gray-50 rounded-lg`
  → `var(--bg-secondary)` + `rounded-xl`, or
  `var(--bg-tertiary)` for slightly more contrast.
- **Permission badge** (L272): `bg-gray-100 rounded` →
  `var(--bg-tertiary)` + `rounded-xl` + `var(--text-secondary)`.
- **Share-link readonly input** (L283–288): `bg-gray-50
  border-gray-300 text-gray-600` → tokenize against
  `var(--bg-secondary)`, `var(--border)`, `var(--text-primary)`;
  alternatively use the `.input-field` class (already in
  `globals.css`).
- **Copy-link button** (L290–298): `bg-blue-600 text-white
  hover:bg-blue-700` → replace with the `<Button variant="primary">`
  primitive. The success state currently is
  `bg-green-100 text-green-800 border border-green-200`; keep the
  semantic green but use a non-Tailwind path:
  `style={{ backgroundColor: 'var(--bg-secondary)', color:
  'var(--accent-flame)' }}` + an ember border, or commit to a
  custom `.btn-success-flash` utility.
- **Text labels** (L201, L222, L231, L246, L266, L267): all
  `text-gray-{500,700,900}` → tokens.

Acceptance: ShareModal opens in dark theme and every row is
legible; Copy-link button is on the ember palette; permission
badges read as ember-system, not Tailwind-default-gray.

**Files:** `components/ShareModal.js`.
**Risk:** LOW.

### Out of scope

- Any change to the modal *surface* (handled by
  `unify-glass-panel-surfaces` Brief 1, which upgrades
  `<GlassSurface>` and therefore `<Modal>`).
- Any structural change (new component, new layout).
- The user-search-dropdown + email-invite-card areas of
  `ShareModal.js` — those were already cleaned up in PR #117.

## Roles invoked

1. `role-implementer` — Briefs 1 and 2 are parallel-safe; can run
   independently.
2. `role-design-system-auditor` — single sign-off after both
   briefs land (token-only sweep; design audit is a sanity check).
3. `role-reviewer` — one review per brief.

(No architect needed — token swaps with no design decisions.
No IA, no UX — content/structure unchanged. The Design-system
auditor catches any palette regressions.)

## Todos

- [ ] Brief 1 — `CardItem.js` list-mode token sweep
- [ ] Brief 2 — `ShareModal.js` interior token sweep
- [ ] Design-system auditor sign-off
- [ ] Post-PR review per brief

## Acceptance criteria

1. `rg -e "bg-(purple|blue|gray|red)-[0-9]" -e
   "text-(purple|blue|gray|red)-[0-9]" -e "border-gray-[0-9]"`
   over `components/CardItem.js` and `components/ShareModal.js`
   returns no matches (excluding lines that are deliberately
   semantic — e.g. `bg-red-500` on a destructive Delete button,
   which architect ratifies per-line during review).
2. Both files render correctly in both themes — visible text,
   visible interactive states.
3. Build + lint + 113/113 vitest pass.

## CI impact

| Workflow / job | Behavior |
| --- | --- |
| `preview-smoke.yml` | Fires. |
| `visual-diff.yml` | **Fires** — expected diff in CardItem list-view and ShareModal rows. Baseline refresh required. |
| `lint` | Fires. |
| `test:` (vitest) | Fires. |

## Known constraints

- **`CardItem` is in many places** — `pages/cards.js` (grid
  default), `pages/my-cards.js`, `pages/collection/[id].js`, etc.
  Brief 1 must NOT touch the grid-mode (L290+), only the
  list-mode branch.

## Multitask dispatch

```yaml
slice_dependencies:
  - brief: 1
    depends_on: []
    files:
      - components/CardItem.js
  - brief: 2
    depends_on: []
    files:
      - components/ShareModal.js
```

Briefs 1 and 2 are fully parallel — disjoint files, no shared
dependencies.
