oficina / docs Public

Review round: the comment was wrong about the bug it fixes

Branch review caught the opening arithmetic counting two insets when
there are three. The shell's own padding is one of them — the same
padding the paragraph twenty lines below was already explaining how to
cancel — so the stated before-numbers, 151px at 375 and 96px at 320,
were both wrong and wrong in the direction that flatters the change.
Measured, they were 119px and 64px. The after-numbers were right.

The breakpoint derivation inherited the same error: 45 characters at
576px only works if you forget an inset, and the honest version is
about 40. 36rem stays, because reflow promises 320px viewports and
577-600px still yields a readable 306-330px column, but it is now
written down as the judgement it is rather than a formula that does
not add up.

Second fix: the media query had no type, so it applied to print. Letter
and A4 print boxes stay above the breakpoint, but A5 portrait is about
559px and crosses it — printing on A5 would have dropped the 1in and
applied a phone's negative margin, in the one medium where a page
margin is not a metaphor. `screen and` restores what the comment above
it promises.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Paul Campbell pushed by paul@keymail.dev 3cc81ea0ab6b1ccfcd6d5429c9a22d261d68ce00 parent d318907
1 file changed, +45 −21
  • internal/docs/views/show.html +45 −21

CI passed — run details

Intent

Human

<task-notification> <task-id>a87988f42ce9b5217</task-id> <tool-use-id>toolu_01WmQnEvWtUPQ5sbvTn9hgxt</tool-use-id> <output-file>/home/paulca/.cache/tmp/claude-1001/-home-paulca-amadan-net-oficina-docs/4a30c557-129a-4e57-8aa3-0c7518c36cf1/tasks/a87988f42ce9b5217.output</output-file> <status>completed</status> <summary>Agent "Fable review of mobile margins" finished</summary> <note>A task-notification fires each time this agent stops with no live background children of its own. The user can send it another message and resume it, so the same task-id may notify more than once.</note> <result>Review complete. All claims verified against rastrillo v0.22.0's actual `tokens.css` and `column.html` layout, and measured empirically in headless Chromium (fixed-width iframes at 320/375/414/576/577/600, both with and without the new media block; iframe viewports carried a 15px classic scrollbar, so this also exercised the scrollbar case the comment argues about). ## Verdict: approve with changes The CSS is sound — every mechanism claim checks out under measurement. The required change is to the comment prose, which this repo treats as load-bearing: the block's opening arithmetic is confidently wrong about the pre-change state, and internally contradicted by its own inner comment. ## Genuine defects **1. The "before" arithmetic omits the shell inset its own inner comment documents — the stated numbers are wrong.** `internal/docs/views/show.html:103-112` and `:120-122`. The opening paragraph says "a reader sees two nested insets" and computes `375 - 32 - 192 = 151px` and 96px at 320px. But the content renders inside `&lt;main rst-page&gt;` (rastrillo's `column.html:21`), whose unconditional `padding: var(--rst-sp-5) var(--rst-sp-4) var(--rst-sp-6)` (`tokens.css:141-146`, sp-4 = 1rem) adds a third inset of 32px — exactly the padding the inner comment at `:137-139` says it measured and cancels. Measured pre-change columns: **119px at 375, 64px at 320, 320px at 576** — not 151/96/352. The error understates the bug, so nothing behavioral is wrong, but the derivation of the breakpoint inherits it: "576 − 32 − 192 = 352 ≈ 45 characters" is really 320px ≈ 41 characters; the honest 45-char derivation gives ~608px ≈ 38rem. 36rem remains defensible (at 577-600px the surviving paper column is 306-330px — cramped but readable, and reflow only promises 320px viewports), but the comment must either restate the derivation with three insets and "about 40 characters", or move to 38rem. Smallest fix: correct "two nested insets" to three, replace 151/96/352 with 119/64/320, and reconcile the 45-character claim. The post-change numbers (343/288) are correct as written — I measured exactly those. **2. The media query is untyped, so it applies in print on narrow paper.** `show.html:133`. `@media (max-width: 36rem)` matches all media. US Letter/A4 print boxes are ~700-800 CSS px, unaffected; but A5 portrait (~559px, less printer margins) crosses the threshold, dropping the 1in padding and applying negative margins in print — precisely where the comment promises "the 1in is untouched... where the print margin can be shown honestly it still is." Failure scenario: File → Print on A5 produces an edge-bleeding sheet with 1rem margins. Smallest fix: `@media screen and (max-width: 36rem)`. Minor — no print stylesheet exists yet and letter/A4 are untouched — but it is the one place the code contradicts its comment. ## Verified sound (stating plainly, per the questions asked) - **Negative-margin breakout (Q1): correct, and the best available technique.** Measured no horizontal scroll at any width including with a real classic scrollbar. The widened canvas equals the shell's border box, which by construction never exceeds the viewport — cancelling known padding genuinely cannot overhang, in RTL (`margin-inline` is logical; the padding is symmetric), under zoom (both lengths are the same rem token), and inside any of rastrillo's three layouts. The `100vw` critique is factually correct and doubly apt here: rastrillo sets `scrollbar-gutter: stable` on `html` (`tokens.css:112-127`), so `100vw` would overhang even when no scrollbar is drawn. The one alternative that removes the token coupling — zeroing `[rst-page]`'s own padding under the query — would un-inset the page header too and break the title alignment, so the margin is the right call. The coupling's failure mode is honestly described and fails soft, as claimed. - **Alignment claim: measured true.** After the change, first character at 16px, `h1` at 16px, `[docs-page]` at 0. `[rst-page-header]` has no inline padding of its own. - **Breakpoint form (Q2): right.** rem in a media query resolves against the user's default font size, so large-text users shed the paper at proportionally wider windows — the correct direction. A container query isn't available (rastrillo declares no container) and would be wrong anyway: the rule cancels the shell's viewport-level padding, which is inherently a viewport relationship; below `[rst-page]`'s 64rem max-width the two agree in every reachable state. RTL needs nothing here. Boundary measured: applies at 576px inclusive, off at 577 ("below 36rem" is off by 1px at the boundary — trivial). - **Surrounding constraints (Q3): all intact.** Flex column, `min-block-size: 11in`, and `[docs-editor] { flex: 1 0 auto }` are untouched, so the click-below-last-paragraph target survives (and the caller's live measurement confirms). Literal colours untouched. `[docs-save-status]` is `position: fixed` and viewport-anchored — independent of the breakout. The scriptless read view shares the same `[docs-text]`/`[docs-page]` rules, so it gets the identical narrow layout. `[docs-back]`'s 24px floor untouched. No new attributes, so `TestOurTemplatesUseTheAttributeGrammar` is unaffected. - **Accessibility (Q4): a straight improvement.** 400% zoom on a 1280px window is a 320px CSS viewport → narrow branch → 288px column, no horizontal scroll (measured): SC 1.4.10 goes from a 64px column to a pass. The zoom-narrows-viewport interaction with the rem breakpoint is the right shape for 1.4.4 too. - **Shadow and bottom margin (Q5): both correct.** The canvas keeps `padding-block: var(--rst-sp-4)`, so grey separation above and below the sheet survives; a viewport-clipped side shadow would indeed read as a smear. `margin-block-end: 0` avoids stacking 2.5rem of margin on the canvas's own 1rem bottom padding. ## Worth stating, not defects - The rule also reduces the canvas's block padding from `--rst-sp-6` (2.5rem) to `--rst-sp-4` (1rem); the prose only mentions the side padding "going to nothing". One clause would cover it. - "96px: under ten characters of 12pt Georgia" stays accidentally true of the real 64px figure, but fix it with the numbers in defect 1. - No automated regression pins the breakpoint behavior; consistent with the repo's current practice of pinning grammar rather than layout, so noted only. The relevant file is `/home/paulca/amadan.net/oficina/docs/.claude/worktrees/live-sync/internal/docs/views/show.html` (lines 100-164 are the diff). One measurement run needed a sandbox bypass: Chromium's process-singleton unix socket is blocked by the command sandbox (`/sandbox` manages this); the bypassed commands only rendered local files read-only.</result> <usage><subagent_tokens>68165</subagent_tokens><tool_uses>31</tool_uses><duration_ms>366206</duration_ms></usage> </task-notification>

