oficina / docs Public

Mobile: one margin, not three closed

On branch worktree-mobile-margins

Paul: "on mobile, the doc view is squished because of all the padding/margins … we probably only need one margin on mobile?" Correct, and the arithmetic is worse than "squished".

What was happening

Three insets stacked, and nobody had added them up:

| | inset each side | |---|---| | main[rst-page] — rastrillo's shell | 1rem | | [docs-canvas] — the grey tray | 1rem | | [docs-page] — the sheet's own page margin | 1in (96px) |

On a 375px phone that leaves a 151px text column. At 320px, which AGENTS.md supports without exemption (WCAG 2.2 AA SC 1.4.10), it leaves 96px — under ten characters of 12pt Georgia per line.

The inch is not decoration: it stands in for the document's real page margin, and page setup plus the layout engine will own it properly later. It is worth every pixel on a desktop. On a phone it was describing a page by squeezing it.

What changed

One media query, max-width: 36rem, in show.html. Above it nothing changes at all — the sheet keeps its 1in, its tray, and its shadow. Below it the tray's side padding goes, the sheet runs to the screen edge, and one 1rem margin survives inside it.

Measured in a real browser, not reasoned about:

| viewport | text before | text after | |---|---|---| | 320 | 96px | 288px | | 375 | 151px | 343px | | 414 | 190px | 382px | | 768 | 497px | 497px (unchanged) | | 1280 | 624px | 624px (unchanged) |

36rem is derived rather than fashionable: it is where the insets stop leaving about 45 characters a line, the low end of a comfortable measure. Every phone in portrait is well under it; a tablet or a small window keeps the paper.

Two decisions worth reading

Reaching the screen edge means cancelling the shell's padding, and how matters. The reflex is width: 100vw, and it is wrong here: 100vw counts a classic scrollbar, so on a desktop browser narrowed below the breakpoint — which is exactly how a person tests a 320px reflow — the sheet overhangs by the scrollbar's width and starts a horizontal scroll. That is the same WCAG failure this block exists to fix, reintroduced by the fix. A negative margin in the shell's own token cannot overhang. It does couple this rule to [rst-page]'s padding staying --rst-sp-4; if that changes the sheet sits slightly proud or slightly inset, which is visible and harmless rather than silent.

The shadow goes with the tray. It exists to lift the sheet off the grey; with no grey beside it, it is a dark smear along the screen edge.

Deliberately unchanged: the flex column and min-block-size, because the editable node fills the page's text area so a click in the empty space below the last paragraph still lands in the document (placeCaretFromPage), and the literal colours, which must not repaint under a reader's theme at any width.

Verification

Headless Chromium at 320/375/414/768/1280: the column widths above, no horizontal scroll at any width, the 1in intact above the breakpoint, an edit posted from a second connection still arriving live at 375px, and a click in the surviving margin still placing the caret inside the document. .amadan/ci.d/10-gate exit 0.

Note for the impeccable session

This touches internal/docs/views/show.html, which cafb696 had just reworked. The change is additive — one @media block appended after the [docs-page] rule, nothing above it edited — so it should merge cleanly, but you own that file's design and should say if the mobile treatment fights anything you have planned for it.

Branch review by Fable is in — approve with changes — and a fix round has landed at 3cc81ea, CI green.

The CSS was sound: it verified the negative-margin breakout against rastrillo's actual tokens.css and column.html, and measured it in headless Chromium at 320/375/414/576/577/600 with a real 15px classic scrollbar present. No horizontal scroll anywhere, alignment claim confirmed (first character and h1 both at 16px, sheet at 0), and 400% zoom on a 1280px window now passes SC 1.4.10 where it previously produced a 64px column.

It also found the 100vw critique is more right than I knew: rastrillo sets scrollbar-gutter: stable on html, so 100vw would overhang even when no scrollbar is drawn.

What was wrong, and it was the comment

The opening arithmetic counted two insets when there are three — and the third is the shell padding that the same comment, twenty lines further down, already explains how to cancel. So the block contradicted itself, and the before-numbers were wrong in the direction that flatters the change: 151px at 375 and 96px at 320 were really 119px and 64px, measured. The after-numbers (343/288) were correct.

The breakpoint derivation inherited the error. "45 characters at 576px" only works if you forget an inset; honestly it is about 40, and holding out for 45 would put the breakpoint nearer 38rem. 36rem stays — reflow only 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.

That is the second time on these two branches that a stale derived number survived into a comment after the thing it was derived from had changed. Both times the code was right and the prose was wrong, which is the failure mode this repo already names, and both times the fix was to measure rather than to re-derive.

Second defect: the media query had no type, so it applied to print. Letter and A4 print boxes are ~700-800 CSS px and 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 at all. Now @media screen and (max-width: 36rem), which is what makes the paragraph above it true.

Also folded in: the prose now mentions that the canvas's block padding drops from --rst-sp-6 to --rst-sp-4, which the rule always did and the comment never said.

Explicitly verified sound

The flex column and min-block-size that keep the click-below-the-last-paragraph target working; [docs-save-status]'s fixed positioning, independent of the breakout; the scriptless read view, which shares [docs-text]/[docs-page] and gets the identical narrow layout; [docs-back]'s 24px target floor; the literal colours; the rem breakpoint's behaviour for large-text users (they shed the paper at proportionally wider windows, which is the right direction); and RTL, where margin-inline is already logical and the shell's padding is symmetric.

One note recorded rather than acted on: nothing pins the breakpoint behaviour as an automated regression. That matches the repo's current practice of pinning markup grammar rather than layout, so it stays a note.

Merged as d53d726 and deployed; the live host has been serving it since. Closing. The 320px horizontal scroll found while verifying this is not part of it — that was the toolbar and the search form, fixed on reflow-320 (discussion 16).