Skip to content

Reader: the tail is the paragraph rhythm, not a 30px reserve - #93

Merged
lvwerra merged 1 commit into
mainfrom
fix/reader-trailing-gap
Aug 19, 2026
Merged

Reader: the tail is the paragraph rhythm, not a 30px reserve#93
lvwerra merged 1 commit into
mainfrom
fix/reader-trailing-gap

Conversation

@lvwerra

@lvwerra lvwerra commented Aug 19, 2026

Copy link
Copy Markdown
Member

it seems like there is an additional newline at the end of the reader view. just a bit too much whitespace imo. can you have an agent confirm and fix?

Confirmed. Measured before/after →

Scrolled to the end of a conversation that fills the pane, the space between the last line and the composer's border was 2.7× the spacing between the paragraphs above it:

last line → composer between paragraphs
desktop, before 32px 12px
phone, before 26px 12px
both, after 14px 12px

What it was — and the three things it wasn't

You were right to point at 633d492's class of cause, so I checked those first and each is ruled out by measurement, not by reasoning:

  • Not an uncollapsed margin. The last rendered block reports margin-bottom: 0px.cx-md > :last-child already gives it up.
  • Not a trailing empty node from the renderer. Nothing renders after the last paragraph at all: I enumerated every element whose box ends at or below it and the list is empty.
  • Not an element with no content. No spacer, no blank pending exchange, no working line with nothing to say — the shape that caused both earlier reports (Reader: the middle of a turn is markdown too #88's empty head row, Reader: the answer where it was said, one working line, a still turn column #85's blank meta half) is simply not present here.

It was .cxv-body's own bottom padding: 30px (24px on a phone), sitting in the same shorthand as the reading gutter:

.cxv-body { --cx-gutter: 14px; … padding: 4px var(--cx-gutter) 30px; }

A number from when the reader's bottom edge was the pane's, before a composer sat under it with a border 30px away. So a margin fix would have been the wrong shape of fix — but so would trimming it by eye.

The fix, and where the number comes from

--cx-tail: 12pxthe same 12px that separates two paragraphs (.markdown p { margin: 0 0 12px }). The end of the flow is then spaced like the flow: 14px in total on both viewports, the tail plus .cx's own 2px.

The phone rule restated the whole shorthand in order to narrow the gutter, and carried a different bottom (24px) as a side effect. With the tail a variable it has nothing left to restate — it narrows --cx-gutter and nothing else, which is the same drift this file already learned about with the prompt band's bleed in #71.

The two things that must not break

Pinned

In web/test/pendingExchange.test.mjs, where the reader's other CSS invariants live:

  • the tail comes from --cx-tail, not a number in the shorthand;
  • its value equals .markdown p's bottom margin — the relationship, not the coincidence, so the two cannot drift apart silently;
  • the phone rule restates no padding;
  • and the last rendered block still gives up its own margin.

Mutation-checked: restoring the 30px tail fails two of those, restating the phone padding fails the third.

Suites

Web green (143 checks), typecheck clean. server/test/archive.test.mjs and resize.test.mjs each failed in a full run and behave identically on pristine main under the same load: archive passes alone on both trees, and resize fails alone on both (2 failures, same assertions, with five other agents' servers running). Neither is related to this change, which is CSS in the reader.

🤖 Generated with Claude Code

> it seems like there is an additional newline at the end of the reader view.
> just a bit too much whitespace imo.

Confirmed, and measured against the rhythm above it rather than adjusted until
it looked nicer. Scrolled to the end of a conversation that fills the pane, the
space between the last line and the composer's border was 32px on desktop and
26px on a phone, while the paragraphs above it are 12px apart — 2.7× the
spacing, about one blank line.

Checked the causes that the mirror-image bug at the top of an expanded note
turned out to have, and it is none of them:

- not an uncollapsed margin: the last rendered block reports
  `margin-bottom: 0px`, which `.cx-md > :last-child` already takes care of;
- not a trailing empty node from the renderer: nothing renders after the last
  paragraph at all — the tail holds zero elements;
- not an element with no content — no spacer, no blank pending exchange, no
  working line with nothing to say, which was the shape of both earlier reports.

It was `.cxv-body`'s own bottom padding: 30px, written into the same shorthand
as the reading gutter, from when the reader's bottom edge was the pane's rather
than a border 30px away. It is `--cx-tail: 12px` now — the same 12px that
separates two paragraphs — so both viewports land on 14px (the tail plus `.cx`'s
own 2px) and the end of the flow is spaced like the flow.

The phone rule restated the whole shorthand to change the gutter, and carried a
different bottom (24px) as a result. With the tail a variable it has nothing left
to restate: it narrows `--cx-gutter` and nothing else, which is the same drift
this file already learned about with the band's bleed.

Neither of the two things that must not break did:

- it still opens on the last exchange with the newest content on screen —
  `atBottom: 0`, last exchange fully visible, answer on screen, at 1180px and at
  390pt; scrolling to the top and back returns to the end with the same 14px;
- #78's needs-input band composes with it. That band is not on main, so I merged
  it in locally to check: it sits last inside the scroller with margins 8px/2px,
  so the gap below it is 2 + 12 = 14px. It carries no reserve of its own, and
  before this change it had 32px under it too.

Pinned in `pendingExchange.test.mjs`: the tail comes from `--cx-tail`; its value
equals `.markdown p`'s bottom margin, so the two cannot disagree without the
test saying so; the phone rule restates no padding; and the last block still
gives up its margin. Mutation-checked — restoring the 30px tail fails two
checks, restating the phone padding fails a third.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@lvwerra
lvwerra merged commit c8f894b into main Aug 19, 2026
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