diff --git a/internal/docs/views/show.html b/internal/docs/views/show.html
index e2cd8e8..0fb6744 100644
--- a/internal/docs/views/show.html
+++ b/internal/docs/views/show.html
@@ -100,27 +100,41 @@
/* Narrow screens stop simulating paper, because at phone widths the
simulation costs more than it says.
- Above this breakpoint a reader sees two nested insets: [docs-canvas]'s
- 1rem tray padding either side, and then [docs-page]'s own 1in. That
- inner inch is a real measurement — it stands for the document's page
- margin, which page setup and the layout engine will own properly
- later — and on a desktop it is worth every pixel. On a 375px phone
- the same arithmetic (375 - 32 - 192) leaves a 151px column, and at
- 320px, which this app supports without exemption (WCAG 2.2 AA SC
- 1.4.10), it leaves 96px: under ten characters of 12pt Georgia per
- line. A page margin that squeezes the page is no longer describing
- the page.
+ Above this breakpoint a reader sees THREE nested insets, and the
+ first one is easy to miss because it belongs to the framework: the
+ shell this template renders into (rastrillo's [rst-page]) pads
+ --rst-sp-4 either side, then [docs-canvas] adds its own 1rem tray
+ padding, then [docs-page] adds 1in. That inner inch is a real
+ measurement — it stands for the document's page margin, which page
+ setup and the layout engine will own properly later — and on a
+ desktop it is worth every pixel. On a 375px phone the three together
+ (375 - 32 - 32 - 192) left a 119px column, and at 320px, which this
+ app supports without exemption (WCAG 2.2 AA SC 1.4.10), 64px: about
+ five characters of 12pt Georgia per line. A page margin that
+ squeezes the page is no longer describing the page.
- So below 36rem the tray's side padding goes to nothing, the sheet
- runs edge to edge, and ONE margin survives inside it. Text goes to
- 343px at 375px wide and 288px at 320px. The 1in is untouched at
- every width above the breakpoint, so where the print margin can be
- shown honestly it still is.
+ (An earlier version of this comment counted two insets and said
+ 151px and 96px. It was wrong in the direction that flatters the
+ code, and it was wrong while the paragraph below it described
+ cancelling the very inset it had forgotten. The numbers here are
+ measured, not derived.)
- 36rem is derived, not fashionable: it is about where the two insets
- stop leaving 45 characters a line (576 - 32 - 192 = 352px), which is
- the low end of a comfortable measure. Phones in portrait are all
- well under it; a tablet or a small window keeps the paper.
+ So below 36rem the tray's side padding goes to nothing, its block
+ padding drops from --rst-sp-6 to --rst-sp-4, the sheet runs edge to
+ edge, and ONE margin survives inside it. Text goes to 343px at 375px
+ wide and 288px at 320px. The 1in is untouched at every width above
+ the breakpoint, so where the print margin can be shown honestly it
+ still is.
+
+ 36rem is a judgement rather than a formula, and the honest version
+ of the arithmetic says so: at 576px the three insets leave a 320px
+ column, about 40 characters of this face, and holding out for the
+ 45 characters that make a comfortable measure would put the
+ breakpoint nearer 38rem. 36rem is the choice because reflow only
+ promises 320px viewports, and between 577 and 600px the paper still
+ yields 306-330px — cramped, readable, and still recognisably a
+ page. Phones in portrait are all well below it; a tablet or a small
+ window keeps the paper.
The shadow goes with the tray — it exists to lift the sheet off the
grey, and once there is no grey beside it, it is a dark smear on the
@@ -129,8 +143,18 @@
so that a click in the empty space below the last paragraph still
lands in the document (see fixtures/editor-entry.js's
placeCaretFromPage), and the literal colours, which must not repaint
- under anyone's theme at any width. */
- @media (max-width: 36rem) {
+ under anyone's theme at any width.
+
+ `screen and`, not a bare width query, and it is not decoration. A
+ media query with no type matches print too, and a print box is
+ measured in CSS pixels like any other: US Letter and A4 come out
+ around 700-800px and stay above this breakpoint, but A5 portrait is
+ about 559px and crosses it. Printing a document on A5 would then
+ drop the 1in and apply a negative margin meant for a phone screen —
+ an edge-bleeding sheet with 1rem margins, in the one medium where
+ the page margin is not a metaphor at all. `screen` is what keeps
+ the paragraph above true. */
+ @media screen and (max-width: 36rem) {
[docs-canvas] {
padding-inline: 0;
padding-block: var(--rst-sp-4);