# Phase 6: Category Icons - Context

**Gathered:** 2026-03-15
**Status:** Ready for planning

<domain>
## Phase Boundary

Replace the emoji-based category icon system with Lucide icons. Build an icon picker component, update all display points throughout the app, migrate existing emoji categories to equivalent Lucide icons via database migration, and clean up the old emoji code. No new category features (color coding, nesting, reordering) — those would be separate phases.

</domain>

<decisions>
## Implementation Decisions

### Icon picker UX
- Same portal-based popover pattern as current EmojiPicker (positioning, click-outside, escape, scroll)
- Search bar + category tab navigation (tabs = icon groups)
- Icon grid with Lucide icons rendered at consistent size
- Trigger button: selected icon in bordered square box, or "+" when empty (same dimensions as current EmojiPicker trigger)
- CategoryPicker combobox shows Lucide icon + name inline for each category (replacing emoji + name)
- CategoryPicker's inline create flow uses new IconPicker instead of EmojiPicker

### Icon display style
- Color: gray tones matching surrounding text (gray-500/600) — subtle, minimalist
- Stroke weight: default 2px (Lucide standard)
- Sizes: context-matched — ~20px in headers, ~16px in card badges/pills, ~14px inline in lists
- Card image placeholder areas (from Phase 5): Lucide category icon at ~32-40px on gray background, replacing emoji
- No color per category — all icons use same gray tones

### Emoji migration
- Automatic mapping table: emoji → Lucide icon name (e.g. 🏕→'tent', 🚲→'bike', 📷→'camera', 📦→'package')
- Unmapped emoji fall back to 'package' icon
- Uncategorized category (id=1): 📦 maps to 'package'
- Database column renamed from `emoji` (text) to `icon` (text), storing Lucide icon name strings
- Default value changes from "📦" to "package"
- Migration runs during `bun run db:push` — one-time schema change with data conversion

### Icon subset
- Curated subset of ~80-120 gear-relevant Lucide icons
- Organized into groups that match picker tabs: Outdoor, Travel, Sports, Electronics, Clothing, Tools, General
- Groups serve as both picker tabs and browsing categories
- Search filters across all groups

### Cleanup
- Old EmojiPicker.tsx and emojiData.ts fully removed after migration
- No emoji references remain anywhere in the codebase
- OnboardingWizard default categories updated to use Lucide icon names

### Claude's Discretion
- Exact icon selections for each curated group
- Icon data file structure (static data file similar to emojiData.ts or alternative)
- Migration script implementation details
- Exact emoji-to-icon mapping table completeness
- Popover sizing and grid column count
- Search algorithm (fuzzy vs exact match on icon names)

</decisions>

<code_context>
## Existing Code Insights

### Reusable Assets
- `EmojiPicker` component (`src/client/components/EmojiPicker.tsx`): 215-line component with portal popover, search, category tabs, click-outside, escape handling — architecture to replicate for IconPicker
- `CategoryPicker` (`src/client/components/CategoryPicker.tsx`): Combobox with search, keyboard nav, inline create — needs EmojiPicker → IconPicker swap
- `CategoryHeader` (`src/client/components/CategoryHeader.tsx`): Edit mode uses EmojiPicker — needs IconPicker swap
- `emojiData.ts` (`src/client/lib/emojiData.ts`): Data structure pattern to replicate for icon groups

### Established Patterns
- Portal-based popover rendering via `createPortal` (EmojiPicker)
- Click-outside detection via document mousedown listener
- Category data flows: `useCategories` hook → components render `cat.emoji` everywhere
- Drizzle ORM schema in `src/db/schema.ts` — `emoji` column on categories table
- `@hono/zod-validator` for request validation — `createCategorySchema` in schemas.ts

### Integration Points
- `src/db/schema.ts`: Rename `emoji` column to `icon`, change default from "📦" to "package"
- `src/shared/schemas.ts`: Update category schemas (field name emoji → icon)
- `src/shared/types.ts`: Types inferred from schemas — will auto-update
- `src/server/services/category.service.ts`: Update service functions
- `src/server/routes/categories.ts`: Update route handlers if needed
- `src/client/components/CategoryHeader.tsx`: Replace EmojiPicker with IconPicker, emoji → icon prop
- `src/client/components/CategoryPicker.tsx`: Replace EmojiPicker with IconPicker, emoji → icon display
- `src/client/components/ItemCard.tsx`: Replace `categoryEmoji` prop with `categoryIcon`, render Lucide icon
- `src/client/components/CandidateCard.tsx`: Same as ItemCard
- `src/client/components/ThreadCard.tsx`: Category icon display
- `src/client/components/OnboardingWizard.tsx`: Default categories use icon names instead of emoji
- `src/client/routes/collection/index.tsx`: Category display in collection view
- `src/client/routes/index.tsx`: Dashboard category display
- `src/db/seed.ts`: Seed data emoji → icon
- `tests/helpers/db.ts`: Update test helper CREATE TABLE and seed data

</code_context>

<specifics>
## Specific Ideas

- Icon picker should feel like a natural evolution of the EmojiPicker — same popover behavior, just rendering Lucide SVGs instead of emoji characters
- Curated icon groups should focus on gear/hobby relevance: outdoor camping, cycling, travel, electronics, clothing, tools
- The migration mapping should cover common gear emoji (tent, bike, backpack, camera, etc.) with 'package' as the universal fallback
- After migration, zero emoji should remain — fully consistent Lucide icon experience

</specifics>

<deferred>
## Deferred Ideas

None — discussion stayed within phase scope

</deferred>

---

*Phase: 06-category-icons*
*Context gathered: 2026-03-15*
