Skip to content

Let a long title, author or series wrap instead of dragging the book page sideways#1178

Open
new-usemame wants to merge 2 commits into
mainfrom
fix/book-detail-title-overflow
Open

Let a long title, author or series wrap instead of dragging the book page sideways#1178
new-usemame wants to merge 2 commits into
mainfrom
fix/book-detail-title-overflow

Conversation

@new-usemame

@new-usemame new-usemame commented Jul 27, 2026

Copy link
Copy Markdown
Owner

Fixes the long-standing ~35px mobile overflow on the book detail page — the one that has been passing through as "pre-existing" in PR after PR.

Symptom

On a phone, a book whose title, author or series contains one long unbreakable word makes the whole book page scrollable sideways. Measured on cwn-local at 390px: 290px of horizontal scroll for a single 35-character German compound title.

The header text column is only ~217px wide at that viewport, and .title, .authors and .series had no break rule, so any token wider than the column overflowed it — and that overflow propagates up to the document.

Why it survived so many attempts

No element's border box crosses the viewport edge. The text overflows its own box, so every getBoundingClientRect().right > clientWidth sweep came back empty. Each hypothesis in notes/e2e-mobile-overflow-ci-triage.md and its predecessors — long tag pills, editor chips, the detail actions row, book cards, poisoned per-user view settings — was looking for something sticking out, and nothing was.

The scrollWidth > clientWidth probe added in #1177 named it on its first CI run:

(no element’s border box crosses the viewport edge — the width comes from a margin,
 a pseudo-element, a transform, or a min-width floor)
  boxes whose content is wider than they are:
  holds 48px more content than its box @ depth 10: <h1 class="_title_sjq14_163"> client=… scroll=…
  holds 47px more content than its box @ depth  7: <div class="_layout_sjq14_21">

_title_sjq14_163 is .title at line 163 of BookDetail.module.css. Exact hit.

Fix

The same treatment the tag pill got in #1170: overflow-wrap: anywhere.

Normal wrapping still breaks between words first, so ordinary titles render exactly as before; anywhere only splits a token that cannot fit on a line by itself. Applied to author and series as well — same column, same arbitrary library metadata, and a transliterated name or a long series title fails identically.

Verification

Live, on cwn-local at 390px:

Document overflow h1 content vs box
Before 290px 519 vs 217 (302px over)
After 0px 217 vs 217 (0)

Title wraps to three lines inside the viewport with the layout intact — verified by screenshot at 390×844 (cover, action buttons and metadata table all sit within the viewport; no horizontal scrollbar).

Regression testbook-detail.spec.ts, measuring the delta the long metadata adds rather than absolute page overflow, so it holds on any fixture regardless of what other baseline the page carries:

  • Red: long title/author/series widened the page by 109px (baseline 290px) against a redeployed pre-fix build.
  • Green: 0px after.
  • Also pins the title box directly (scrollWidth - clientWidth <= 1) — the measurement that localised the bug, so a future regression is named rather than merely counted.

No regressions: book-detail.spec.ts, browse.spec.ts, mobile.spec.ts24 passed, 1 skipped, on desktop and mobile.

Depends on

Nothing — this stands alone. But the diagnostic in #1177 is what found it, and merging that first means the next occurrence of this bug class self-identifies instead of costing another few ticks.

Tier: single-file CSS change plus a test. No production Python, no new dependency.

…page sideways

The book detail header column is ~217px wide on a 390px phone, and .title,
.authors and .series carried no break rule. A single token wider than that column
overflows it, and the overflow propagates all the way up to the document: one
35-character German compound title produced 290px of horizontal scroll on
cwn-local, 0 after.

This is the ~35px baseline overflow CI's detail page has carried for several
releases (notes/e2e-mobile-overflow-ci-triage.md). It resisted diagnosis because
no element's BORDER BOX crosses the viewport edge — the text overflows its own
box, so every getBoundingClientRect sweep came back empty and each hypothesis in
turn (tag pills, editor chips, the actions row, book cards, poisoned view
settings) was looking for the wrong thing. The scrollWidth > clientWidth probe
added in #1177 named it on the first CI run: <h1 class="_title_sjq14_163"> holding
48px more content than its box.

