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>
This commit is contained in:
210
.planning/milestones/v2.2-phases/31-mobile-polish/31-01-PLAN.md
Normal file
210
.planning/milestones/v2.2-phases/31-mobile-polish/31-01-PLAN.md
Normal file
@@ -0,0 +1,210 @@
|
||||
---
|
||||
phase: 31-mobile-polish
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- src/client/routes/items/$itemId.tsx
|
||||
- src/client/routes/threads/$threadId/candidates/$candidateId.tsx
|
||||
autonomous: true
|
||||
requirements: [D-01, D-02, D-03, D-04]
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- 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
|
||||
artifacts:
|
||||
- src/client/routes/items/$itemId.tsx (modified)
|
||||
- src/client/routes/threads/$threadId/candidates/$candidateId.tsx (modified)
|
||||
key_links:
|
||||
- LucideIcon component used for all icons (not inline SVGs)
|
||||
- md: breakpoint matches BottomTabBar responsive pattern
|
||||
---
|
||||
|
||||
<objective>
|
||||
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.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@$HOME/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<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
|
||||
</context>
|
||||
|
||||
<interfaces>
|
||||
<!-- Key types and contracts the executor needs -->
|
||||
|
||||
From src/client/lib/iconData.tsx:
|
||||
```typescript
|
||||
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)
|
||||
```
|
||||
</interfaces>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Add responsive icon buttons to item detail page</name>
|
||||
<files>src/client/routes/items/$itemId.tsx</files>
|
||||
|
||||
<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>
|
||||
|
||||
<action>
|
||||
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.
|
||||
</action>
|
||||
|
||||
<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>
|
||||
|
||||
<verify>
|
||||
<automated>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"</automated>
|
||||
</verify>
|
||||
|
||||
<done>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.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Add responsive icon buttons to candidate detail page</name>
|
||||
<files>src/client/routes/threads/$threadId/candidates/$candidateId.tsx</files>
|
||||
|
||||
<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>
|
||||
|
||||
<action>
|
||||
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.
|
||||
</action>
|
||||
|
||||
<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>
|
||||
|
||||
<verify>
|
||||
<automated>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"</automated>
|
||||
</verify>
|
||||
|
||||
<done>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.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<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>
|
||||
|
||||
<verification>
|
||||
- `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
|
||||
</verification>
|
||||
|
||||
<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>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/31-mobile-polish/31-01-SUMMARY.md`
|
||||
</output>
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
phase: 31-mobile-polish
|
||||
plan: 01
|
||||
subsystem: client-routes
|
||||
tags: [mobile, responsive, icons, accessibility]
|
||||
key-files:
|
||||
created: []
|
||||
modified:
|
||||
- src/client/routes/items/$itemId.tsx
|
||||
- src/client/routes/threads/$threadId/candidates/$candidateId.tsx
|
||||
metrics:
|
||||
tasks: 2
|
||||
commits: 3
|
||||
files_changed: 2
|
||||
---
|
||||
|
||||
# Plan 01 Summary: Item Detail + Candidate Detail Icon Buttons
|
||||
|
||||
## What Was Built
|
||||
|
||||
Added responsive icon-based action buttons to item detail and candidate detail pages. On mobile viewports (below md: breakpoint / 768px), action buttons display as icon-only with 44px minimum touch targets. Desktop viewports retain full text buttons unchanged.
|
||||
|
||||
## Commits
|
||||
|
||||
| Task | Commit | Description |
|
||||
|------|--------|-------------|
|
||||
| 1 | 7effede | Add responsive icon buttons to item detail page |
|
||||
| 2 | b6f12fa | Add responsive icon buttons to candidate detail page |
|
||||
| fix | 97b1936 | Fix biome lint formatting for JSX expressions |
|
||||
|
||||
## Changes
|
||||
|
||||
### Item Detail ($itemId.tsx)
|
||||
- Duplicate button: paired desktop text / mobile icon (copy icon)
|
||||
- Delete/Remove button: paired desktop text / mobile icon (trash-2 icon), dynamic aria-label for reference vs owned items
|
||||
- Edit button: paired desktop text / mobile icon (pencil icon)
|
||||
|
||||
### Candidate Detail ($candidateId.tsx)
|
||||
- Header Edit button: split into desktop (text+icon) / mobile (icon-only)
|
||||
- Pick as Winner button: paired desktop text+icon / mobile icon (trophy icon)
|
||||
- Delete button: paired desktop text+icon / mobile icon (trash-2 icon)
|
||||
|
||||
## Deviations
|
||||
|
||||
None.
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- [x] All icon buttons have aria-label attributes
|
||||
- [x] All icon buttons have title attributes for tooltip
|
||||
- [x] All icon buttons have min-w-[44px] min-h-[44px] for touch targets
|
||||
- [x] md: breakpoint used consistently (matching BottomTabBar)
|
||||
- [x] Desktop buttons unchanged
|
||||
- [x] Edit mode Cancel/Save buttons not affected
|
||||
- [x] Lint passes
|
||||
- [x] Build succeeds
|
||||
224
.planning/milestones/v2.2-phases/31-mobile-polish/31-02-PLAN.md
Normal file
224
.planning/milestones/v2.2-phases/31-mobile-polish/31-02-PLAN.md
Normal file
@@ -0,0 +1,224 @@
|
||||
---
|
||||
phase: 31-mobile-polish
|
||||
plan: 02
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- src/client/routes/setups/$setupId.tsx
|
||||
- src/client/routes/global-items/$globalItemId.tsx
|
||||
autonomous: true
|
||||
requirements: [D-01, D-02, D-03, D-04]
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- Setup detail shows icon-only action buttons below md breakpoint
|
||||
- Setup detail shows text action buttons at md and above
|
||||
- Global item detail shows icon-only action buttons below md breakpoint
|
||||
- Global item 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
|
||||
- Setup page inline SVGs replaced with LucideIcon component
|
||||
artifacts:
|
||||
- src/client/routes/setups/$setupId.tsx (modified)
|
||||
- src/client/routes/global-items/$globalItemId.tsx (modified)
|
||||
key_links:
|
||||
- LucideIcon component used for all icons (not inline SVGs)
|
||||
- md: breakpoint matches BottomTabBar responsive pattern
|
||||
---
|
||||
|
||||
<objective>
|
||||
Add responsive icon-based action buttons to setup detail and global item detail pages, and migrate setup page inline SVGs to LucideIcon.
|
||||
|
||||
Purpose: Complete the mobile icon button rollout across all remaining detail pages. Also clean up inline SVGs on setup page by migrating to the project's LucideIcon component for consistency.
|
||||
Output: Modified setup detail and global item detail pages with responsive action buttons.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@$HOME/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<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
|
||||
</context>
|
||||
|
||||
<interfaces>
|
||||
<!-- Key types and contracts the executor needs -->
|
||||
|
||||
From src/client/lib/iconData.tsx:
|
||||
```typescript
|
||||
export function LucideIcon({ name, size, className, strokeWidth }: {
|
||||
name: string;
|
||||
size?: number;
|
||||
className?: string;
|
||||
strokeWidth?: number;
|
||||
}): React.ReactElement;
|
||||
```
|
||||
|
||||
Available icon names needed:
|
||||
- "plus" — Add Items button
|
||||
- "globe" — Public/Private toggle
|
||||
- "trash-2" — Delete Setup button
|
||||
- "message-square-plus" — Add to Thread button (verify exists in lucide-react)
|
||||
</interfaces>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Add responsive icon buttons to setup detail page and migrate inline SVGs to LucideIcon</name>
|
||||
<files>src/client/routes/setups/$setupId.tsx</files>
|
||||
|
||||
<read_first>
|
||||
- src/client/routes/setups/$setupId.tsx (current action buttons at lines ~155-210, inline SVGs for plus and globe icons)
|
||||
- src/client/lib/iconData.tsx (LucideIcon component — confirm import path)
|
||||
- .planning/phases/31-mobile-polish/31-UI-SPEC.md (icon mapping and color contract)
|
||||
</read_first>
|
||||
|
||||
<action>
|
||||
In src/client/routes/setups/$setupId.tsx:
|
||||
|
||||
**Step 1: Add LucideIcon import.**
|
||||
Add `import { LucideIcon } from "../../lib/iconData";` at the top of the file (if not already present).
|
||||
|
||||
**Step 2: Migrate inline SVGs to LucideIcon.**
|
||||
- Replace the inline plus SVG in the "Add Items" button (lines ~162-175) with `<LucideIcon name="plus" size={16} />`
|
||||
- Replace the inline globe SVG in the Public/Private toggle button (lines ~188-198) with `<LucideIcon name="globe" size={16} />`
|
||||
|
||||
**Step 3: Add responsive icon/text splitting to all action buttons.**
|
||||
|
||||
**Add Items button:**
|
||||
- Desktop: `<button className="hidden md:inline-flex items-center gap-2 px-4 py-2 bg-gray-700 hover:bg-gray-800 text-white text-sm font-medium rounded-lg transition-colors" ...><LucideIcon name="plus" size={16} />Add Items</button>`
|
||||
- Mobile: `<button className="md:hidden inline-flex items-center justify-center min-w-[44px] min-h-[44px] p-2 bg-gray-700 hover:bg-gray-800 text-white rounded-lg transition-colors" aria-label="Add Items" title="Add Items" ...><LucideIcon name="plus" size={16} /></button>`
|
||||
|
||||
**Public/Private toggle:**
|
||||
- Desktop: `<button className="hidden md:inline-flex items-center gap-1.5 px-3 py-2 text-sm font-medium rounded-lg transition-colors {conditional classes}" ...><LucideIcon name="globe" size={16} />{setup.isPublic ? "Public" : "Private"}</button>`
|
||||
- Mobile: `<button className="md:hidden inline-flex items-center justify-center min-w-[44px] min-h-[44px] p-2 rounded-lg transition-colors {conditional classes}" aria-label={setup.isPublic ? "Public" : "Private"} title={setup.isPublic ? "Public" : "Private"} ...><LucideIcon name="globe" size={16} /></button>`
|
||||
- Keep the conditional color classes: `text-green-700 bg-green-50 hover:bg-green-100` when public, `text-gray-500 bg-gray-50 hover:bg-gray-100` when private.
|
||||
|
||||
**Delete Setup button:**
|
||||
- Desktop: `<button className="hidden md:inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-red-600 bg-red-50 hover:bg-red-100 rounded-lg transition-colors" ...>Delete Setup</button>`
|
||||
- Mobile: `<button className="md:hidden inline-flex items-center justify-center min-w-[44px] min-h-[44px] p-2 text-red-600 bg-red-50 hover:bg-red-100 rounded-lg transition-colors" aria-label="Delete Setup" title="Delete Setup" ...><LucideIcon name="trash-2" size={16} /></button>`
|
||||
|
||||
Keep all existing onClick handlers, disabled states, and conditional rendering logic. The `flex-1` spacer between toggle and delete buttons remains.
|
||||
|
||||
Per D-01: Apply to setup detail page.
|
||||
Per D-02: Desktop text, mobile icons at md: breakpoint.
|
||||
Per D-03: Standard icon mapping — plus for Add, globe for toggle, trash-2 for Delete.
|
||||
</action>
|
||||
|
||||
<acceptance_criteria>
|
||||
- `$setupId.tsx` contains `import { LucideIcon }` or `import { LucideIcon` from iconData
|
||||
- `$setupId.tsx` contains `<LucideIcon name="plus"` (replacing inline plus SVG)
|
||||
- `$setupId.tsx` contains `<LucideIcon name="globe"` (replacing inline globe SVG)
|
||||
- `$setupId.tsx` contains `<LucideIcon name="trash-2"` for Delete Setup icon
|
||||
- `$setupId.tsx` contains `aria-label="Add Items"` on an icon button
|
||||
- `$setupId.tsx` contains `aria-label="Delete Setup"` on an icon button
|
||||
- `$setupId.tsx` contains `min-w-[44px]` for touch target sizing (at least 3 occurrences)
|
||||
- `$setupId.tsx` contains NO inline `<svg` elements (all migrated to LucideIcon)
|
||||
- `$setupId.tsx` contains `hidden md:inline-flex` on desktop text buttons
|
||||
</acceptance_criteria>
|
||||
|
||||
<verify>
|
||||
<automated>grep -c "aria-label" src/client/routes/setups/\$setupId.tsx | grep -q "[3-9]" && grep -c "LucideIcon" src/client/routes/setups/\$setupId.tsx | grep -q "[3-9]" && ! grep -q "<svg" src/client/routes/setups/\$setupId.tsx && echo "PASS" || echo "FAIL"</automated>
|
||||
</verify>
|
||||
|
||||
<done>Setup detail page shows icon-only Add Items/Public toggle/Delete Setup buttons on mobile, full text buttons on desktop. Inline SVGs replaced with LucideIcon. All icon buttons have aria-label and 44px minimum touch targets.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Add responsive icon buttons to global item detail page</name>
|
||||
<files>src/client/routes/global-items/$globalItemId.tsx</files>
|
||||
|
||||
<read_first>
|
||||
- src/client/routes/global-items/$globalItemId.tsx (current action buttons at lines ~167-193)
|
||||
- src/client/lib/iconData.tsx (LucideIcon component, verify "message-square-plus" icon exists in lucide-react)
|
||||
- .planning/phases/31-mobile-polish/31-UI-SPEC.md (icon mapping and color contract)
|
||||
</read_first>
|
||||
|
||||
<action>
|
||||
In src/client/routes/global-items/$globalItemId.tsx:
|
||||
|
||||
**Step 1: Add LucideIcon import.**
|
||||
Add `import { LucideIcon } from "../../lib/iconData";` at the top of the file (if not already present).
|
||||
|
||||
**Step 2: Add responsive icon/text splitting to action buttons.**
|
||||
|
||||
The action buttons section (`flex gap-3 mb-6` containing "Add to Collection" and "Add to Thread") needs responsive variants:
|
||||
|
||||
**Add to Collection button:**
|
||||
- Desktop: `<button className="hidden md:inline-flex items-center gap-2 bg-gray-700 text-white rounded-lg px-5 py-2.5 text-sm font-medium hover:bg-gray-800 transition-colors" ...>Add to Collection</button>`
|
||||
- Mobile: `<button className="md:hidden inline-flex items-center justify-center min-w-[44px] min-h-[44px] p-2.5 bg-gray-700 text-white rounded-lg hover:bg-gray-800 transition-colors" aria-label="Add to Collection" title="Add to Collection" ...><LucideIcon name="plus" size={16} /></button>`
|
||||
|
||||
**Add to Thread button:**
|
||||
First, verify that "message-square-plus" exists in lucide-react. If it does not, use "message-square" instead. Check by running: `grep -r "message-square-plus" node_modules/lucide-react/dist/ 2>/dev/null | head -1`
|
||||
- Desktop: `<button className="hidden md:inline-flex items-center gap-2 bg-white text-gray-700 border border-gray-200 rounded-lg px-5 py-2.5 text-sm font-medium hover:bg-gray-50 transition-colors" ...>Add to Thread</button>`
|
||||
- Mobile: `<button className="md:hidden inline-flex items-center justify-center min-w-[44px] min-h-[44px] p-2.5 bg-white text-gray-700 border border-gray-200 rounded-lg hover:bg-gray-50 transition-colors" aria-label="Add to Thread" title="Add to Thread" ...><LucideIcon name="message-square-plus" size={16} /></button>`
|
||||
|
||||
Keep all existing onClick handlers (including the auth check that calls `openAuthPrompt()` for unauthenticated users).
|
||||
|
||||
Per D-01: Apply to catalog/global item detail page.
|
||||
Per D-02: Desktop text, mobile icons at md: breakpoint.
|
||||
Per D-03: Standard icon mapping — plus for Add to Collection, message-square-plus for Add to Thread.
|
||||
</action>
|
||||
|
||||
<acceptance_criteria>
|
||||
- `$globalItemId.tsx` contains `import { LucideIcon }` from iconData
|
||||
- `$globalItemId.tsx` contains `<LucideIcon name="plus"` for Add to Collection icon
|
||||
- `$globalItemId.tsx` contains `<LucideIcon name="message-square` for Add to Thread icon
|
||||
- `$globalItemId.tsx` contains `aria-label="Add to Collection"` on an icon button
|
||||
- `$globalItemId.tsx` contains `aria-label="Add to Thread"` on an icon button
|
||||
- `$globalItemId.tsx` contains `min-w-[44px]` for touch target sizing (at least 2 occurrences)
|
||||
- `$globalItemId.tsx` contains `hidden md:inline-flex` on desktop text buttons (at least 2 occurrences)
|
||||
</acceptance_criteria>
|
||||
|
||||
<verify>
|
||||
<automated>grep -c "aria-label" src/client/routes/global-items/\$globalItemId.tsx | grep -q "[2-9]" && grep -c "LucideIcon" src/client/routes/global-items/\$globalItemId.tsx | grep -q "[2-9]" && echo "PASS" || echo "FAIL"</automated>
|
||||
</verify>
|
||||
|
||||
<done>Global item detail page shows icon-only Add to Collection/Add to Thread buttons on mobile, full text buttons on desktop. All icon buttons have aria-label and 44px minimum touch targets.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<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-02 | 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>
|
||||
|
||||
<verification>
|
||||
- `bun run lint` passes with no errors in modified files
|
||||
- `bun test` passes (no test regressions)
|
||||
- Manual: Open setup detail at mobile viewport (< 768px) — see icon-only buttons
|
||||
- Manual: Open global item detail at mobile viewport — see icon-only buttons
|
||||
- Manual: Open both pages at desktop viewport — see text buttons
|
||||
- No inline `<svg` elements remain in setup detail page
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Setup detail page renders icon-only Add Items/Public toggle/Delete Setup buttons on mobile
|
||||
- Global item detail page renders icon-only Add to Collection/Add to Thread buttons on mobile
|
||||
- Desktop rendering unchanged (text buttons with optional icons)
|
||||
- Setup page inline SVGs fully replaced with LucideIcon component
|
||||
- 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 across both pages
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/31-mobile-polish/31-02-SUMMARY.md`
|
||||
</output>
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
phase: 31-mobile-polish
|
||||
plan: 02
|
||||
subsystem: client-routes
|
||||
tags: [mobile, responsive, icons, accessibility, cleanup]
|
||||
key-files:
|
||||
created: []
|
||||
modified:
|
||||
- src/client/routes/setups/$setupId.tsx
|
||||
- src/client/routes/global-items/$globalItemId.tsx
|
||||
metrics:
|
||||
tasks: 2
|
||||
commits: 2
|
||||
files_changed: 2
|
||||
---
|
||||
|
||||
# Plan 02 Summary: Setup Detail + Global Item Detail Icon Buttons
|
||||
|
||||
## What Was Built
|
||||
|
||||
Added responsive icon-based action buttons to setup detail and global item detail pages. Migrated inline SVGs on the setup page to LucideIcon component for consistency. On mobile viewports (below md: breakpoint), action buttons display as icon-only with 44px minimum touch targets.
|
||||
|
||||
## Commits
|
||||
|
||||
| Task | Commit | Description |
|
||||
|------|--------|-------------|
|
||||
| 1 | 410a649 | Add responsive icon buttons to setup detail, migrate inline SVGs to LucideIcon |
|
||||
| 2 | f69861d | Add responsive icon buttons to global item detail page |
|
||||
|
||||
## Changes
|
||||
|
||||
### Setup Detail ($setupId.tsx)
|
||||
- Add Items button: paired desktop text / mobile icon (plus icon via LucideIcon, replacing inline SVG)
|
||||
- Public/Private toggle: paired desktop text / mobile icon (globe icon via LucideIcon, replacing inline SVG)
|
||||
- Delete Setup button: paired desktop text / mobile icon (trash-2 icon)
|
||||
- All inline SVGs removed and replaced with LucideIcon component
|
||||
|
||||
### Global Item Detail ($globalItemId.tsx)
|
||||
- Added LucideIcon import (was not previously imported)
|
||||
- Add to Collection button: paired desktop text / mobile icon (plus icon)
|
||||
- Add to Thread button: paired desktop text / mobile icon (message-square-plus icon)
|
||||
|
||||
## Deviations
|
||||
|
||||
None.
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- [x] All icon buttons have aria-label attributes
|
||||
- [x] All icon buttons have title attributes for tooltip
|
||||
- [x] All icon buttons have min-w-[44px] min-h-[44px] for touch targets
|
||||
- [x] md: breakpoint used consistently
|
||||
- [x] No inline SVGs remain in setup detail page
|
||||
- [x] LucideIcon imported in global item detail
|
||||
- [x] Auth check (openAuthPrompt) preserved in global item detail buttons
|
||||
- [x] Lint passes
|
||||
- [x] Build succeeds
|
||||
@@ -0,0 +1,88 @@
|
||||
# Phase 31: Mobile Polish - Context
|
||||
|
||||
**Gathered:** 2026-04-12
|
||||
**Status:** Ready for planning
|
||||
|
||||
<domain>
|
||||
## Phase Boundary
|
||||
|
||||
Replace text-based action buttons with icon buttons on mobile across all detail pages. This is a focused UI polish phase — no new features, just better mobile touch UX.
|
||||
|
||||
</domain>
|
||||
|
||||
<decisions>
|
||||
## Implementation Decisions
|
||||
|
||||
### Icon Actions Scope
|
||||
- **D-01:** Apply icon-based action buttons on mobile to **all detail pages**: item detail, candidate detail, setup detail, catalog detail — anywhere action buttons appear.
|
||||
- **D-02:** Desktop keeps text buttons. Mobile (below sm: breakpoint) switches to icons. Uses the same responsive breakpoint as BottomTabBar.
|
||||
- **D-03:** Standard icon mapping: pencil/edit for Edit, trash for Delete, copy for Duplicate, share for Share (if applicable).
|
||||
|
||||
### Mobile UX
|
||||
- **D-04:** No other specific mobile UX issues to address — user is happy with current mobile support beyond the icon buttons.
|
||||
|
||||
### Claude's Discretion
|
||||
- Whether to add long-press tooltips on icon buttons for discoverability
|
||||
- Exact breakpoint for icon/text switch (likely `sm:` matching BottomTabBar)
|
||||
- Icon sizing and spacing for comfortable touch targets (minimum 44px)
|
||||
- Whether to use Lucide icons (already in project) or keep inline SVGs
|
||||
- Any additional small polish items noticed during implementation (tap target sizes, etc.)
|
||||
|
||||
</decisions>
|
||||
|
||||
<canonical_refs>
|
||||
## Canonical References
|
||||
|
||||
**Downstream agents MUST read these before planning or implementing.**
|
||||
|
||||
### Action Buttons (need icon variants)
|
||||
- `src/client/routes/items/$itemId.tsx` — Item detail actions: Duplicate, Delete/Remove, Edit (lines ~186-210)
|
||||
- `src/client/routes/threads/$threadId/candidates/$candidateId.tsx` — Candidate detail actions
|
||||
- `src/client/routes/setups/$setupId.tsx` — Setup detail actions (if any)
|
||||
- `src/client/routes/global-items/$globalItemId.tsx` — Catalog detail actions (if any)
|
||||
|
||||
### Responsive Patterns
|
||||
- `src/client/components/BottomTabBar.tsx` — Mobile bottom nav, uses `md:hidden` breakpoint
|
||||
- `src/client/components/TopNav.tsx` — Desktop top nav, uses `hidden md:flex` breakpoint
|
||||
|
||||
### Icon System
|
||||
- `src/client/lib/iconData.ts` — LucideIcon component, 119 curated icons
|
||||
|
||||
</canonical_refs>
|
||||
|
||||
<code_context>
|
||||
## Existing Code Insights
|
||||
|
||||
### Reusable Assets
|
||||
- `LucideIcon` component — renders Lucide icons by name. Already used throughout the app.
|
||||
- BottomTabBar responsive pattern — `md:hidden` / `hidden md:flex` for mobile/desktop switch.
|
||||
- Tailwind responsive classes already established throughout the codebase.
|
||||
|
||||
### Established Patterns
|
||||
- Mobile/desktop responsive switch at `md:` breakpoint (768px) — consistent with BottomTabBar and TopNav.
|
||||
- Action buttons are inline `<button>` elements with text — straightforward to add responsive icon variants.
|
||||
|
||||
### Integration Points
|
||||
- Each detail page's action button section — wrap in responsive containers showing icons on mobile, text on desktop.
|
||||
|
||||
</code_context>
|
||||
|
||||
<specifics>
|
||||
## Specific Ideas
|
||||
|
||||
- This is a small, focused phase. The user is generally happy with mobile support — just the text buttons on detail pages are the pain point.
|
||||
- Keep it simple — responsive icon/text swap using existing Tailwind breakpoints and LucideIcon.
|
||||
|
||||
</specifics>
|
||||
|
||||
<deferred>
|
||||
## Deferred Ideas
|
||||
|
||||
None — discussion stayed within phase scope
|
||||
|
||||
</deferred>
|
||||
|
||||
---
|
||||
|
||||
*Phase: 31-mobile-polish*
|
||||
*Context gathered: 2026-04-12*
|
||||
@@ -0,0 +1,53 @@
|
||||
# Phase 31: Mobile Polish - Discussion Log
|
||||
|
||||
> **Audit trail only.** Do not use as input to planning, research, or execution agents.
|
||||
> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.
|
||||
|
||||
**Date:** 2026-04-12
|
||||
**Phase:** 31-mobile-polish
|
||||
**Areas discussed:** Icon actions scope, Other mobile UX tweaks
|
||||
|
||||
---
|
||||
|
||||
## Icon Actions Scope
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| All detail pages | Item, candidate, setup, catalog detail — full consistency | ✓ |
|
||||
| Item + candidate only | Most-used pages on mobile | |
|
||||
| You decide | Claude applies where needed | |
|
||||
|
||||
**User's choice:** All detail pages
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| Tooltip on long-press | Help users learn icons | |
|
||||
| No tooltips | Icons are universally understood | |
|
||||
| You decide | Claude picks | ✓ |
|
||||
|
||||
**User's choice:** You decide (Claude's discretion)
|
||||
|
||||
---
|
||||
|
||||
## Other Mobile UX Tweaks
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| Tap targets too small | Minimum 44px touch targets needed | |
|
||||
| Scroll/spacing issues | Content too close to edges, etc. | |
|
||||
| Nothing specific | Happy with mobile otherwise | ✓ |
|
||||
|
||||
**User's choice:** Nothing specific — icon buttons are the main thing
|
||||
|
||||
---
|
||||
|
||||
## Claude's Discretion
|
||||
|
||||
- Long-press tooltips
|
||||
- Breakpoint for icon/text switch
|
||||
- Icon sizing and touch targets
|
||||
- Additional small polish if noticed
|
||||
|
||||
## Deferred Ideas
|
||||
|
||||
None
|
||||
143
.planning/milestones/v2.2-phases/31-mobile-polish/31-RESEARCH.md
Normal file
143
.planning/milestones/v2.2-phases/31-mobile-polish/31-RESEARCH.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# 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
|
||||
@@ -0,0 +1,43 @@
|
||||
---
|
||||
phase: 31
|
||||
slug: mobile-polish
|
||||
status: clean
|
||||
depth: standard
|
||||
files_reviewed: 4
|
||||
findings:
|
||||
critical: 0
|
||||
warning: 0
|
||||
info: 0
|
||||
total: 0
|
||||
reviewed: 2026-04-12
|
||||
---
|
||||
|
||||
# Phase 31: Mobile Polish — Code Review
|
||||
|
||||
## Scope
|
||||
|
||||
| File | Lines Changed | Status |
|
||||
|------|--------------|--------|
|
||||
| src/client/routes/items/$itemId.tsx | +35 / -3 | Clean |
|
||||
| src/client/routes/threads/$threadId/candidates/$candidateId.tsx | +45 / -10 | Clean |
|
||||
| src/client/routes/setups/$setupId.tsx | +42 / -28 | Clean |
|
||||
| src/client/routes/global-items/$globalItemId.tsx | +37 / -2 | Clean |
|
||||
|
||||
## Summary
|
||||
|
||||
No issues found. All 4 files pass review at standard depth.
|
||||
|
||||
### Patterns Verified
|
||||
|
||||
- **Consistent breakpoint usage:** All files use `md:` (768px) matching BottomTabBar and TopNav
|
||||
- **Accessibility:** Every icon-only button has `aria-label` and `title` attributes
|
||||
- **Touch targets:** All mobile buttons have `min-w-[44px] min-h-[44px]`
|
||||
- **No handler duplication bugs:** onClick handlers on paired buttons are identical (same function references)
|
||||
- **No stale imports:** LucideIcon was already imported in itemId.tsx, candidateId.tsx, setupId.tsx; correctly added to globalItemId.tsx
|
||||
- **Inline SVG cleanup:** Setup page inline SVGs fully replaced with LucideIcon (plus, globe)
|
||||
- **Edit mode isolation:** Cancel/Save buttons in edit mode are untouched across all files
|
||||
- **Conditional rendering preserved:** isEditing, isActive, isAuthenticated guards unchanged
|
||||
|
||||
## Findings
|
||||
|
||||
None.
|
||||
33
.planning/milestones/v2.2-phases/31-mobile-polish/31-UAT.md
Normal file
33
.planning/milestones/v2.2-phases/31-mobile-polish/31-UAT.md
Normal file
@@ -0,0 +1,33 @@
|
||||
---
|
||||
status: complete
|
||||
phase: 31-mobile-polish
|
||||
source: [31-01-SUMMARY.md, 31-02-SUMMARY.md]
|
||||
started: 2026-04-12T19:45:00Z
|
||||
updated: 2026-04-12T19:45:00Z
|
||||
---
|
||||
|
||||
## Current Test
|
||||
|
||||
[testing complete]
|
||||
|
||||
## Tests
|
||||
|
||||
### 1. Icon action buttons on mobile
|
||||
expected: Open an item detail page on mobile. Action buttons (Edit, Delete, Duplicate) show as icons only instead of text labels.
|
||||
result: pass
|
||||
|
||||
### 2. Icons across all detail pages
|
||||
expected: Candidate detail, setup detail, catalog item detail all have icon buttons on mobile too.
|
||||
result: pass
|
||||
|
||||
## Summary
|
||||
|
||||
total: 2
|
||||
passed: 2
|
||||
issues: 0
|
||||
pending: 0
|
||||
skipped: 0
|
||||
|
||||
## Gaps
|
||||
|
||||
[none]
|
||||
161
.planning/milestones/v2.2-phases/31-mobile-polish/31-UI-SPEC.md
Normal file
161
.planning/milestones/v2.2-phases/31-mobile-polish/31-UI-SPEC.md
Normal file
@@ -0,0 +1,161 @@
|
||||
---
|
||||
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
|
||||
@@ -0,0 +1,75 @@
|
||||
---
|
||||
phase: 31
|
||||
slug: mobile-polish
|
||||
status: draft
|
||||
nyquist_compliant: true
|
||||
wave_0_complete: false
|
||||
created: 2026-04-12
|
||||
---
|
||||
|
||||
# Phase 31 — Validation Strategy
|
||||
|
||||
> Per-phase validation contract for feedback sampling during execution.
|
||||
|
||||
---
|
||||
|
||||
## Test Infrastructure
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **Framework** | Bun test (unit/integration) + Playwright (E2E) |
|
||||
| **Config file** | `playwright.config.ts` |
|
||||
| **Quick run command** | `bun test` |
|
||||
| **Full suite command** | `bun test && bun run test:e2e` |
|
||||
| **Estimated runtime** | ~15 seconds (unit) + ~30 seconds (E2E) |
|
||||
|
||||
---
|
||||
|
||||
## Sampling Rate
|
||||
|
||||
- **After every task commit:** Run `bun test`
|
||||
- **After every plan wave:** Run `bun test && bun run test:e2e`
|
||||
- **Before `/gsd-verify-work`:** Full suite must be green
|
||||
- **Max feedback latency:** 45 seconds
|
||||
|
||||
---
|
||||
|
||||
## Per-Task Verification Map
|
||||
|
||||
| Task ID | Plan | Wave | Requirement | Test Type | Automated Command | Status |
|
||||
|---------|------|------|-------------|-----------|-------------------|--------|
|
||||
| 31-01-01 | 01 | 1 | D-01 | grep | `grep -r "aria-label" src/client/routes/items/\$itemId.tsx` | pending |
|
||||
| 31-01-02 | 01 | 1 | D-02 | grep | `grep -r "md:hidden\|hidden md:" src/client/routes/items/\$itemId.tsx` | pending |
|
||||
| 31-02-01 | 02 | 1 | D-01 | grep | `grep -r "aria-label" src/client/routes/threads/` | pending |
|
||||
| 31-03-01 | 03 | 1 | D-01 | grep | `grep -r "aria-label" src/client/routes/setups/\$setupId.tsx` | pending |
|
||||
| 31-04-01 | 04 | 1 | D-01 | grep | `grep -r "aria-label" src/client/routes/global-items/\$globalItemId.tsx` | pending |
|
||||
|
||||
*Status: pending*
|
||||
|
||||
---
|
||||
|
||||
## Wave 0 Requirements
|
||||
|
||||
Existing infrastructure covers all phase requirements. No new test files needed — validation is grep-based (checking for aria-label, responsive classes, LucideIcon usage).
|
||||
|
||||
---
|
||||
|
||||
## Manual-Only Verifications
|
||||
|
||||
| Behavior | Requirement | Why Manual | Test Instructions |
|
||||
|----------|-------------|------------|-------------------|
|
||||
| Icon buttons visible on mobile viewport | D-01, D-02 | Visual rendering requires browser | Open detail pages at 375px width, verify icon-only buttons |
|
||||
| Text buttons visible on desktop viewport | D-02 | Visual rendering requires browser | Open detail pages at 1024px width, verify text buttons |
|
||||
| Touch targets comfortable | D-03 | Physical interaction needed | Tap icon buttons on mobile device |
|
||||
|
||||
---
|
||||
|
||||
## Validation Sign-Off
|
||||
|
||||
- [x] All tasks have automated verify or manual verification
|
||||
- [x] Sampling continuity: no 3 consecutive tasks without automated verify
|
||||
- [x] No watch-mode flags
|
||||
- [x] Feedback latency < 45s
|
||||
- [x] `nyquist_compliant: true` set in frontmatter
|
||||
|
||||
**Approval:** pending
|
||||
@@ -0,0 +1,59 @@
|
||||
---
|
||||
phase: 31
|
||||
slug: mobile-polish
|
||||
status: passed
|
||||
verified: 2026-04-12
|
||||
plans_verified: 2
|
||||
must_haves_verified: 7
|
||||
must_haves_total: 7
|
||||
---
|
||||
|
||||
# Phase 31: Mobile Polish — Verification
|
||||
|
||||
## Phase Goal
|
||||
|
||||
> Mobile item views use icon-based action buttons instead of text labels, with small UX refinements across touch interactions
|
||||
|
||||
## Must-Haves Verification
|
||||
|
||||
| # | Must-Have | Status | Evidence |
|
||||
|---|-----------|--------|----------|
|
||||
| 1 | Item detail shows icon-only buttons below md: | PASS | 3x md:hidden buttons in $itemId.tsx |
|
||||
| 2 | Item detail shows text buttons at md: and above | PASS | 3x hidden md:inline-flex buttons in $itemId.tsx |
|
||||
| 3 | Candidate detail shows icon-only buttons below md: | PASS | 3x md:hidden buttons in $candidateId.tsx |
|
||||
| 4 | Candidate detail shows text buttons at md: and above | PASS | 3x hidden md:inline-flex buttons in $candidateId.tsx |
|
||||
| 5 | Setup detail shows icon-only buttons below md: | PASS | 3x md:hidden buttons in $setupId.tsx |
|
||||
| 6 | Global item detail shows icon-only buttons below md: | PASS | 2x md:hidden buttons in $globalItemId.tsx |
|
||||
| 7 | All icon buttons have aria-label and 44px touch targets | PASS | 11 aria-label attributes, 11 min-w-[44px] classes across all files |
|
||||
|
||||
## Accessibility Verification
|
||||
|
||||
| File | aria-label Count | min-w-[44px] Count | title Count |
|
||||
|------|-----------------|-------------------|-------------|
|
||||
| $itemId.tsx | 3 | 3 | 3 |
|
||||
| $candidateId.tsx | 3 | 3 | 3 |
|
||||
| $setupId.tsx | 3 | 3 | 3 |
|
||||
| $globalItemId.tsx | 2 | 2 | 2 |
|
||||
|
||||
## Consistency Verification
|
||||
|
||||
| Check | Status | Detail |
|
||||
|-------|--------|--------|
|
||||
| Breakpoint consistency | PASS | All files use md: (768px) matching BottomTabBar |
|
||||
| LucideIcon usage | PASS | All icons via LucideIcon, no inline SVGs remaining |
|
||||
| Edit mode isolation | PASS | Cancel/Save buttons unaffected in all files |
|
||||
| Desktop unchanged | PASS | Text buttons preserved at md: and above |
|
||||
| Lint | PASS | bun run lint exits 0 |
|
||||
| Build | PASS | bun run build succeeds |
|
||||
|
||||
## Human Verification
|
||||
|
||||
| Item | Expected | Status |
|
||||
|------|----------|--------|
|
||||
| Mobile viewport (< 768px) shows icon-only buttons on all detail pages | Icon buttons visible, text hidden | Pending manual test |
|
||||
| Desktop viewport (>= 768px) shows text buttons on all detail pages | Text buttons visible, icon buttons hidden | Pending manual test |
|
||||
| Touch targets comfortable on mobile device | 44px minimum, easy to tap | Pending manual test |
|
||||
|
||||
## Result
|
||||
|
||||
**Status: PASSED** — All automated must-haves verified. 3 items pending manual visual testing.
|
||||
Reference in New Issue
Block a user