---
phase: 31
slug: mobile-polish
status: draft
shadcn_initialized: false
preset: none
created: 2026-04-12
---

# Phase 31 — UI Design Contract

> Visual and interaction contract for mobile icon-based action buttons. Generated by gsd-ui-researcher, verified by gsd-ui-checker.

---

## Design System

| Property | Value |
|----------|-------|
| Tool | none |
| Preset | not applicable |
| Component library | none (plain Tailwind) |
| Icon library | lucide-react via LucideIcon component |
| Font | System default (Tailwind default stack) |

---

## Spacing Scale

Declared values (must be multiples of 4):

| Token | Value | Usage |
|-------|-------|-------|
| xs | 4px | Icon gaps, inline padding |
| sm | 8px | Compact element spacing, icon button padding |
| md | 16px | Default element spacing |
| lg | 24px | Section padding |
| xl | 32px | Layout gaps |
| 2xl | 48px | Major section breaks |
| 3xl | 64px | Page-level spacing |

Exceptions: Touch targets minimum 44x44px (11 Tailwind units) for icon-only buttons on mobile

---

## Typography

| Role | Size | Weight | Line Height |
|------|------|--------|-------------|
| Body | 14px (text-sm) | 400 | 1.5 |
| Label | 12px (text-xs) | 500 | 1.5 |
| Heading | 24px (text-2xl) | 700 (bold) | 1.2 |
| Display | 20px (text-xl) | 600 (semibold) | 1.2 |

Note: Icon-only buttons have no text labels on mobile. Tooltips (if added) use text-xs (12px).

---

## Color

| Role | Value | Usage |
|------|-------|-------|
| Dominant (60%) | white (#ffffff) | Background, surfaces |
| Secondary (30%) | gray-50 (#f9fafb) / gray-100 (#f3f4f6) | Cards, hover states, icon button hover bg |
| Accent (10%) | gray-700 (#374151) | Primary action icon buttons (Edit) |
| Destructive | red-500 (#ef4444) | Delete/Remove icon buttons only |

Accent reserved for: Edit button (primary action), icon button active/pressed states

### Icon Button Color Mapping

| Action | Icon Color | Hover BG | Notes |
|--------|-----------|----------|-------|
| Edit | gray-700 (white bg variant) | gray-100 | Primary action, most prominent |
| Duplicate | gray-500 | gray-50 | Secondary action |
| Delete/Remove | red-400 | red-50 | Destructive — matches existing pattern |
| Pick as Winner | amber-700 | amber-100 | Matches existing candidate resolve pattern |
| Add to Collection | white (on gray-700 bg) | gray-800 | Primary CTA on catalog detail |
| Add to Thread | gray-700 | gray-50 | Secondary CTA on catalog detail |

---

## Copywriting Contract

| Element | Copy |
|---------|------|
| Primary CTA | n/a (icon-only on mobile, text preserved on desktop) |
| Empty state heading | n/a (no new empty states in this phase) |
| Empty state body | n/a |
| Error state | n/a (no new error states in this phase) |
| Destructive confirmation | Existing ConfirmDialog patterns unchanged |

### Icon-to-Action Mapping (Mobile)

| Action | Lucide Icon Name | Size | aria-label |
|--------|-----------------|------|------------|
| Edit | `pencil` | 16px | "Edit" |
| Delete | `trash-2` | 16px | "Delete" |
| Remove from Collection | `trash-2` | 16px | "Remove from Collection" |
| Duplicate | `copy` | 16px | "Duplicate" |
| Pick as Winner | `trophy` | 14px | "Pick as winner" |
| Add to Collection | `plus` | 16px | "Add to Collection" |
| Add to Thread | `message-square-plus` | 16px | "Add to Thread" |
| Add Items (setup) | `plus` | 16px | "Add Items" |
| Toggle Public | `globe` | 16px | "Toggle public" |
| Delete Setup | `trash-2` | 16px | "Delete Setup" |

### Accessibility

- Every icon-only button MUST have `aria-label` matching the action text shown on desktop
- Icon buttons use `title` attribute matching `aria-label` for hover tooltip on touch-and-hold
- Minimum touch target: 44x44px (achieved via `min-w-[44px] min-h-[44px]` or equivalent padding)

---

## Responsive Breakpoint Contract

| Breakpoint | Behavior |
|------------|----------|
| Below `md:` (< 768px) | Icon-only buttons, no text labels |
| `md:` and above (>= 768px) | Full text buttons (current behavior, unchanged) |

Implementation pattern:
```tsx
{/* Desktop: text button */}
<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 button */}
<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>
```

---

## Registry Safety

| Registry | Blocks Used | Safety Gate |
|----------|-------------|-------------|
| n/a | none | not required |

No shadcn or third-party registries. All components are hand-rolled with Tailwind CSS.

---

## Checker Sign-Off

- [ ] Dimension 1 Copywriting: PASS
- [ ] Dimension 2 Visuals: PASS
- [ ] Dimension 3 Color: PASS
- [ ] Dimension 4 Typography: PASS
- [ ] Dimension 5 Spacing: PASS
- [ ] Dimension 6 Registry Safety: PASS

**Approval:** pending
