Show article read status on grid cards and unify card overlays

Merged
FR
2h ago
+605-137

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) and CardPlayIndicator, 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, and card-frame.ts mirrors 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: ArticleReadStatusBadge RTL test (read/unread branch + accessible label); Grid/CardFit stories gain a long-title article (title inset) and a webpage (no inset) case so the estimate can't drift; new Grid/CardOverlays story renders every overlay
  • User-facing change → demo video attached (record-pr-demo skill); refactor through a user flow → parity run, or N/A: before/after below
  • If this fixes a recurring defect, considered a guardrail via the learn skill, 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 for Grid/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 for hover: 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_at then 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.