Skip to content

Colour the read-time chip and measure only visible text - #22

Merged
sjmiller609 merged 1 commit into
hypeship/viewer-read-timefrom
hypeship/read-time-chip-visible
Jul 31, 2026
Merged

Colour the read-time chip and measure only visible text#22
sjmiller609 merged 1 commit into
hypeship/viewer-read-timefrom
hypeship/read-time-chip-visible

Conversation

@sjmiller609

@sjmiller609 sjmiller609 commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Stacked on #21 (base is hypeship/viewer-read-time) — review that one first.

1. The chip

The read time becomes text in a filled box whose weight escalates with the estimate:

bucket fill text
< 5 min kernel-green #81b300 charcoal #212225
5–15 min beige-muted #e1dccf charcoal
> 15 min charcoal #212225 beige #f2f0e7

On brand compliance: the Kernel palette has no status colours — no red, no amber — and gold is explicitly never a background, so a literal ok/warn/error traffic light can't be built from approved tokens. Every value above is from the palette, and the escalation is carried by contrast weight instead of hue. Geometry mirrors the website's Tag (4px radius, hairline border, 4/8px padding). Contrast: beige-muted+charcoal and charcoal+beige are AAA; kernel-green+charcoal is AA, which the brand guide allows for short dense glyphs like this one.

On a dark bar the two darker borders switch to a light tone (paletteVars) — otherwise the grey hairline disappears and the charcoal chip merges into the bar. Fills are literals, not sampled-palette-derived, so the chip holds its contrast whatever colours the doc uses.

Colour isn't the only signal: the tooltip still spells out the estimate, and past 15 minutes it says why the chip is heavy.

Thresholds live next to the rate constants in lib/docs/reading-time.ts. Exactly 15 minutes is warn; only 16+ is over.

2. Visible-text measurement

The server estimate counts every word in the stored HTML — it can't see that three of four tab panels are hidden by a class defined in a <style> block. The overlay can, so it walks the laid-out DOM, prunes hidden subtrees (display:none, visibility, opacity:0, hidden, aria-hidden, closed <details> beyond its summary, <dialog> without open) plus our own injected chips, counts what survives with the same wpm/CJK rules, and posts jh:readtime.

The shell seeds readMinutes from the SSR value and swaps in the overlay's — so the chip paints immediately and a doc whose overlay never runs keeps the full-text number. Measurement runs on load, at 400ms with the theme sample, and once more at 1500ms; each report supersedes the last, which is what catches a tab UI that only hides its panels after script runs.

The rate constants are duplicated in the overlay (stringified browser JS, can't import server code) and marked as needing to stay in sync — same split as the reaction signature.

Assumption worth confirming: the chip's colour follows the number shown, i.e. the visible-text estimate. So a 40-minute doc with most content behind tabs shows a green 4 min read. The alternative is colouring by total text while displaying visible-only; say the word and I'll flip it.

Testing

  • npm test — 171 passing, including threshold boundaries at 4/5/15/16 and the tooltip copy.
  • npm run build and tsc --noEmit clean. /d/[slug] first-load JS unchanged at 165 kB.
  • Verified in a browser against seeded fixtures, reading the settled DOM over CDP: 600/1600/4000-word docs → 4 min green, 9 min beige, 21 min charcoal; a CSS-tabbed doc 23 → 4; a script-tabbed doc that hides panels 1s after load 23 → 4; a doc with 4000 words in a closed <details> 22 → 3. Chips checked on light and dark docs.
  • The overlay has no unit tests (stringified browser JS), so its half is fixture-verified by hand, not covered by the suite.

Note

Low Risk
Viewer chrome and postMessage-only refinement; no auth or persistence changes. Main caveat is keeping overlay WPM/CJK constants in sync with reading-time.ts.

Overview
The viewer chrome read-time chip now uses Kernel palette fills that escalate with length (ok < 5 min, warn 5–15, over 16+), with data-level styling, dark-bar border tweaks, and tooltips via readTimeLevel / readTimeTitle in lib/docs/reading-time.ts.

Read minutes still SSR from full stored HTML, but CommentsShell keeps them in state and replaces them when the sandbox overlay posts jh:readtime. The overlay walks the laid-out DOM, skips hidden/closed content and injected UI, counts with the same WPM/CJK rules (constants duplicated in overlay.ts), and reports on load, 400ms, and 1500ms so late tab UIs converge.

Reviewed by Cursor Bugbot for commit b6908c7. Bugbot is set up for automated code reviews on this repo. Configure here.

The bar's read time becomes a filled chip whose weight escalates with the
estimate — kernel-green under 5 minutes, beige-muted to 15, charcoal past that.
The Kernel palette has no red or amber and gold is never a background, so the
escalation is contrast weight rather than hue; the tooltip carries the same
meaning in words.

The overlay also re-measures the text that is actually visible once the doc has
settled (hidden tab panels, closed <details>, unopened dialogs) and posts
jh:readtime. The SSR full-text estimate still paints first, so a doc whose
overlay never runs is unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 31, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
justhtml Ready Ready Preview Jul 31, 2026 3:34pm

@sjmiller609
sjmiller609 marked this pull request as ready for review July 31, 2026 15:36
@sjmiller609
sjmiller609 merged commit b5e5311 into hypeship/viewer-read-time Jul 31, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant