Files
the-daily-epub/docs/plans/briefs/ui-pass/handoff-rating.md
T
thalladaandClaude Fable 5.1 4248e48a8b UI pass: rating widget, issue section headers, ratings verdict column
Finishes the Codex partial (0cdc302) after seeing it rendered.

Rating widget
- Inactive segments go text-ink-2 (ink on hover) so the group recedes
  beside serif body copy; the per-verdict tint stays the only colour.
- Hit area: the ::after overlay is positioned against the padding box,
  so -inset-y-1/-1.5 only gave 37/41 px. Now -inset-y-1.5/-2 -> 41 px
  desktop, 45 px phone.
- focus-visible hugs the segment (z-20, ring-offset-0); the inherited
  ring-offset-2 painted a detached box over the neighbouring buttons.
- The note field is a quiet bottom-hairline input everywhere (rule at
  rest, ink on focus) instead of a box on reader pages and an invisible
  border in tables.

Issue section headers
- The Brief, every section name and Colophon now use one serif
  uppercase, letter-spaced mark on a double rule. A sentence-case
  text-2xl header was screenshotted first and rejected: it reads as
  another headline, which is the problem being fixed.

Ratings table verdict column
- New .rating-cell block: a one-column 15rem grid, segmented group with
  clear/cleared flush right, note field beneath on the same width, group
  top-aligned with the article title. 13rem cannot hold the labels plus
  the wider "cleared" state, so rows wrapped and overlapped the Article
  column.
- Drop the dead CurrentRow::verdict field left over from removing the
  badge (clippy -D warnings).

Also: reader-page on world/behind so the text-wrap: balance polish item
covers those chapters.

Verified on the dev server at 127.0.0.1:3602 in light and dark, desktop
and 390 px: rating clicks toggle .active/aria-pressed with no navigation
and no console output; note text round-trips; cargo fmt, clippy
--all-targets -D warnings, cargo test (445 lib + integration) and
npm run css:check are clean. See docs/plans/briefs/ui-pass/handoff-rating.md.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MD4VWGq6mGcd8Bg67qyx9k
2026-09-04 17:42:21 +00:00

9.5 KiB

Task 2 handoff — rating widget, issue section headers, ratings-table verdict column

Branch ui-rating in /home/thallada/workspace/the-daily-epub-ui2, dev port 3602. A previous agent (Codex) landed a partial pass as 0cdc302 before hitting a quota limit and never saw the rendered result; main was merged in afterwards (bcbe158). This pass kept the parts of that work that held up on screen, fixed the parts that did not, and finished the remaining items.

What changed

src/web/tailwind.css

  • .reader-section-heading — replaced Codex's sentence-case text-xl serif heading with a serif uppercase, letter-spaced section mark on a double rule: border-t-[3px] border-double border-rule-strong pt-3 font-serif text-[1.05rem] font-semibold uppercase leading-[1.2] tracking-[0.15em]. text-wrap: balance stays. (Rationale under Design decisions.)
  • Rating buttons — inactive labels are now text-ink-2 (were text-ink) and go to text-ink on hover, so the group recedes next to serif body copy.
  • Rating button hit area — the ::after overlay went from -inset-y-1 / -inset-y-1.5 (phone) to -inset-y-1.5 / -inset-y-2. The pseudo is positioned against the padding box, so the old values gave 37 px desktop / 41 px phone; the new values measure 41 px and 45 px (verified with elementFromPoint), clearing the ≥ 40 px / ≥ 44 px rules in the shared brief.
  • Rating focus ring.rating button[data-label]:focus-visible { z-20 ring-offset-0 }. In a segmented group the inherited ring-offset-2 painted a detached red box over the neighbouring buttons; hugging the segment reads as one control.
  • .rating-note input — now a quiet bottom-hairline field everywhere (rounded-none border-x-0 border-t-0 border-b border-rule bg-transparent px-0 py-1 shadow-none focus:border-ink) instead of a boxed input on reader/dashboard pages and a transparent bottom border in tables.
  • Table/list overridestd .rating / .picks .rating lost the max-w-[15rem] cap (the cell now sets its own width) and gained gap-y-1; table buttons use px-1.5 so the group fits; .picks .rating gets mt-2 so the widget is not welded to the pick title.
  • New .rating-cell block — the ratings-table verdict cell is a one-column grid, w-[15rem], gap-y-1.5: row 1 is the segmented group with clear pushed to the right edge (ml-auto), row 2 is the note field spanning the same width. min-h-0 on .rating-actions inside the cell top-aligns the group with the row's article title (measured: 1 px apart).

src/web/dashboard/ratings.rs

  • Removed the now-dead CurrentRow::verdict field and its assignment (Codex dropped {{ row.verdict }} from the template but left the field, which fails clippy -D warnings). badge stays — it is still read by the "no embedding" / "—" branch in the template.

src/web/templates/world.html, src/web/templates/behind.html

  • Added reader-page to the <article> so the polish item's text-wrap: balance on reader headlines covers the World Briefing and Behind the paper chapters too. (Deviation — see below.)

Kept from Codex's partial commit (verified on screen, unchanged)

_rating_widget.html (.rating-actions wrapper, clear/cleared label spans, placeholder="Add a note…"), article.html / issue_full.html / issue_public.html (reader-page, reader-section-heading, index-summary, mt-14 section spacing, colophon and The Brief on the same heading system), dashboard/ratings.html (badge dropped from the verdict cell, rating-cell class), the per-verdict tints, .prose-body img outline, .prose-body p / .index-summary text-wrap: pretty, and active:scale-[0.96] with named transition properties on .btn / .btn-primary / .btn-danger.

