Files
GearBox/.planning/milestones/v2.2-phases/31-mobile-polish/31-01-PLAN.md
Jean-Luc Makiola 2853477a75
All checks were successful
CI / ci (push) Successful in 1m15s
CI / e2e (push) Has been skipped
CI / deploy (push) Has been skipped
chore: archive v2.2 User Experience Polish milestone
Phases 28-31 archived to milestones/v2.2-phases/
Requirements and roadmap snapshots archived to milestones/

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:00:35 +02:00

11 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
31-mobile-polish 01 execute 1
src/client/routes/items/$itemId.tsx
src/client/routes/threads/$threadId/candidates/$candidateId.tsx
true
D-01
D-02
D-03
D-04
truths artifacts key_links
Item detail shows icon-only action buttons below md breakpoint
Item detail shows text action buttons at md and above
Candidate detail shows icon-only action buttons below md breakpoint
Candidate detail shows text action buttons at md and above
All icon-only buttons have aria-label attributes
All icon-only buttons have minimum 44px touch targets
src/client/routes/items/$itemId.tsx (modified)
src/client/routes/threads/$threadId/candidates/$candidateId.tsx (modified)
LucideIcon component used for all icons (not inline SVGs)
md
breakpoint matches BottomTabBar responsive pattern
Add responsive icon-based action buttons to item detail and candidate detail pages.

Purpose: Replace text-label action buttons with icon-only buttons on mobile viewports (below md: breakpoint) for better mobile UX. Desktop retains full text buttons. Output: Modified item detail and candidate detail pages with responsive action buttons.

<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/31-mobile-polish/31-CONTEXT.md @.planning/phases/31-mobile-polish/31-UI-SPEC.md

@src/client/components/BottomTabBar.tsx @src/client/lib/iconData.tsx

From src/client/lib/iconData.tsx:

export function LucideIcon({ name, size, className, strokeWidth }: {
  name: string;
  size?: number;
  className?: string;
  strokeWidth?: number;
}): React.ReactElement;

From src/client/components/BottomTabBar.tsx:

// Responsive breakpoint reference: md:hidden (mobile), hidden md:flex (desktop)
Task 1: Add responsive icon buttons to item detail page src/client/routes/items/$itemId.tsx

<read_first>

  • src/client/routes/items/$itemId.tsx (current action button implementation, lines ~189-213)
  • src/client/components/BottomTabBar.tsx (responsive breakpoint pattern reference)
  • src/client/lib/iconData.tsx (LucideIcon component API)
  • .planning/phases/31-mobile-polish/31-UI-SPEC.md (icon mapping and color contract) </read_first>
In src/client/routes/items/$itemId.tsx, modify the action button group (the `div` with `flex items-center gap-2` containing Duplicate, Delete, and Edit buttons, visible when `!isEditing`).

For each button, create a paired desktop/mobile pattern:

Duplicate button:

  • Desktop (hidden on mobile): <button className="hidden md:inline-flex items-center gap-1.5 px-3 py-1.5 text-sm text-gray-500 hover:text-gray-700 hover:bg-gray-50 rounded-lg transition-colors" ...>Duplicate</button>
  • Mobile (hidden on desktop): <button className="md:hidden inline-flex items-center justify-center min-w-[44px] min-h-[44px] p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-50 rounded-lg transition-colors" aria-label="Duplicate" title="Duplicate" ...><LucideIcon name="copy" size={16} /></button>

Delete/Remove button:

  • Desktop: <button className="hidden md:inline-flex items-center gap-1.5 px-3 py-1.5 text-sm text-red-400 hover:text-red-600 hover:bg-red-50 rounded-lg transition-colors" ...>{isReference ? "Remove from Collection" : "Delete"}</button>
  • Mobile: <button className="md:hidden inline-flex items-center justify-center min-w-[44px] min-h-[44px] p-2 text-red-400 hover:text-red-600 hover:bg-red-50 rounded-lg transition-colors" aria-label={isReference ? "Remove from Collection" : "Delete"} title={isReference ? "Remove from Collection" : "Delete"} ...><LucideIcon name="trash-2" size={16} /></button>

Edit button:

  • Desktop: <button className="hidden md:inline-flex items-center gap-1.5 px-4 py-1.5 text-sm font-medium text-white bg-gray-700 hover:bg-gray-800 rounded-lg transition-colors" ...>Edit</button>
  • Mobile: <button className="md:hidden inline-flex items-center justify-center min-w-[44px] min-h-[44px] p-2 text-white bg-gray-700 hover:bg-gray-800 rounded-lg transition-colors" aria-label="Edit" title="Edit" ...><LucideIcon name="pencil" size={16} /></button>

Ensure LucideIcon is already imported (it is — check line ~8). Keep all existing onClick handlers and disabled states. The Cancel/Save buttons in edit mode remain unchanged (text buttons on all viewports).

Per D-01: Apply icon-based action buttons on mobile to item detail page. Per D-02: Desktop keeps text buttons, mobile switches to icons at md: breakpoint. Per D-03: Standard icon mapping — pencil for Edit, trash-2 for Delete, copy for Duplicate.

<acceptance_criteria>

  • $itemId.tsx contains aria-label="Duplicate" on an icon button
  • $itemId.tsx contains aria-label="Edit" on an icon button with md:hidden class
  • $itemId.tsx contains <LucideIcon name="copy" for Duplicate icon
  • $itemId.tsx contains <LucideIcon name="trash-2" for Delete icon
  • $itemId.tsx contains <LucideIcon name="pencil" for Edit icon
  • $itemId.tsx contains min-w-[44px] for touch target sizing
  • $itemId.tsx contains hidden md:inline-flex on desktop text buttons
  • Cancel and Save buttons in edit mode do NOT have md:hidden responsive splitting </acceptance_criteria>
