From 6cfb344e8e860e109c2d4ba26e75213e06b77fd0 Mon Sep 17 00:00:00 2001 From: kris1027 Date: Sun, 11 Oct 2026 10:03:56 +0200 Subject: [PATCH 01/25] docs(prompts): plan the four column grid, cyan tabs, footer icon key and skeletons Co-Authored-By: Claude Opus 5.5 --- prompts/grid-tabs-footer-legend.md | 90 ++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 prompts/grid-tabs-footer-legend.md diff --git a/prompts/grid-tabs-footer-legend.md b/prompts/grid-tabs-footer-legend.md new file mode 100644 index 0000000..e1ae323 --- /dev/null +++ b/prompts/grid-tabs-footer-legend.md @@ -0,0 +1,90 @@ +# Four column grid, cyan selected tabs, footer icon key, honest skeletons + +## Goal + +Four UI changes, settled with the user in a grilling session on 2026-10-11: + +1. Stop the layout jumping when a page finishes loading: every skeleton matches the footprint of what replaces it. +2. Regrid the poster grids to at most 4 columns, so the 20 cards a page holds fill 5 complete rows. +3. Light the selected navbar tab in the personal score cyan, as a glass fill. +4. Replace the per tab badge legend with one fixed icon key, centred at the top of the site footer on every route. + +Branch `feat/grid-tabs-footer-legend`. No spec: this plan records the decisions. + +## Inspected code + +- Grid: `components/poster-grid.tsx` (2/3/4/5/6 columns), `components/poster-card.tsx` (`POSTER_SIZES`), `app/shows/page.tsx` and `app/movies/page.tsx` (`PAGE_SIZE = 20`, `EAGER_POSTERS = 6`, skeletons), `components/library/library-grid.tsx` (`EAGER_POSTERS`), `components/show/season-grid.tsx` (also `PosterGrid`), `lib/tracking/library-lists.ts` (`LIBRARY_PAGE_SIZE = 20`) +- Skeletons: `components/library/library-panels.tsx` (`LibrarySkeleton`, always 20 cards), `components/search/search-results.tsx` (`SearchResultsSkeleton`), `components/skeleton.tsx`, `components/movie/movie-detail-skeleton.tsx`, `components/show/show-detail-skeleton.tsx`, `components/show/season-skeleton.tsx`, `components/catalog/detail-skeletons.tsx` +- Tabs: `components/layout/media-type-tabs.tsx`, `components/layout/library-nav.tsx` (bar and sheet variants), `glass-selected` in `app/globals.css`, `--color-score-personal: #50c6e5` +- Legend: `components/library/badge-legend.tsx`, its callers `show-library-tab.tsx` and `movie-library-tab.tsx`, `components/library/library-section.test.tsx` (legend cases), `components/tracking/tracking-icons.tsx` (no `"use client"`, so usable from the server footer), `components/AGENTS.md` (badge legend line), `app/showcase/page.tsx` +- Footer: `components/layout/site-footer.tsx`, `site-footer.test.tsx`, `app/layout.tsx`, `app/layout-purity.test.ts` (the footer must stay a request free Server Component) + +## Skills used + +`next-dev-loop` to measure skeletons against loaded pages and verify the result in the running app. No database change, so no Supabase skill. + +## Decisions + +1. **Page size stays 20 everywhere.** `/shows`, `/movies` and `/search` keep one TMDB page per BeStats page; the library pages keep `LIBRARY_PAGE_SIZE = 20`. No TMDB re-paging. +2. **Grid columns: 2 below `md`, 4 from `md` (768px).** 20 fills every row at both counts (10 × 2, 5 × 4); 3 columns would leave a partial row, so there is no 3 column step. Applies to every `PosterGrid`, including the seasons on `/shows/{id}`. +3. **Grid width capped at 1200px and centred** on every page (`mx-auto max-w-[1200px]` on `PosterGrid`; changed from left aligned at the owner's request after review), so a 4 column poster stays near 285px wide and a 5 row page stays a sensible length. Pagination and other content beside the grid keep their current width. +4. **Image sizes follow the grid.** `POSTER_SIZES` becomes `(min-width: 1200px) 300px, (min-width: 768px) 25vw, 50vw`; every `EAGER_POSTERS` becomes 4 (one full row at the widest grid). +5. **Skeletons.** + - `LibrarySkeleton` draws one row (4 cards): the real count is unknown until the read, and one row is the smallest honest guess. + - The `/shows`, `/movies` and search skeletons add a placeholder for the pagination row at its real height. + - Every other route (detail, season, search filters, auth, account) is measured in the browser: skeleton height against loaded height at 390px and 1440px. Any mismatch over a few pixels is fixed in its skeleton; the plan's report lists each route measured and what changed. +6. **Selected tab: cyan glass.** A new `glass-selected-personal` utility in `app/globals.css`, the same layer stack as `glass-selected` (fill, plate, rim on the 1.5px border) with the gradient tinted from `--color-score-personal`, translucent so the glass reads. Text on it is near black (a token, e.g. `text-on-personal`) so it meets WCAG AA 4.5:1; the exact tint is checked with a contrast measurement, not assumed. Used by the selected tab in `MediaTypeTabsView` and in both `LibraryNav` variants. Unselected tabs do not change. +7. **Footer icon key.** + - `BadgeLegend` and its per tab entries are deleted, with their test cases; the two library tabs no longer render it. + - A new `IconKey` Server Component (`components/layout/icon-key.tsx`) renders one fixed list, always all entries, in this order: TMDB rating, Your score, Plan, Planned, Next episode, Mark watched, Release date. Same icons and colours as the cards (`StarIcon` in `rating-tmdb` / `score-personal`, `PlanIcon`, `PlannedIcon`, `NextEpisodeIcon`, `WatchedIcon` outline, `CalendarIcon`), `size-4`, `text-text-legend`. + - It is the footer's first row, on every route, inside the footer's 1600px container: a hairline (`border-t border-border`) above, centred `flex-wrap` with the current legend gaps, wrapping centred on mobile. The existing attribution and legal row sits below it unchanged. + - `