# Phase 31: Mobile Polish — Research

**Researched:** 2026-04-12
**Status:** Complete
**Focus:** Icon-based action buttons on mobile detail pages

## Standard Stack

- **Component library:** None (plain Tailwind CSS v4)
- **Icon library:** lucide-react via `LucideIcon` component (`src/client/lib/iconData.tsx`)
- **Styling:** Tailwind CSS v4 with `@import "tailwindcss"` (no custom tokens, no config file)
- **Responsive pattern:** `md:` breakpoint (768px) — matches BottomTabBar (`md:hidden`) and TopNav (`hidden md:flex`)

## Action Button Inventory

### 1. Item Detail (`src/client/routes/items/$itemId.tsx`)

**Location:** Top bar, right side (lines ~190-213)
**Current pattern:** Text-only buttons in a `flex items-center gap-2` container
**Edit mode:** Visible when `!isEditing`

| Button | Text | Current Classes | Icon Candidate |
|--------|------|----------------|----------------|
| Duplicate | "Duplicate" | `px-3 py-1.5 text-sm text-gray-500 hover:text-gray-700 hover:bg-gray-50 rounded-lg` | `copy` (16px) |
| Delete/Remove | "Delete" or "Remove from Collection" | `px-3 py-1.5 text-sm text-red-400 hover:text-red-600 hover:bg-red-50 rounded-lg` | `trash-2` (16px) |
| Edit | "Edit" | `px-4 py-1.5 text-sm font-medium text-white bg-gray-700 hover:bg-gray-800 rounded-lg` | `pencil` (16px) |

**Edit mode buttons (Cancel/Save):** These should remain text buttons even on mobile — users need clear text feedback during edit operations.

### 2. Candidate Detail (`src/client/routes/threads/$threadId/candidates/$candidateId.tsx`)

**Location 1:** Header area — Edit button inline with heading (line ~282-289)
**Current pattern:** Small text+icon button (`LucideIcon name="pencil" size={14}` + "Edit" text)

**Location 2:** Bottom actions area (lines ~530-548)
**Current pattern:** Text+icon buttons in `flex gap-3 pt-4 border-t border-gray-100`

| Button | Text | Current Pattern | Icon Candidate |
|--------|------|----------------|----------------|
| Edit (header) | "Edit" | `px-3 py-1.5 text-sm` + pencil icon 14px | Already has icon — hide text on mobile |
| Pick as Winner | "Pick as winner" | `px-4 py-2` + trophy icon 14px | `trophy` (16px) |
| Delete | "Delete" | `px-4 py-2` + trash-2 icon 14px | Already has icon — hide text on mobile |

### 3. Setup Detail (`src/client/routes/setups/$setupId.tsx`)

**Location:** Toolbar area below header (lines ~155-210)
**Current pattern:** Mixed text+icon and text-only buttons in `flex items-center gap-3`

| Button | Text | Current Pattern | Icon Candidate |
|--------|------|----------------|----------------|
| Add Items | "Add Items" | `px-4 py-2` + inline SVG plus icon | `plus` (16px) via LucideIcon |
| Public toggle | "Public"/"Private" | `px-3 py-2` + inline SVG globe | `globe` (16px) via LucideIcon |
| Delete Setup | "Delete Setup" | `px-4 py-2 text-red-600 bg-red-50` | `trash-2` (16px) |

**Note:** Setup page uses inline SVGs instead of LucideIcon — migration to LucideIcon is a natural cleanup.

### 4. Global Item Detail (`src/client/routes/global-items/$globalItemId.tsx`)

**Location:** Action buttons below image (lines ~167-193)
**Current pattern:** Text-only buttons in `flex gap-3 mb-6`

| Button | Text | Current Pattern | Icon Candidate |
|--------|------|----------------|----------------|
| Add to Collection | "Add to Collection" | `px-5 py-2.5 bg-gray-700 text-white` | `plus` (16px) |
| Add to Thread | "Add to Thread" | `px-5 py-2.5 bg-white border` | `message-square-plus` (16px) |

## Architecture Patterns

### Recommended Implementation Pattern

Use paired hidden/visible elements with responsive Tailwind classes:

```tsx
{/* Desktop: text + optional icon */}
<button className="hidden md:inline-flex items-center gap-1.5 px-3 py-1.5 text-sm ...">
  <LucideIcon name="pencil" size={14} />
  Edit
</button>

{/* Mobile: icon-only with touch target */}
<button
  className="md:hidden inline-flex items-center justify-center min-w-[44px] min-h-[44px] p-2 rounded-lg ..."
  aria-label="Edit"
  title="Edit"
>
  <LucideIcon name="pencil" size={16} />
</button>
```

### Alternative: Single Element with Responsive Text Hiding

```tsx
<button className="inline-flex items-center gap-1.5 px-2 md:px-3 py-1.5 min-w-[44px] min-h-[44px] md:min-w-0 md:min-h-0 justify-center md:justify-start ..." aria-label="Edit">
  <LucideIcon name="pencil" size={16} className="md:w-3.5 md:h-3.5" />
  <span className="hidden md:inline text-sm">Edit</span>
</button>
```

**Recommendation:** Use the paired-element approach for cleaner code and independent styling control. The single-element approach has too many responsive overrides.

**Alternative considered and rejected:** A shared `IconActionButton` component. The action buttons across pages have different styling (primary, secondary, destructive), different sizes, and different hover states. A shared component would need too many props and wouldn't simplify the code meaningfully for just 4 pages.

### LucideIcon Migration for Setup Page

The setup detail page uses inline SVGs for the plus icon and globe icon. These should be migrated to `LucideIcon` for consistency:
- Plus SVG → `<LucideIcon name="plus" size={16} />`
- Globe SVG → `<LucideIcon name="globe" size={16} />`

### Touch Target Sizing

- Minimum 44x44px per WCAG 2.5.5 (AAA) / Apple HIG
- Achieved with `min-w-[44px] min-h-[44px]` on mobile icon buttons
- Desktop buttons keep current sizing (no min-width needed)

### Edit Mode Buttons

Cancel and Save buttons during edit mode should **remain text buttons** on both mobile and desktop:
- These are contextual actions that need clear text labels
- Edit mode is a temporary state — users need to see "Cancel" and "Save" text clearly
- No risk of button crowding since they replace the action buttons

## Dependencies

None. This phase is self-contained — only modifies existing button rendering in 4 route files.

## Risks

1. **Low risk:** Button group layout may need adjustment on very small screens (< 375px) if multiple icon buttons overflow. Mitigation: test at 320px width.
2. **Low risk:** Missing `aria-label` would make icon buttons inaccessible. Mitigation: acceptance criteria require aria-label on every icon button.

## Validation Architecture

### Validation Strategy

| Dimension | What to Validate | How |
|-----------|-----------------|-----|
| Visual | Icon buttons render on mobile, text on desktop | E2E viewport test or manual check |
| Accessibility | All icon buttons have aria-label | Grep for aria-label on new button elements |
| Touch targets | Minimum 44px on mobile | CSS class inspection (min-w-[44px] min-h-[44px]) |
| Consistency | Same breakpoint (md:) across all pages | Grep for breakpoint usage |
| No regression | Desktop buttons unchanged | Visual comparison |

## RESEARCH COMPLETE