grep -c "aria-label" src/client/routes/items/\$itemId.tsx | grep -q "[3-9]" && grep -c "md:hidden" src/client/routes/items/\$itemId.tsx | grep -q "[3-9]" && echo "PASS" || echo "FAIL"

Item detail page shows icon-only Duplicate/Delete/Edit buttons on mobile, full text buttons on desktop. All icon buttons have aria-label and 44px minimum touch targets.

Task 2: Add responsive icon buttons to candidate detail page src/client/routes/threads/$threadId/candidates/$candidateId.tsx

<read_first>

  • src/client/routes/threads/$threadId/candidates/$candidateId.tsx (current action buttons — header Edit at line ~282, bottom actions at lines ~530-548)
  • .planning/phases/31-mobile-polish/31-UI-SPEC.md (icon mapping and color contract) </read_first>
In src/client/routes/threads/$threadId/candidates/$candidateId.tsx, modify action buttons in two locations:

Location 1: Header Edit button (line ~282-289) Currently shows <LucideIcon name="pencil" size={14} /> + "Edit" text. Split into:

  • Desktop: <button className="shrink-0 hidden md:inline-flex items-center gap-1.5 px-3 py-1.5 text-sm text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded-lg transition-colors" ...><LucideIcon name="pencil" size={14} />Edit</button>
  • Mobile: <button className="shrink-0 md:hidden inline-flex items-center justify-center min-w-[44px] min-h-[44px] p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded-lg transition-colors" aria-label="Edit" title="Edit" ...><LucideIcon name="pencil" size={16} /></button>

Location 2: Bottom action buttons (lines ~530-548) Currently shows "Pick as winner" with trophy icon and "Delete" with trash-2 icon. Split each:

Pick as Winner:

  • Desktop: <button className="hidden md:inline-flex items-center gap-1.5 px-4 py-2 bg-amber-50 hover:bg-amber-100 text-amber-700 text-sm font-medium rounded-lg transition-colors" ...><LucideIcon name="trophy" size={14} />Pick as winner</button>
  • Mobile: <button className="md:hidden inline-flex items-center justify-center min-w-[44px] min-h-[44px] p-2 bg-amber-50 hover:bg-amber-100 text-amber-700 rounded-lg transition-colors" aria-label="Pick as winner" title="Pick as winner" ...><LucideIcon name="trophy" size={16} /></button>

Delete:

  • Desktop: <button className="hidden md:inline-flex items-center gap-1.5 px-4 py-2 text-sm text-red-500 hover:text-red-600 hover:bg-red-50 rounded-lg transition-colors" ...><LucideIcon name="trash-2" size={14} />Delete</button>
  • Mobile: <button className="md:hidden inline-flex items-center justify-center min-w-[44px] min-h-[44px] p-2 text-red-500 hover:text-red-600 hover:bg-red-50 rounded-lg transition-colors" aria-label="Delete" title="Delete" ...><LucideIcon name="trash-2" size={16} /></button>

Keep all existing onClick handlers. The edit mode buttons (Cancel, Save) remain unchanged.

Per D-01: Apply to candidate detail page. Per D-02: Desktop text, mobile icons at md: breakpoint. Per D-03: Standard icon mapping — pencil for Edit, trash-2 for Delete, trophy for Pick as winner.

<acceptance_criteria>

  • $candidateId.tsx contains aria-label="Edit" on an icon button with md:hidden
  • $candidateId.tsx contains aria-label="Pick as winner" on an icon button
  • $candidateId.tsx contains aria-label="Delete" on an icon button
  • $candidateId.tsx contains min-w-[44px] for touch target sizing (at least 3 occurrences)
  • $candidateId.tsx contains hidden md:inline-flex on desktop text buttons (at least 3 occurrences)
  • Edit mode Cancel/Save buttons do NOT have responsive splitting </acceptance_criteria>
grep -c "aria-label" src/client/routes/threads/\$threadId/candidates/\$candidateId.tsx | grep -q "[3-9]" && grep -c "md:hidden" src/client/routes/threads/\$threadId/candidates/\$candidateId.tsx | grep -q "[3-9]" && echo "PASS" || echo "FAIL"

Candidate detail page shows icon-only Edit/Pick as winner/Delete buttons on mobile, full text+icon buttons on desktop. All icon buttons have aria-label and 44px minimum touch targets.

<threat_model>

Trust Boundaries

No new trust boundaries introduced. This plan only modifies client-side rendering of existing buttons. No new API calls, no new data flows, no new authentication paths.

STRIDE Threat Register

Threat ID Category Component Disposition Mitigation Plan
T-31-01 Information Disclosure Icon buttons accept Icon buttons show same actions as existing text buttons — no new information exposed. aria-label text matches existing button text.
</threat_model>
- `bun run lint` passes with no errors in modified files - `bun test` passes (no test regressions) - Manual: Open item detail at mobile viewport (< 768px) — see icon-only buttons - Manual: Open item detail at desktop viewport (>= 768px) — see text buttons - Manual: Open candidate detail at mobile viewport — see icon-only buttons

<success_criteria>

  • Item detail page renders icon-only Duplicate/Delete/Edit buttons on mobile
  • Candidate detail page renders icon-only Edit/Pick as winner/Delete buttons on mobile
  • Desktop rendering unchanged (text buttons with optional icons)
  • All icon buttons have aria-label for accessibility
  • All icon buttons have min-w-[44px] min-h-[44px] for comfortable touch targets
  • md: breakpoint used consistently (matching BottomTabBar pattern) </success_criteria>
After completion, create `.planning/phases/31-mobile-polish/31-01-SUMMARY.md`