src/web/static/app.css

Rebuilt from tailwind.css (Tailwind 4.3.3).

Design decisions

  • Section headers: uppercase mark, not a bigger headline. The brief asked for both options to be screenshotted. A sentence-case serif text-2xl header (after-rating/sec/C.png) is the same size as the article headlines directly beneath it and reads as another story, which is the exact failure being fixed. The uppercase, letter-spaced serif mark on a double rule (after-rating/sec/B.png, B2-*.png) is unmistakably a section marker at a glance and never competes with a headline — the newspaper convention the rest of the design language is borrowing. It is also visibly not the old 0.72 rem sans eyebrow: serif, ink-coloured, ~50 % larger, and sitting on the same double rule as the masthead. No extra eyebrow was added; the header is already uppercase, so a second uppercase line above it would be noise.
  • clear stays invisible, not [hidden]. It keeps its layout box, so choosing a verdict does not shift the group sideways. .rating:has(button .active) button.clear reveals it; the cleared state swaps the label to a muted "cleared" with no underline, which doubles as the brief's "muted cleared state in the group" for the ratings table.
  • Verdict cell is w-[15rem], not w-[13rem]. 13 rem is 208 px; the three labels plus clear measure ~219 px at text-xs/px-1.5, and the wider "cleared" label needs ~237 px. 15 rem (240 px) fits both states with the clear/cleared link flush right in each, so rows stay aligned whichever state they are in. Shrinking the type to ~0.7 rem would have hit 13 rem but is too small for a dense table that is otherwise 14 px.
  • Note field shows a faint rule at rest. The brief said "bottom-hairline only, no box" and "only shows a border on focus"; Codex read that as a transparent border, which left "Add a note…" floating with no affordance and no shared baseline across rows. A --rule hairline that goes to --ink on focus keeps the cell calm, gives the unit a base line, and still reads as quiet.
  • .prose-body h2/h3 left alone. Checked against 1.0625 rem body copy (prose-heading-light.png): text-2xl semibold over a hairline is already a clear heading. The brief said to adjust only if weak.

Deviations

  • world.html and behind.html are not in this task's file list, but the polish item asks for text-wrap: balance on reader-page headlines and those two are reader pages. The change is one class per file and touches nothing else.
  • Verdict cell width 15 rem rather than the brief's example 13 rem (reason above).
  • Section header is text-[1.05rem] uppercase rather than text-2xl/text-xl sentence case (reason above).

Verification

Dev server on 127.0.0.1:3602 against this worktree's own copy of the dev DB (dev/config.toml was repointed from the main checkout's dev/ to the-daily-epub-ui2/dev/ so rating clicks could not touch the shared database; dev/ is git-ignored).

  • cargo fmt -- --check — clean.
  • cargo clippy --all-targets -- -D warnings — clean.
  • cargo test --lib web:: — 84 passed, 0 failed.
  • cargo test — 445 lib tests + all integration tests passed, 0 failed. (One earlier run had a single flaky failure in config::tests::registry_validation_rejects_bad_roles_kinds_and_efforts; it passes in isolation and on re-run, and is unrelated to this change — the host was building for three agents at once.)
  • npm run css:check — no diff (Tailwind 4.3.3 from the adjacent checkout's node_modules/.bin via PATH; only read, never modified).
  • Live JS handler (interact.mjs): clicked loved → good → not-for-me → clear → loved on /issues/2026-09-02/articles/4. .active and aria-pressed follow every click, the tint switches, 0 page navigations, console clean (no errors or warnings). Also typed into the note field and confirmed it round-trips into the ratings table's Note column.
  • Hit areas measured with elementFromPoint: 41 px desktop, 45 px phone.
  • Top alignment measured: verdict group top is 1 px from the article title top.

Screenshots

Under /tmp/claude-1000/-home-thallada-workspace-the-daily-epub/553c7d77-d8f4-44f8-a379-73cedea23814/scratchpad/after-rating/:

  • sec/B.png, sec/C.png, sec/B2-brief.png, sec/B2-colophon.png — the section-header variant comparison behind the decision above.
  • wip/* — Codex's partial state before this pass (the overflowing verdict cell is wip/ratings-cell-light.png).
  • final/ — the shipped result:
    • / index card + section: index-card-light, index-card-mobile, index-card-mobile-dark, index-section-light, index-section-dark, index-section-mobile
    • front-page heading system: brief-light, brief-dark, colophon-light, colophon-dark
    • anonymous issue page: public-issue-light, public-issue-dark
    • chapter footer: article-footer-light, article-footer-mobile
    • ratings table: ratings-light, ratings-dark, ratings-mobile, ratings-mobile-dark, ratings-cleared-light, ratings-cleared-dark
    • dashboard article widget: dash-article-light, dash-article-dark, dash-article-mobile
    • states: rating-focus, rating-hover, overview-picks
    • polish checks: prose-heading-light, prose-img-light, prose-img-dark

Left open

  • Nothing from this brief. The dev server is left running on 3602 against the-daily-epub-ui2/dev/daily-epub.db, which now holds 5 current verdicts (loved / good / not-for-me / good / loved, one with a note) plus the extra rating events those clicks appended — rating_events is append-only, so the history is longer than the seeded baseline.