Show article read status on grid cards and unify card overlays
Merged
FR
2h ago+605+605+012345678901234567890123456789-137-137-012345678901234567890123456789
What & why
Grid cards didn't show whether an article had been read, and the overlays cards draw differed by kind: YouTube/Vimeo badges were brand-coloured while X/Instagram were translucent black, the book status badge was larger, product price and document page-count tags were fixed px (not scaling with grid density) with a different radius and opacity from the GIF/duration tags, and the tweet play button was fixed px while the video one scaled.
- Shared overlay primitives (
components/ui/card-overlay.tsx):CardCornerBadge(top right, optional hover label),CardTag(bottom corners) andCardPlayIndicator, with one surface (bg-black/60+ blur), one inset and em sizing. Tweets, Instagram, video, book status, product price and document page count all use them. Text-only tweets pad the author row so a long name truncates before the X badge. - Article read status: owners see a dot (unread) / check (read) corner badge on article cards, with "Unread" / "Read" on hover, matching the book status badge. It's owner-only, like the detail view's read toggle. Webpages have no read status, so no badge.
- Layout: coverless article titles reserve room for the badge (
ARTICLE_TITLE_BADGE_INSET_EM). The frame estimate can't tell who's viewing, so every article reserves it, andcard-frame.tsmirrors it so reserved heights stay right.
Checklist
- User-facing change → added/updated a PostHog event (and a funnel/insight for a new flow), or N/A: N/A, display only; no new action (marking read is the existing detail-view toggle)
- User-facing or service change → docs updated (README features/roadmap/services,
.env.example, in-app help,CONTRIBUTING.md, onboarding checklist), or N/A: N/A, no help page covers grid card badges or article read status today - New error paths report via
captureServerException/ the error boundary, or N/A: N/A, no new error paths - New behavior is covered by tests, or N/A:
ArticleReadStatusBadgeRTL test (read/unread branch + accessible label);Grid/CardFitstories gain a long-title article (title inset) and a webpage (no inset) case so the estimate can't drift; newGrid/CardOverlaysstory renders every overlay - User-facing change → demo video attached (
record-pr-demoskill); refactor through a user flow → parity run, or N/A: before/after below - If this fixes a recurring defect, considered a guardrail via the
learnskill, or N/A: N/A, not a defect fix
Demo
Same seeded grid on both takes (3 articles, the third already read; a text tweet; a YouTube video; a book), pointer visiting each corner badge.
❌ Before
https://github.com/user-attachments/assets/5c55df46-e9de-46e3-8ece-2335eceb2458
✅ After
https://github.com/user-attachments/assets/c6254430-9365-4f01-a920-95af49395538
Notes
- Verified:
bun run check:fix, full unit suite (bun run test), and the storybook tests forGrid/CardFit,Grid/CardOverlays, item card states, tweet/article/book stories. Checked light, dark and a 0.75 grid font scale in the overlay story, plus the hover label (headless Chrome needs a hover-capable pointer emulated forhover:styles). - Not shown in the demo: toggling read in the detail view. On the demo stack (no Trigger.dev key) the item PATCH writes
read_atthen 500s on the room-sync trigger, which is an existing issue unrelated to this PR. - Design calls worth a look: video badges are no longer brand-coloured; the book badge is slightly smaller (matches the platform badges); the hover label overlaps the end of a title's first line while open, as the book badge already did.
- Commits: shared overlay primitives first (no new feature), then the article read badge.