From ac28efffb0bd7577f0df3d875747b5d6c644ecce Mon Sep 17 00:00:00 2001 From: Hound Date: Fri, 21 Aug 2026 09:35:29 -0500 Subject: [PATCH] site: fix mobile overflow at the cause, and give the header a hamburger MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Joe opened the page on a phone and found what I could not: the install commands pushed the layout sideways, and the header did the same. ROOT CAUSE, and it was one thing behind both: grid and flex children default to min-width:auto, so a box holding a long unbreakable string — a curl command, a terminal block — refuses to shrink below that string and pushes the whole PAGE sideways instead of scrolling inside itself. `overflow-x: auto` on the inner element does nothing while its parent will not shrink. min-width:0 on the children of every grid is the fix, and it is why the page was fine at 1080px and broken at 390. The copy blocks also had the button inside the scrolling region, so on a narrow screen the one control anybody actually uses scrolled out of reach. The command now scrolls in its own box, the button sits outside it, and below 720px the command wraps instead of scrolling — the whole thing visible at once, break-all because a URL has no useful break points. HEADER: hiding the nav links on small screens was never a fix, because the theme switch and the call to action still competed for a 360px bar. Below 760px the bar now holds the mark and one button, and everything else moves into a panel: aria-expanded on the control, Escape closes it, following a link closes it — a panel left open over the thing you just asked to see is its own bug — and a resize past the breakpoint closes it so rotating to landscape cannot strand a phone panel over a desktop layout. One regression I introduced and caught before it shipped: I had written `#menu { display: contents }` to make the panel transparent on desktop. An ID beats a class, so that quietly overrode nav.links's own flex layout and margin-left:auto and unstuck the desktop navigation from the right-hand side. The panel styles now live inside the breakpoint and nowhere else. I still cannot see this page — the browser here renders with CSS disabled — so this was reasoned from the cause Joe's report pointed at rather than from looking. Another pair of eyes on a phone would be worth having. Co-Authored-By: Claude Opus 5 --- site/index.html | 118 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 106 insertions(+), 12 deletions(-) diff --git a/site/index.html b/site/index.html index 2b47bba..9c5a77a 100644 --- a/site/index.html +++ b/site/index.html @@ -92,6 +92,13 @@ body { } .wrap { max-width: 1080px; margin: 0 auto; padding: 0 24px; } + +/* Grid and flex children default to min-width:auto, so anything holding + a long unbreakable string — a terminal block, a curl command — refuses + to shrink below that string and pushes the whole PAGE sideways instead + of scrolling inside its own box. This is the single most common cause + of a layout that is fine on a desktop and broken on a phone. */ +.hero > *, .grid2 > *, .grid3 > *, .tiers > *, .install > *, .stats > * { min-width: 0; } .mono { font-family: "JetBrains Mono", ui-monospace, monospace; } a { color: var(--brand); text-decoration: none; } @@ -126,7 +133,46 @@ header { nav.links { margin-left: auto; display: flex; gap: 22px; align-items: center; } nav.links a { color: var(--dim); font-size: 14.5px; font-weight: 500; } nav.links a:hover { color: var(--fg); text-decoration: none; } -@media (max-width: 720px) { nav.links a.hide-sm { display: none; } } + +/* Hamburger. Hiding links on a phone was not enough — the theme switch + and the call to action still competed for a 360px bar and pushed it + sideways. Below the breakpoint the bar holds the mark and one button, + and everything else moves into a panel. */ +.menu-btn { + display: none; appearance: none; border: 1px solid var(--line-hi); + background: var(--raised); color: var(--fg); cursor: pointer; + border-radius: 9px; padding: 9px; line-height: 0; margin-left: auto; +} +.menu-btn svg { width: 20px; height: 20px; } +.menu-btn .close { display: none; } +.menu-btn[aria-expanded="true"] .open { display: none; } +.menu-btn[aria-expanded="true"] .close { display: block; } + +/* No display:contents here. An ID beats a class, so it would override + nav.links's own flex layout and margin-left:auto and quietly unstick + the desktop navigation from the right-hand side. The panel styles + belong inside the breakpoint and nowhere else. */ + +@media (max-width: 760px) { + .menu-btn { display: inline-flex; } + #menu { + display: none; + position: absolute; left: 0; right: 0; top: 100%; + background: var(--bg); border-bottom: 1px solid var(--line); + padding: 8px 24px 20px; + flex-direction: column; align-items: stretch; gap: 4px; + box-shadow: var(--shadow); + } + #menu.open { display: flex; } + #menu a:not(.btn) { + padding: 13px 4px; border-bottom: 1px solid var(--line); + font-size: 16px; color: var(--fg); + } + #menu .theme { align-self: flex-start; margin: 14px 0 4px; } + #menu .btn { justify-content: center; padding: 13px 17px; } + header { position: sticky; } + .bar { position: relative; } +} /* theme switch */ .theme { @@ -166,7 +212,7 @@ nav.links a:hover { color: var(--fg); text-decoration: none; } /* terminal */ .term { background: var(--term-bg); border: 1px solid var(--line); border-radius: 13px; - box-shadow: var(--shadow); overflow: hidden; + box-shadow: var(--shadow); overflow: hidden; min-width: 0; max-width: 100%; } .term-bar { display: flex; align-items: center; gap: 9px; padding: 10px 14px; @@ -175,7 +221,7 @@ nav.links a:hover { color: var(--fg); text-decoration: none; } } .term-bar i { width: 9px; height: 9px; border-radius: 50%; background: #32405A; display: inline-block; } .term pre { - margin: 0; padding: 16px 18px 20px; overflow-x: auto; + margin: 0; padding: 16px 18px 20px; overflow-x: auto; max-width: 100%; font: 12.5px/1.75 "JetBrains Mono", ui-monospace, monospace; color: var(--term-fg); } @@ -247,17 +293,35 @@ code, .inline { .copy { display: flex; align-items: center; gap: 12px; background: var(--code-bg); border: 1px solid var(--line); - border-radius: 10px; padding: 13px 15px; overflow-x: auto; + border-radius: 10px; padding: 13px 15px; + min-width: 0; +} +/* Only the command scrolls. The button sits outside that box so it can + never be scrolled out of reach — which is what happens when the whole + row is the scroll container. */ +.copy .cmd { overflow-x: auto; min-width: 0; flex: 1 1 auto; } +.copy code { + background: none; border: 0; padding: 0; font-size: 13px; + white-space: nowrap; display: block; } -.copy code { background: none; border: 0; padding: 0; white-space: nowrap; font-size: 13px; } .copy button { - margin-left: auto; appearance: none; border: 1px solid var(--line-hi); + appearance: none; border: 1px solid var(--line-hi); background: var(--raised); color: var(--dim); cursor: pointer; font: 500 12px/1 "IBM Plex Sans", system-ui, sans-serif; - padding: 6px 10px; border-radius: 6px; flex: 0 0 auto; + padding: 7px 11px; border-radius: 6px; flex: 0 0 auto; } .copy button:hover { color: var(--fg); } +/* On a phone, wrapping beats sideways scrolling: the whole command is + visible at once, and the Copy button is the thing they will use + anyway. break-all because a URL has no useful break points. */ +@media (max-width: 720px) { + .copy { align-items: flex-start; flex-direction: column; gap: 10px; } + .copy .cmd { overflow-x: visible; width: 100%; } + .copy code { white-space: pre-wrap; word-break: break-all; line-height: 1.7; } + .copy button { align-self: flex-end; } +} + /* ── footer ───────────────────────────────────────────────────────── */ footer { border-top: 1px solid var(--line); padding: 40px 0 56px; color: var(--faint); font-size: 13.5px; } .foot { display: flex; gap: 28px; flex-wrap: wrap; align-items: center; } @@ -274,9 +338,13 @@ footer { border-top: 1px solid var(--line); padding: 40px 0 56px; color: var(--f Hound -