fix(reader): render pages at a consistent width instead of thin stripes

Reader page sizing was height-driven — `max-height: 90vh` in single mode
and unbounded natural width in continuous mode. A long vertical page hit
the height cap first and, preserving aspect ratio, collapsed into a thin
vertical stripe; narrow and wide scans rendered at different widths.

Switch both modes to width-driven sizing via a capped `--reader-page-width`
reading column (min(100%, 700px)): every page renders at the same width
regardless of intrinsic dimensions, and tall pages extend downward and
scroll instead of shrinking. Top-align the single-mode grid and make the
prev/next chevrons sticky so they stay reachable on a long page.

Add a Playwright spec asserting equal rendered width across differently-
proportioned pages and that a tall page exceeds the viewport height. The
page-context-menu fixture served a 1x1 image that now renders as a 700x700
square taller than the viewport, making Playwright scroll it into view
before right-clicking — a synthetic scroll that trips the menu's by-design
close-on-scroll. Give that fixture a realistic landscape aspect so the page
fits the viewport, matching real user behaviour (no scroll on right-click).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-07-04 20:44:30 +02:00
parent 38146b4d03
commit cf8971faae
6 changed files with 185 additions and 17 deletions

View File

@@ -1783,11 +1783,23 @@
written by the ResizeObserver in onMount so the reservation
always matches actual rendered height. Focus mode collapses
the reservation in lockstep with the bar's slide-out. */
/* Sizing is width-driven: every page renders at this consistent width
(a capped reading column on desktop, full width on narrow screens)
regardless of its intrinsic dimensions. Tall pages therefore extend
downward and scroll instead of shrinking to fit the viewport height —
which is what used to turn long vertical pages into thin stripes. */
.page-wrap,
.continuous {
--reader-page-width: min(100%, 700px);
}
.page-wrap {
display: grid;
grid-template-columns: auto 1fr auto;
gap: var(--space-2);
align-items: center;
/* start (not center): a tall page's top must sit under the reader
nav so the reader begins at the top and scrolls down. */
align-items: start;
padding-top: var(--reader-nav-h);
transition: padding-top 220ms ease-out;
}
@@ -1811,8 +1823,8 @@
}
.page-image {
width: var(--reader-page-width);
max-width: 100%;
max-height: 90vh;
height: auto;
margin: 0 auto;
display: block;
@@ -1829,12 +1841,10 @@
-webkit-user-select: none;
}
.continuous .page-image {
/* In continuous mode the user is scrolling — let each page take
its natural height instead of capping at viewport height, so
there are no scroll dead-zones inside a single page. */
max-height: none;
}
/* Continuous mode inherits the width-driven sizing above: every page is
the same width and takes its natural (uncapped) height, so there are
no scroll dead-zones inside a single page and widths stay consistent
between a narrow scan and a wide one. */
.nav {
display: inline-flex;
@@ -1851,6 +1861,12 @@
transition:
background var(--transition),
border-color var(--transition);
/* Now that the page-wrap grid is top-aligned, a tall page makes its
row much taller than the viewport. Stick the prev/next chevrons to
the vertical middle of the viewport so they stay reachable while
the reader scrolls down a long page. */
position: sticky;
top: calc(50vh - 22px);
}
/* ===== Continuous-mode chapter bar (sticky bottom) =====