Fix is the same treatment the tag pill got in #1170: overflow-wrap: anywhere.
Normal wrapping still breaks between words first, so ordinary titles are
unchanged; anywhere only splits a token that cannot fit on a line by itself.
Applied to author and series too — the same column, the same arbitrary
library metadata, and a transliterated name or long series title fails the same
way.

Regression test measures the DELTA the long metadata adds rather than absolute
page overflow, so it holds on any fixture: red at 109px against a redeployed
pre-fix build, green at 0 after. It also pins the title box directly
(scrollWidth - clientWidth <= 1), which is the measurement that localised the bug.

Verified on cwn-local at 390px: 290px -> 0px, title wraps to three lines inside
the viewport, layout intact (screenshot in the PR). book-detail, browse and mobile
specs 24 passed.
new-usemame added a commit that referenced this pull request Jul 27, 2026
assertNoHorizontalOverflow said only "expected <= 1, received 35" — no element, no
CSS. CI's book detail page carried that number for several releases and it was
annotated "pre-existing" in PR after PR, with a real user-facing bug (#1170)
sitting behind the label.

On failure it now lists the offending elements deepest-first with the properties
that cause this bug class, and falls back to margin-box and
scrollWidth-vs-clientWidth probes when no border box crosses the edge — the case
CI actually hits. It ran once and named the culprit: the detail page <h1> holding
48px more content than its box. Fixed in #1178.

Children absorbed by a scrollable ancestor are excluded; the first draft listed
catalog book cards as 139px past the edge on a page whose real overflow was 0.
Pinned by e2e/overflow-diagnostics.spec.ts (7/7, desktop and mobile), including
the pseudo-element case. A right margin was tested and does NOT extend scrollable
overflow in Chromium, so that probe stays a secondary signal rather than an
asserted behaviour.

Tests only, no production code. The advisory E2E red on this branch is the bug it
diagnosed, not a regression it introduced.
v4.1.22 is not tagged yet and the image built from main carries this fix, so the
entry belongs in that section rather than in [Unreleased] — the same invariant the
recut in #1176 restored. Adds the symptom-first CHANGELOG entry, the matching
What's New item so the shipped SPA documents the build it ships in, and the
CHANGES-vs-upstream row.
@new-usemame

Copy link
Copy Markdown
Owner Author

CI confirms it — the 35px is gone

E2E run on this branch, hidden-books.spec.ts:252:

✓  100 [desktop] › e2e/hidden-books.spec.ts:252:1 › mobile detail actions and View settings stay within 390px
✓  259 [mobile]  › e2e/hidden-books.spec.ts:252:1 › mobile detail actions and View settings stay within 390px

Green on both projects for the first time. browse.spec.ts:27 (the other spec that tripped on the same page, at 45px) no longer appears in the failure list either.

Job totals moved 267 → 273 passed, 3 hard failures → 1.

The one remaining hard failure is login-redirect.spec.ts:68 (magic-link deep next destination), which is the third of the three long-standing failures and is unrelated to layout — it fails on started.ok() being false, not on overflow. The two flaky entries (sp1-ux, sp2-display-options) passed on retry.

So the class of failure this PR targets is fully cleared, and the advisory E2E red on this PR is the untouched magic-link case, not a regression introduced here.

Changelog

Pushed a follow-up commit adding the symptom-first CHANGELOG entry, the matching What's New item, and the CHANGES-vs-upstream row.

They go into the v4.1.22 section rather than [Unreleased]: v4.1.22 is not tagged yet and the image built from main carries this fix, so putting it in [Unreleased] would ship it undocumented and then re-announce it under v4.1.23 — the exact inconsistency #1176 just cleaned up.

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