UXS-015: Operator Shows Library (shows-first browse)¶
- Status: Active
- Surface:
web/gi-kg-viewer—librarytab, Shows mode - Inherits: UXS-001 (operator shared design system — tokens, type, density), UXS-003 (episode-first Library — episode-row contract)
- PRD/RFC: PRD-044 · RFC-104
This UXS owns the static visual contract (layout grid, card appearance, tokens, states, a11y). Behavioral rules (fetch strategy, state machine, cross-link) live in RFC-104.
Scope¶
Two surfaces:
- Shows grid — the corpus's shows as cover cards, in the Library tab's Shows mode.
- Show detail — one show's header + its episode list, in the right subject rail
(
ShowRailPanel,'show'subject kind). Plus the mode toggle (Shows | Episodes) at the tab head. ReusesPodcastCoverand the UXS-003 episode-row; introduces no new tokens.
Amendment (operator feedback, 2026-07-09). Show detail now opens in the right subject rail, not in-panel — a card-click calls
subject.focusShow; the rail header mirrors the episode rail (compact cover + title + "N episodes · RSS" meta), then the episode list. Clicking an episode in the show rail opens it in the same rail with a ‹ Back to the show (subject history). The "Show detail (ShowDetailView)" section below described the original in-panel layout; the tokens/rows carry over toShowRailPanel, but the surface is the rail, and the header follows theEpisodeDetailPanelheader exactly.Signals band (Phase 2, 2026-07-09). Between the header and the episode list, a compact
show-rail-signalsblock surfaces the show's Top topics and Key people as count-labelled chips (show-rail-topic/show-rail-person), fromGET /api/corpus/feed-signals(Topic/Person nodes counted across the show's episode KGs, ranked by episode count; diarization placeholders filtered). Topic chips usetext-topic, people chipstext-person(rounded-fullbg-overlay,text-[11px], count intext-muted). Clicking a chip opens that node in the same rail (subject.focusTopic/focusPerson, Back returns to the show). The whole band hides when the feed has no topics or people. Cross-show overlaps remain deferred (need an all-feeds pass).
Mode toggle¶
- Segmented control at the Library tab head, left-aligned, above the content:
[ Shows | Episodes ]. - Tokens: inherits the shared segmented-control style (
bg-overlay, activebg-overlay-2 text-primary, inactivetext-muted),text-xs font-semibold,rounded,h-7. Matches the Digest/Library chip bar. data-testid="library-mode-shows"/library-mode-episodes;aria-pressedon the active segment.
Shows grid (ShowsView)¶
Layout
- Responsive grid:
grid gap-3atgrid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5(dense, operator-tool density per UXS-001 — smaller than the consumer's editorial grid). - Show card (
shows-card-{feed_id}): vertical stack — PodcastCoveratsize-class="h-full w-full aspect-square",rounded-lg,object-cover,bg-elevatedplaceholder; initials fallback when no art.- Title:
text-sm font-semibold leading-snug line-clamp-2 text-surface-foreground. - Meta:
text-[11px] text-muted— "N episodes" (episode_count). - Optional description:
text-[11px] text-muted line-clamp-2(only if present; never pushes layout). - Card container:
rounded-lg border border-default bg-overlay p-2 hover:bg-overlay-2,role="button" tabindex="0", visiblefocus-visible:ring-2 ring-primary(parity with library rows).
States
- Loading: skeleton cards (
animate-pulse bg-overlaysquares) or the shared spinner — match UXS-003. - Error:
text-xs text-dangerinline message + retry affordance (shared pattern). - Empty (0 feeds): centered
text-sm text-muted— "No shows in this corpus." (shows-grid-empty).
Show detail (ShowDetailView)¶
Header (show-detail)
- Row: large
PodcastCoverh-20 w-20 sm:h-24 sm:w-24 rounded-xl object-cover shrink-0+ a text column. - Title:
text-xl font-extrabold leading-tight tracking-tight text-surface-foreground. - Sub-line:
text-xs text-muted— "N episodes" · optional RSS link (rss_url,text-primary hover:underline, opens in new tab,rel="noopener"). - Description:
text-sm text-muted leading-relaxed,line-clamp-3; when longer than the clamp, a "Show more/less" toggle (text-xs text-primary) mirroring the consumer PodcastView 180-char clamp. - Back to shows: a top-left
‹ Showsbutton (show-detail-back),text-xs text-muted hover:text-surface-foreground, keyboard-focusable; returns to the grid (replace-in-panel).
Episode list (show-detail-episode-{i})
- Reuses the UXS-003 episode row verbatim (cover
h-9 w-9, recency dot, title, publish date, summary line, topic pills, GI/KG badges), same classes +data-library-episode-rowsemantics, so a Show-detail episode is visually and behaviorally identical to a flat-Library episode. - Newest-first; "Load more" button (
show-detail-load-more) whennext_cursorpresent. - Empty (show has 0 episodes):
text-xs text-muted— "No episodes." (show-detail-empty).
Cross-surface flow (visual)¶
- Grid card → detail: in-panel replace (no modal, no new backdrop — one-surface rule, operator memory / UXS-014). Back returns to grid at prior scroll.
- Episode row → graph: click routes through the existing
focusEpisodepath; the Library tab yields to the Graph tab (existing transition), so no new visual affordance is introduced here.
Accessibility¶
- Grid + rows:
role="button",tabindex="0", Enter/Space activate, visible focus ring; cardaria-label="{title}, {episode_count} episodes". - Cover images:
alt="Cover for {title}"; decorative fallback initials arearia-hiddenwith the label carried by the cardaria-label. - Mode toggle:
aria-pressed; arrow-key movement optional (parity with existing segmented controls). - Contrast: all text meets UXS-001 AA targets on
bg-overlay/bg-elevated.
Review checklist¶
- [ ] Uses only UXS-001 tokens; no bespoke colors/shadows.
- [ ]
PodcastCoverreused (no new image component); graceful art fallback, never a broken<img>. - [ ] Episode row is byte-for-byte the UXS-003 row (classes + testids), not a re-styled copy.
- [ ] Loading / error / empty present on grid and detail.
- [ ] Replace-in-panel (no modal / second backdrop) for grid ↔ detail.
- [ ] Focus rings + keyboard activation on every card/row/toggle.
- [ ]
data-testids match RFC-104 §Testing (shows-grid,shows-card-{id},show-detail,show-detail-back,show-detail-episode-{i},library-mode-shows|episodes).
Related¶
UXS-001 (tokens), UXS-003 (episode row), UXS-011/012 (consumer parallel for reference only — the operator uses UXS-001 density, not the consumer editorial system).