Blame · Line-by-line history
widen-layout.ts
Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.
| a6dc91c | 1 | /** |
| 2 | * One-shot layout-widening sweep. | |
| 3 | * | |
| 4 | * The site felt "thin": `main` allowed 1440px but nearly every page wrapped its | |
| 5 | * content in a much narrower centered column (880–1180px), leaving big empty | |
| 6 | * gutters on modern wide screens. This script widens the per-page container | |
| 7 | * declarations into consistent tiers so the whole site uses the available | |
| 8 | * width the way GitHub / Linear / Vercel do. | |
| 9 | * | |
| 10 | * It is deliberately conservative: it only rewrites a CSS declaration line when | |
| 11 | * that line is unmistakably a page-level container — i.e. the selector is a | |
| 12 | * class whose name ends in `-wrap`, `-container`, or `-page`, AND the same line | |
| 13 | * sets both `max-width: <N>px` and `margin: 0 auto`. Hero-inner / sub-heading / | |
| 14 | * media-query widths never match (no `margin: 0 auto`), so prose measure and | |
| 15 | * narrow confirm dialogs are left intact. | |
| 16 | * | |
| 17 | * Tiers (old -> new): | |
| 18 | * >= 1000px -> 1320 (dashboards, lists, tables, admin, explore, insights) | |
| 19 | * 900..999 -> 1120 (medium / form-heavy pages) | |
| 20 | * 820..899 -> 1040 (settings, repo-settings, import) | |
| 21 | * < 820 -> unchanged (intentionally small confirm / detail / claim views) | |
| 22 | * | |
| 23 | * Run once: `bun scripts/widen-layout.ts` | |
| 24 | */ | |
| 25 | ||
| 26 | import { Glob } from "bun"; | |
| 27 | ||
| 28 | const ROOT = new URL("..", import.meta.url).pathname; | |
| 29 | ||
| 30 | function widen(old: number): number | null { | |
| 31 | if (old >= 1000) return 1320; | |
| 32 | if (old >= 900) return 1120; | |
| 33 | if (old >= 820) return 1040; | |
| 34 | return null; // leave small intentional widths alone | |
| 35 | } | |
| 36 | ||
| 37 | // Matches a single-line page-container declaration, capturing the px width. | |
| 38 | // Example: ` .admin-wrap { max-width: 1080px; margin: 0 auto; }` | |
| 39 | const LINE_RE = | |
| 40 | /^(\s*\.[A-Za-z0-9_-]*(?:wrap|container|page)\b[^\n{]*\{[^\n}]*max-width:\s*)(\d+)(px[^\n]*margin:\s*0 auto[^\n]*)$/; | |
| 41 | ||
| 42 | const glob = new Glob("src/**/*.tsx"); | |
| 43 | let filesChanged = 0; | |
| 44 | let linesChanged = 0; | |
| 45 | ||
| 46 | for await (const rel of glob.scan({ cwd: ROOT })) { | |
| 47 | if (!rel.startsWith("src/routes/") && !rel.startsWith("src/views/")) continue; | |
| 48 | const path = ROOT + rel; | |
| 49 | const src = await Bun.file(path).text(); | |
| 50 | const lines = src.split("\n"); | |
| 51 | let touched = false; | |
| 52 | ||
| 53 | for (let i = 0; i < lines.length; i++) { | |
| 54 | const m = lines[i].match(LINE_RE); | |
| 55 | if (!m) continue; | |
| 56 | const old = parseInt(m[2], 10); | |
| 57 | const next = widen(old); | |
| 58 | if (next === null || next === old) continue; | |
| 59 | lines[i] = `${m[1]}${next}${m[3]}`; | |
| 60 | console.log(`${rel}:${i + 1} ${old}px -> ${next}px`); | |
| 61 | touched = true; | |
| 62 | linesChanged++; | |
| 63 | } | |
| 64 | ||
| 65 | if (touched) { | |
| 66 | await Bun.write(path, lines.join("\n")); | |
| 67 | filesChanged++; | |
| 68 | } | |
| 69 | } | |
| 70 | ||
| 71 | console.log(`\nDone: ${linesChanged} container(s) widened across ${filesChanged} file(s).`); |