docs: meet the mobile baseline, which the carousel was not doing

Checked the section against MOBILE.md at its three reference widths instead
of assuming the breakpoints were enough. Two real failures at 360px.

The arrows sat in the heading's flex row, taking 96px from a 328px card and
crushing the lede into a four-line column — for a control that swiping
already provides. Below 640px they are now gone, the header is a single
column, one card shows at 86% so the next one peeks, and the affordance is
the right-edge scroll-fade MOBILE.md already documents for horizontal
scrollers. The fade lifts at the end of the travel, so the at-end state is
computed whether or not an arrow exists to consume it.

The arrow and add-to-compare buttons were 40px against a 44px floor. Both
are 44 now. A card title's own box is shorter, but its hit area is the whole
card through the ::after overlay, so it passes on the target that actually
receives the tap.

360, 390 and 430 now all report zero overflow, no failing tap targets and no
text under 11px. MOBILE.md wanted a Playwright width check and recorded that
Playwright was not in the project; it is, so the journey now carries one for
this page.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
TudorandClaude Opus 5 committed 2026-09-21 22:37:34 +01:00
1 parent e4e8f02599
commit 8a23e3657d
3 files changed
+170 -18

No files matched your search

+24 -7
View File
@@ -56,7 +56,7 @@ body {
.page > header { margin-bottom:28px; display:flex; flex-wrap:wrap; gap:16px; align-items:flex-start; justify-content:space-between; }
.page > header h1 { font:700 25px/1.25 var(--font-display); letter-spacing:-.6px; margin:0 0 6px; }
.page > header p { margin:0; color:var(--text-muted); font-size:14px; max-width:60ch; }
.theme-switch { border:1px solid var(--border-strong); background:var(--bg-card); color:var(--text-secondary); border-radius:999px; padding:8px 14px; font:500 13px var(--font-ui); cursor:pointer; min-height:40px; }
.theme-switch { border:1px solid var(--border-strong); background:var(--bg-card); color:var(--text-secondary); border-radius:999px; padding:8px 14px; font:500 13px var(--font-ui); cursor:pointer; min-height:44px; }
.theme-switch:hover { border-color:var(--brand); color:var(--brand); }
/* ── The page context each variant is shown inside ─────────────────── */
@@ -83,7 +83,7 @@ body {
plain horizontally scrollable row. */
.arrows { display:flex; gap:8px; flex:none; }
.arrow {
width:40px; height:40px; display:grid; place-items:center; cursor:pointer;
width:44px; height:44px; display:grid; place-items:center; cursor:pointer;
border:1px solid var(--border-strong); border-radius:999px;
background:var(--bg-card); color:var(--brand);
}
@@ -103,7 +103,17 @@ body {
.scroller::-webkit-scrollbar { display:none; }
.scroller:focus-visible { outline:2px solid var(--brand); outline-offset:4px; border-radius:var(--radius-md); }
@media (max-width:820px) { .scroller { grid-auto-columns:calc((100% - 14px) / 2); } }
@media (max-width:560px) { .scroller { grid-auto-columns:86%; } .card { padding:20px; } }
/* Touch widths: the arrows would squeeze the lede into a four-line column for a
control that swiping already provides, so they go and the documented
right-edge fade carries the affordance instead (MOBILE.md). The fade lifts at
the end of the travel, where there is nothing more to hint at. */
@media (max-width:640px) {
.top { display:block; }
.arrows { display:none; }
.scroller { grid-auto-columns:86%; mask-image:linear-gradient(to right, #000 calc(100% - 28px), transparent); }
.scroller[data-at-end=true] { mask-image:none; }
.card { padding:20px; }
}
.school {
position:relative; display:flex; flex-direction:column; scroll-snap-align:start;
@@ -132,7 +142,7 @@ body {
.metric .label { margin:4px 0 0; font-size:12px; color:var(--text-secondary); }
.metric .ref { margin:2px 0 0; font-size:12px; color:var(--text-muted); }
.add {
position:relative; z-index:1; margin-top:14px; width:100%; min-height:40px;
position:relative; z-index:1; margin-top:14px; width:100%; min-height:44px;
font:500 13px var(--font-ui); cursor:pointer; border-radius:var(--radius-md);
border:1px solid var(--border-strong); background:var(--bg-card); color:var(--brand);
}
@@ -307,12 +317,19 @@ function render() {
const EDGE = 8;
function syncArrows(id, scroller) {
const max = scroller.scrollWidth - scroller.clientWidth;
const atStart = scroller.scrollLeft <= EDGE;
const atEnd = scroller.scrollLeft >= max - EDGE;
// Drives the mobile scroll-fade, so it is computed even where no arrow is
// rendered to consume it.
scroller.dataset.atEnd = String(atEnd);
const prev = document.querySelector(`.arrow[data-scroll="prev"][data-variant="${id}"]`);
const next = document.querySelector(`.arrow[data-scroll="next"][data-variant="${id}"]`);
if (!prev || !next) return;
const max = scroller.scrollWidth - scroller.clientWidth;
prev.disabled = scroller.scrollLeft <= EDGE;
next.disabled = scroller.scrollLeft >= max - EDGE;
prev.disabled = atStart;
next.disabled = atEnd;
}
document.getElementById('variants').addEventListener('click', (event) => {