diff --git a/.gsd/PROJECT.md b/.gsd/PROJECT.md index c9155ce..86d9e35 100644 --- a/.gsd/PROJECT.md +++ b/.gsd/PROJECT.md @@ -37,6 +37,11 @@ Ten milestones complete. The system is deployed and running on ub01 at `http://u - **Related techniques cross-linking** — Every technique page shows up to 4 related techniques scored by creator overlap, topic match, and shared tags. Curated links take priority; dynamic results fill remaining slots. - **Topic color coding & page transitions** — Per-category accent colors on sub-topic pages and search results. CSS-only fade-in page enter animation on all 7 public pages. - **Search autocomplete** — Shared SearchAutocomplete component with popular suggestions on empty focus and debounced typeahead on 2+ characters. Used on Home and SearchResults pages. +- **Interaction delight** — Card hover animations (scale + shadow), staggered entrance animations on card grids, featured technique glow treatment, Random Technique discovery button. +- **Topics collapse/expand** — Topics page loads with all categories collapsed. Smooth CSS grid-template-rows animation on expand/collapse. +- **Creator stats pills** — Creator detail page topic stats rendered as colored pill badges matching category colors. +- **Tag overflow** — Shared TagList component caps visible tags at 4 with "+N more" overflow pill. Applied across all 5 tag-rendering sites. +- **Empty subtopic handling** — Subtopics with 0 techniques show "Coming soon" badge instead of dead-end links. ### Stack diff --git a/.gsd/milestones/M011/M011-ROADMAP.md b/.gsd/milestones/M011/M011-ROADMAP.md index 097c0e7..582c017 100644 --- a/.gsd/milestones/M011/M011-ROADMAP.md +++ b/.gsd/milestones/M011/M011-ROADMAP.md @@ -7,6 +7,6 @@ Transform Chrysopedia from functionally adequate to engaging and accessible. Add | ID | Slice | Risk | Depends | Done | After this | |----|-------|------|---------|------|------------| | S01 | Interaction Delight & Discovery | medium | — | ✅ | Cards animate on hover with scale+shadow. Card grids stagger entrance. Featured technique has glow treatment. Random Technique button navigates to a random page. | -| S02 | Topics, Creator Stats & Card Polish | medium | — | ⬜ | Topics page loads collapsed, expands with animation. Creator stats show colored topic pills. Cards limit tags to 4 with +N more. Empty subtopics show Coming soon. | +| S02 | Topics, Creator Stats & Card Polish | medium | — | ✅ | Topics page loads collapsed, expands with animation. Creator stats show colored topic pills. Cards limit tags to 4 with +N more. Empty subtopics show Coming soon. | | S03 | Global Search & Mobile Navigation | medium | — | ⬜ | Compact search bar in nav on all pages. Cmd+K focuses it. Mobile viewport shows hamburger menu. | | S04 | Accessibility & SEO Fixes | low | — | ⬜ | Every page has single H1, skip-to-content link, AA contrast text, and descriptive browser tab title. | diff --git a/.gsd/milestones/M011/slices/S02/S02-SUMMARY.md b/.gsd/milestones/M011/slices/S02/S02-SUMMARY.md new file mode 100644 index 0000000..78591b6 --- /dev/null +++ b/.gsd/milestones/M011/slices/S02/S02-SUMMARY.md @@ -0,0 +1,96 @@ +--- +id: S02 +parent: M011 +milestone: M011 +provides: + - TagList component available for any future tag-rendering sites + - Collapse/expand animation pattern reusable for other accordion-style UI +requires: + [] +affects: + [] +key_files: + - frontend/src/components/TagList.tsx + - frontend/src/pages/TopicsBrowse.tsx + - frontend/src/pages/CreatorDetail.tsx + - frontend/src/pages/Home.tsx + - frontend/src/pages/SearchResults.tsx + - frontend/src/pages/SubTopicPage.tsx + - frontend/src/App.css +key_decisions: + - Used CSS grid-template-rows 0fr/1fr for smooth collapse/expand — no JS height measurement needed + - Added pillClass prop to TagList to preserve existing pill--tag styling in Home.tsx + - Kept single dot separator between video count and topic pills in CreatorDetail +patterns_established: + - Shared TagList component as single source for tag rendering with overflow — all 5 tag sites now use it + - CSS grid-template-rows 0fr/1fr pattern for animating variable-height content without JS +observability_surfaces: + - none +drill_down_paths: + - .gsd/milestones/M011/slices/S02/tasks/T01-SUMMARY.md + - .gsd/milestones/M011/slices/S02/tasks/T02-SUMMARY.md + - .gsd/milestones/M011/slices/S02/tasks/T03-SUMMARY.md +duration: "" +verification_result: passed +completed_at: 2026-03-31T08:36:24.689Z +blocker_discovered: false +--- + +# S02: Topics, Creator Stats & Card Polish + +**Topics page loads collapsed with smooth CSS grid animation, creator stats use colored topic pills, tags capped at 4 with +N overflow via shared TagList component, empty subtopics show Coming soon badge.** + +## What Happened + +Three tasks delivered four UI improvements across the frontend. + +**T01 — Topics collapse/expand animation.** Changed TopicsBrowse to initialize with an empty expanded set (all categories collapsed on load). Replaced the conditional render (`{isExpanded && ...}`) with an always-rendered wrapper using CSS `grid-template-rows: 0fr/1fr` animation. The wrapper transitions over 300ms, the inner div uses `overflow: hidden; min-height: 0` for smooth clipping. No JS measurement needed. + +**T02 — Creator stats colored pills.** Replaced the run-on dot-separated topic stats in CreatorDetail with `badge badge--cat-{slug}` spans wrapped in a flex container. Reuses the `catSlug()` utility and existing badge color classes from the Topics page, so colors are consistent across the app. + +**T03 — Shared TagList + empty subtopic handling.** Created `TagList.tsx` component with configurable `max` (default 4) and optional `pillClass` prop. Applied it across all 5 tag-rendering sites: Home (featured + recent cards), SearchResults, SubTopicPage, CreatorDetail. Added `pill--overflow` styling for the "+N more" badge. In TopicsBrowse, subtopics with `technique_count === 0` now render as non-clickable spans with a "Coming soon" pill instead of dead-end links. Added `topic-subtopic--empty` and `pill--coming-soon` CSS. + +## Verification + +TypeScript type check (`npx tsc --noEmit`) passes with zero errors. Production build (`npm run build`) succeeds — 51 modules transformed, built in 777ms. All key source artifacts confirmed: collapsed init in TopicsBrowse, grid-template-rows CSS rules, badge--cat classes in CreatorDetail, TagList component with overflow logic, Coming soon badge rendering for empty subtopics, TagList imported in all 4 consumer pages. + +## Requirements Advanced + +- R019 — Topics page initializes collapsed, expand/collapse uses smooth 300ms CSS grid animation +- R026 — Creator stats rendered as colored badge pills using catSlug-based classes +- R027 — TagList component caps visible tags at 4 with +N more overflow badge, applied across all 5 sites +- R028 — Empty subtopics render as non-clickable spans with Coming soon pill badge + +## Requirements Validated + +None. + +## New Requirements Surfaced + +None. + +## Requirements Invalidated or Re-scoped + +None. + +## Deviations + +T03 added a `pillClass` prop to TagList (not in plan) to preserve the existing `pill--tag` class used in Home.tsx. Minor additive change, no impact. + +## Known Limitations + +None. + +## Follow-ups + +None. + +## Files Created/Modified + +- `frontend/src/components/TagList.tsx` — New shared component — renders up to max tags with +N overflow pill +- `frontend/src/pages/TopicsBrowse.tsx` — Collapsed-by-default init, grid animation wrapper, empty subtopic Coming soon badge +- `frontend/src/pages/CreatorDetail.tsx` — Topic stats as colored badge pills in flex container, TagList for technique tags +- `frontend/src/pages/Home.tsx` — Replaced inline tag maps with TagList component (featured + recent cards) +- `frontend/src/pages/SearchResults.tsx` — Replaced inline tag map with TagList component +- `frontend/src/pages/SubTopicPage.tsx` — Replaced inline tag map with TagList component +- `frontend/src/App.css` — Added grid animation rules, topic-pills flex container, pill--overflow, pill--coming-soon, topic-subtopic--empty styles diff --git a/.gsd/milestones/M011/slices/S02/S02-UAT.md b/.gsd/milestones/M011/slices/S02/S02-UAT.md new file mode 100644 index 0000000..7d20c32 --- /dev/null +++ b/.gsd/milestones/M011/slices/S02/S02-UAT.md @@ -0,0 +1,72 @@ +# S02: Topics, Creator Stats & Card Polish — UAT + +**Milestone:** M011 +**Written:** 2026-03-31T08:36:24.689Z + +# S02 UAT — Topics, Creator Stats & Card Polish + +## Preconditions +- Chrysopedia frontend running (http://ub01:8096 or local dev server) +- At least one creator with multiple topic categories in the database +- At least one technique with more than 4 topic tags +- At least one subtopic with 0 techniques + +--- + +## Test 1: Topics Page Loads Collapsed + +1. Navigate to `/topics` +2. **Expected:** All 7 category cards are visible but no subtopic lists are shown +3. Click any category card header +4. **Expected:** Subtopics expand with a smooth ~300ms slide animation (not an instant pop) +5. Click the same category header again +6. **Expected:** Subtopics collapse with the same smooth animation +7. Click two different categories in sequence +8. **Expected:** Both expand independently; expanding one does not collapse the other + +## Test 2: Empty Subtopic Coming Soon Badge + +1. Navigate to `/topics` +2. Expand a category that contains a subtopic with 0 techniques +3. **Expected:** The empty subtopic shows its name with a "Coming soon" pill badge +4. **Expected:** The empty subtopic text is dimmed (opacity ~0.5) and the cursor is `default` (not pointer) +5. Click the empty subtopic +6. **Expected:** Nothing happens — it is not a link, no navigation occurs +7. Hover over the empty subtopic +8. **Expected:** No background highlight (unlike active subtopics) + +## Test 3: Creator Stats Colored Pills + +1. Navigate to `/creators` and click a creator with multiple topic categories +2. **Expected:** The stats line shows topic categories as colored pill badges (not plain text with dots) +3. **Expected:** Each pill has a distinct color corresponding to its topic category (same colors as Topics page category borders) +4. **Expected:** Pills wrap naturally if the container is narrow (flex-wrap behavior) + +## Test 4: Tag Overflow on Cards + +1. Navigate to the homepage (`/`) +2. Find a technique card (featured or recent) that has more than 4 tags in the database +3. **Expected:** Exactly 4 tag pills are visible, followed by a "+N more" pill in muted/italic style +4. Navigate to `/search` and search for a term that returns a technique with >4 tags +5. **Expected:** Same overflow behavior — 4 tags + "+N more" +6. Navigate to a subtopic page with a technique that has >4 tags +7. **Expected:** Same overflow behavior +8. Navigate to a creator detail page with a technique that has >4 tags +9. **Expected:** Same overflow behavior + +## Test 5: Tag Overflow Edge Cases + +1. Find a technique card with exactly 4 tags +2. **Expected:** All 4 tags shown, no "+N more" pill +3. Find a technique card with fewer than 4 tags (e.g., 2) +4. **Expected:** All tags shown, no "+N more" pill +5. Find a technique card with exactly 5 tags +6. **Expected:** 4 tags shown + "+1 more" pill + +## Test 6: Collapse Animation is CSS-Based (Not Conditional Render) + +1. Navigate to `/topics` +2. Open browser DevTools → Elements panel +3. Expand a category +4. **Expected:** The subtopics wrapper div is always present in the DOM (not conditionally rendered). Its `data-expanded` attribute toggles between true/absent. The `grid-template-rows` CSS property transitions between `0fr` and `1fr`. + diff --git a/.gsd/milestones/M011/slices/S02/tasks/T03-VERIFY.json b/.gsd/milestones/M011/slices/S02/tasks/T03-VERIFY.json new file mode 100644 index 0000000..ac1debc --- /dev/null +++ b/.gsd/milestones/M011/slices/S02/tasks/T03-VERIFY.json @@ -0,0 +1,30 @@ +{ + "schemaVersion": 1, + "taskId": "T03", + "unitId": "M011/S02/T03", + "timestamp": 1774946107163, + "passed": false, + "discoverySource": "task-plan", + "checks": [ + { + "command": "cd frontend", + "exitCode": 0, + "durationMs": 7, + "verdict": "pass" + }, + { + "command": "npx tsc --noEmit", + "exitCode": 1, + "durationMs": 821, + "verdict": "fail" + }, + { + "command": "npm run build", + "exitCode": 254, + "durationMs": 84, + "verdict": "fail" + } + ], + "retryAttempt": 1, + "maxRetries": 2 +} diff --git a/.gsd/milestones/M011/slices/S03/S03-PLAN.md b/.gsd/milestones/M011/slices/S03/S03-PLAN.md index 98424ec..6c7094c 100644 --- a/.gsd/milestones/M011/slices/S03/S03-PLAN.md +++ b/.gsd/milestones/M011/slices/S03/S03-PLAN.md @@ -1,6 +1,60 @@ # S03: Global Search & Mobile Navigation -**Goal:** Make search accessible from any page and improve mobile navigation +**Goal:** Compact search bar in nav on all non-home pages with Cmd+K focus. Mobile viewport (<768px) shows hamburger menu with stacked nav links and generous touch targets. **Demo:** After this: Compact search bar in nav on all pages. Cmd+K focuses it. Mobile viewport shows hamburger menu. ## Tasks +- [x] **T01: Refactored SearchAutocomplete from heroSize boolean to variant string prop and wired compact nav search bar with Cmd+K shortcut into App.tsx header on all non-home routes** — Refactor SearchAutocomplete from heroSize boolean to variant string prop ('hero' | 'inline' | 'nav'), add nav-variant CSS (compact sizing, hidden submit button, high z-index dropdown), add globalShortcut prop for Cmd+K/Ctrl+K focus, wire nav search into App.tsx conditionally on non-home routes, update existing callers in Home.tsx and SearchResults.tsx. + +This task delivers R020 (Global Search in Navigation). + +## Steps + +1. In `SearchAutocomplete.tsx`: replace `heroSize?: boolean` with `variant?: 'hero' | 'inline' | 'nav'` (default `'inline'`). Update the className logic: `search-form--hero` when variant=hero, `search-form--inline` when variant=inline, `search-form--nav` when variant=nav. Same pattern for `search-input--*`. +2. When `variant === 'nav'`: hide the submit button (don't render ` + {variant !== 'nav' && ( + + )} + {variant === 'nav' && ( + ⌘K + )} {showDropdown && (showPopular || showSearch) && ( diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx index 319f3a7..ed8edea 100644 --- a/frontend/src/pages/Home.tsx +++ b/frontend/src/pages/Home.tsx @@ -104,7 +104,7 @@ export default function Home() {

navigate(`/search?q=${encodeURIComponent(q)}`)} /> diff --git a/frontend/src/pages/SearchResults.tsx b/frontend/src/pages/SearchResults.tsx index 27777d1..e2ad498 100644 --- a/frontend/src/pages/SearchResults.tsx +++ b/frontend/src/pages/SearchResults.tsx @@ -54,6 +54,7 @@ export default function SearchResults() {
{/* Inline search bar */} navigate(`/search?q=${encodeURIComponent(newQ)}`, { replace: true })