diff --git a/docs/superpowers/specs/2026-04-23-tag-selector-search-design.md b/docs/superpowers/specs/2026-04-23-tag-selector-search-design.md new file mode 100644 index 0000000..cda96b9 --- /dev/null +++ b/docs/superpowers/specs/2026-04-23-tag-selector-search-design.md @@ -0,0 +1,66 @@ +# Tag Selector Search in CatalogSearchOverlay + +**Date:** 2026-04-23 +**Status:** Approved, ready for implementation + +## Problem + +The tag filter in the global catalog search overlay (`src/client/components/CatalogSearchOverlay.tsx`) renders every tag as a chip in a narrow sidebar. When the tag taxonomy grows, users must scroll through the full list to find the one they want. There is no typing-to-filter affordance. + +## Goal + +Allow users to narrow the visible tag list by typing a query, without disrupting selection or the surrounding filter UX. + +## Scope + +Single-file change to `src/client/components/CatalogSearchOverlay.tsx`. No backend changes. No new dependencies. No shared component extraction. + +## Behavior + +**Search state** + +- Add a `tagSearch: string` local state, defaulting to `""`. + +**Input rendering** + +- Render a compact text input at the top of the "Tags" section inside the filter sidebar (above the existing tag chip list). +- Style to match the existing minimalist look: similar to the main catalog search input but narrower and with reduced padding so it fits the 224px sidebar. +- Render the input only when `tags.length > 8`. Below that threshold scrolling isn't an issue and the extra chrome is noise. +- Placeholder text: `Filter tags...`. + +**Filter logic** + +- Compute the visible tag list as: tags whose `name` includes `tagSearch` (case-insensitive substring match). +- A selected tag that does not match the query is hidden from the sidebar list. It remains active and visible as a blue pill in the header row (existing behavior — no change required). +- When the filter produces zero visible tags, render a small muted hint: `No tags match`. + +**Reset** + +- When the overlay closes, reset `tagSearch` to `""` by extending the existing reset `useEffect` at lines 84–98. + +## Non-goals + +- No fuzzy matching or ranking — plain case-insensitive substring is sufficient. +- No keyboard navigation of the tag list (arrow keys / Enter to toggle). Users click chips. +- No persistence of `tagSearch` across overlay open/close cycles. +- No changes to the active-pill row, active-filter counting, or weight/price range filters. +- No unit tests — pure UI state, covered by manual UAT verification during milestone close-out. + +## Acceptance criteria + +1. Typing in the tag search input immediately narrows the visible tag chips by case-insensitive substring match on tag name. +2. Tags that are selected but no longer match the query disappear from the sidebar list and remain selected (visible as blue pills in the header). +3. With 8 or fewer tags, the search input is not rendered. +4. With more than 8 tags and a query that matches none of them, a `No tags match` hint is shown. +5. Closing the overlay and reopening it shows an empty tag search input. +6. No regressions to other filter behavior (weight range, price range, active filter pills, clear-all). + +## Files touched + +- `src/client/components/CatalogSearchOverlay.tsx` + +## Out of scope for this spec + +- Tag selector in other components (admin items list, etc.). +- Any server-side tag search or query. +- Hierarchy-aware filtering (parent match reveals children, etc.) — tags are consumed flat from `useTags()` in this component.