Skip to content
Second Brain Chronicles
Go back

One iPhone Screenshot, Eight Sites Broken

One iPhone Screenshot, Eight Sites Broken

iPhone 17 Pro. 375px viewport. I pulled the page sideways and the content slid with it.

jimchristian.net — my personal site, the one that’s supposed to look professional — had horizontal overflow. A monospace code block set to max-width: 70ch was rendering at roughly 630px. The iPhone viewport is 375px. The math doesn’t work and the CSS didn’t care.

One site broken. But I have eight Astro sites deployed. If this pattern exists on one, it probably exists on others.

It existed on seven of them.

The Parallel Audit

Rather than checking each site manually, I launched eight subagents simultaneously — one per site. Each one scanned for the same class of issues: minmax() grids without mobile clamping, fixed-width elements inside responsive containers, nav bars that don’t wrap, prose blocks with hardcoded max-widths.

Eight agents. Eight reports. Seven failures. They came back within seconds of each other, and watching the results land was like opening browser tabs on a site you built drunk — the same mistake, over and over, in slightly different fonts.

Site TypeStatusPrimary Issue
Personal siteBroken70ch prose container, grid minmax(300px)
Newsletter siteBrokenCard grid minmax(280px), button row no wrap
Blog (this one)BrokenCode blocks exceeding viewport
4 other project sitesBrokenVarious grid and padding issues
Parody siteCleanIntentionally simple layout

The only clean site is the joke. There’s probably a lesson in that.

Seven sites with the same class of bug. The parody site — the one I spent the least time on — was the only one that passed. Complexity creates surface area. Simplicity survives the viewport.

The One-Line Fix

Every grid problem had the same solution:

/* Before: breaks on any viewport narrower than 300px */
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));  

/* After: clamps to viewport width */
grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));  

min(Npx, 100%) makes the browser pick whichever value is smaller — the fixed pixel width or the full container width. On desktop, 100% is larger than 300px, so you get your intended column size. On mobile, 100% is 375px minus padding, so the grid clamps to whatever fits. No media query. No breakpoint. One function call and desktop behaviour is identical while mobile stops overflowing.

What I expected: Each site would need its own responsive fix — different layouts, different breakpoints.

What actually happened: Every single grid bug was the same pattern. Wrap the pixel value in min(). Done.

The Four Bug Categories

Across seven sites, every overflow grouped into four categories:

  1. CSS Grid minmax() without clamping — The most common. Card grids, feature grids, pricing grids. All using fixed minimum widths that exceeded mobile viewports. Fix: wrap in min().

  2. Prose containers with max-width: 70ch — Fine for body text in a proportional font. Broken for monospace code blocks inside that container, because 70ch in monospace (where every character is the same width) renders wider than 70ch in the body font the container was designed for.

  3. Nav and button groups without flex-wrap — Horizontal button rows that ran off-screen on narrow viewports. Fix: add flex-wrap: wrap and appropriate gap.

  4. Container padding too generous2rem padding on a 375px viewport eats 128px, leaving 247px for content. Fix: padding: clamp(1rem, 4vw, 2rem).

Damage Report

MetricValue
Sites audited8
Sites broken7
Total CSS changes~30 lines across 7 repos
Deploys to VPS6 (one site has no live domain)
Time elapsed~2 hours, audit to deploy

Thirty lines of CSS. Seven repositories. That’s the cost of not knowing about min() when I wrote the first grid, and then copy-pasting the pattern into every project after.

Preventing Recurrence

Fixing seven sites is fine. Fixing seven sites and then building an eighth with the same bugs is not.

I added mandatory mobile overflow prevention rules to the web-architect agent — the agent that scaffolds new Astro sites:

Every minmax() grid MUST use min(Npx, 100%) for the minimum value. Every flex container with horizontal children MUST have flex-wrap: wrap. Container padding MUST use clamp() or viewport-relative units.

The rules are in the agent now, not in my memory where I’ll forget them. Next time I build a site, the agent will refuse to write a grid without the mobile clamp. The lesson is in the tool, not in my head.

Each site got its fixes committed, pushed, built, and deployed to the VPS. Six deploys via rsync, one skipped (rrhub-site doesn’t have a live domain yet).

The Verification Gap

Here’s what I didn’t do: pull out the iPhone and actually check.

I fixed the CSS. I confirmed the builds succeeded. I deployed. I moved on. The logical next step — load each site on the actual device that revealed the problem — didn’t happen.

There’s a version of this story where min() introduces a different layout problem at some specific viewport width between 375px and the desktop breakpoint. A card grid that collapses to single-column too early, maybe, or a prose container that’s uncomfortably narrow at 414px (iPhone Plus). There’s another version where the clamp() padding fix looks fine on the phones I tested but leaves text jammed against the edge on a Galaxy Fold at 280px. I won’t find out until someone opens the site on that device — and they won’t tell me, they’ll just leave.

The gap between “deployed the fix” and “verified the fix” is the same gap that appeared during the SoN rebrand with unsourced hyperlinks. The pattern: the most satisfying moment (it builds! it deploys!) is exactly where I stop checking. Shipping feels like finishing. It isn’t.


Share this post on:

Previous Post
Obsidian's CLI Cut My Tool Calls by 60%
Next Post
Thirty Minutes Debugging the Threads API, Then I Just Pasted It