┌─ SCSS ─────────────────────────────────────────────────────────────────────┐
│ // Design tokens, reset, body. Component styles live next to each │
│ // component; page chrome is in layouts/ArticleLayout/ArticleLayout.scss. │
│ │
│ :root { │
│ // Palette │
│ --paper: #fafaf7; │
│ --ink: #111111; │
│ --ink-soft: #4a4a48; │
│ --backdrop: #e9e7df; │
│ │
│ // Type │
│ --mono: "IBM Plex Mono", ui-monospace, Menlo, monospace; │
│ // ASCII frame font - must be perfectly monospace for box-drawing. │
│ // Cascadia Mono ships with uniform glyph widths across letters, │
│ // digits, and box-drawing chars; IBM Plex Mono does not. │
│ --mono-frame: "Cascadia Mono", Menlo, Monaco, monospace; │
│ // Fluid scale: 13px on tablet+ down to 10px around 375px viewport. │
│ // Frame width is 86 cells, so the entire page (cells + paddings) scales │
│ // proportionally with --fs and stays aligned. │
│ --fs: clamp(10px, calc(7px + 0.8vw), 13px); │
│ --lh: 1.45; │
│ │
│ // Frame geometry - defaults to desktop (86 cells); the mobile media │
│ // query below flips it to 48 cells. Must stay in sync with FRAME_W / │
│ // MOBILE_FRAME_W in src/lib/config.ts. │
│ --frame-cells: 86; │
│ // Cascadia Mono "0" advance ? 7.617px at 13px font-size ? 0.586em. │
│ // Pinned to em (not 1ch) so prose (IBM Plex) and pre.ascii (Cascadia) │
│ // resolve to the exact same px width - keeps body paragraphs from │
│ // extending past the right edge of the ASCII rules. │
│ --cell-w: 0.586em; │
│ --cell-h: calc(var(--fs) * var(--lh)); │
│ --frame-w: calc(var(--frame-cells) * var(--cell-w)); │
│ │
│ // Page chrome - paddings in em so they scale with --fs. │
│ --page-pad-t: 2.15em; │
│ --page-pad-r: 2.75em; │
│ --page-pad-b: 2.5em; │
│ --page-pad-l: 4.3em; │
│ --punch-w: 2.15em; │
│ --page-w: calc(var(--frame-w) + var(--page-pad-l) + var(--page-pad-r)); │
│ } │
│ │
│ // Mobile: switch to the 48-cell frame, drop the punch column, and scale │
│ // the font so the frame plus a 1em gutter on each side fits the │
│ // viewport. 48 cells ? 0.586em/cell = 28.128em, plus 2em for gutters = │
│ // 30.128em total - solving fs ? 30.128em == 100vw gives the cap below. │
│ // 1em ? 12px at our mobile fs, matching the 12-16px gutter common to │
│ // Material / Apple / Tailwind. The max(...) padding falls back to │
│ // centering if the frame ever falls short of the viewport (e.g. near │
│ // the 700px breakpoint where fs hits the 15px cap). │
│ @media (max-width: 700px) { │
│ :root { │
│ --frame-cells: 48; │
│ --fs: min(15px, calc(100vw / 30.128)); │
│ --page-w: 100%; │
│ --page-pad-l: max(1em, calc((100vw - var(--frame-w)) / 2)); │
│ --page-pad-r: max(1em, calc((100vw - var(--frame-w)) / 2)); │
│ } │
│ } │
│ │
│ // Show only the matching frame variant. Components emit both wide and │
│ // narrow at build time; one is hidden per viewport. Print always gets │
│ // the wide frame - stated here in the media condition itself, so print │
│ // never needs to override display (a revert/!important there would │
│ // wipe the block display the rule spans rely on and un-clip them). │
│ @media (min-width: 701px), print { │
│ .frame-narrow { │
│ display: none !important; │
│ } │
│ } │
│ │
│ @media screen and (max-width: 700px) { │
│ .frame-wide { │
│ display: none !important; │
│ } │
│ } │
│ │
│ [data-theme="dark"] { │
│ --paper: #282828; │
│ --ink: #d9c7a5; │
│ --ink-soft: #d4be98; │
│ --backdrop: #32302f; │
│ } │
│ │
│ * { │
│ box-sizing: border-box; │
│ } │
│ │
│ // Reserve the scrollbar gutter on both edges: page height differences │
│ // otherwise shift the centered page (and the fixed docs card, whose │
│ // position math uses 100vw) sideways on every navigation. Symmetric │
│ // gutters keep (100vw - page) / 2 equal to the page's true left edge. │
│ // Screen only - paper has no scrollbars, and a reserved gutter would │
│ // skew the print width solve. │
│ @media screen { │
│ html { │
│ scrollbar-gutter: stable both-edges; │
│ } │
│ } │
│ │
│ // Kept in the DOM for screen readers (e.g. the semantic table behind an │
│ // ASCII-rendered one) but visually removed. │
│ .sr-only { │
│ position: absolute; │
│ width: 1px; │
│ height: 1px; │
│ padding: 0; │
│ margin: -1px; │
│ overflow: hidden; │
│ clip: rect(0, 0, 0, 0); │
│ white-space: nowrap; │
│ border: 0; │
│ } │
│ │
│ body { │
│ margin: 0; │
│ font-family: var(--mono); │
│ font-size: var(--fs); │
│ line-height: var(--lh); │
│ font-variant-numeric: tabular-nums lining-nums; │
│ background: var(--backdrop); │
│ color: var(--ink); │
│ display: flex; │
│ justify-content: center; │
│ } │
│ │
│ // Print: ink on paper, literally. Force the light palette and the wide │
│ // frame, drop the screen chrome (nav, search, punch column, toasts), │
│ // and keep frames from splitting across page breaks. │
│ @media print { │
│ :root, │
│ html[data-theme="dark"] { │
│ --paper: #fff; │
│ --ink: #000; │
│ --ink-soft: #444; │
│ --backdrop: #fff; │
│ --frame-cells: 86; │
│ // Same solve as the mobile query: 86 cells ? 0.586em + 1em gutter │
│ // each side = 52.4em, and fs ? 52.4 == 100vw makes the frame fill │
│ // the printable width exactly. In paged media 100vw is the page │
│ // area, so this adapts to letter, A4, or anything else. │
│ --fs: calc(100vw / 52.4); │
│ --page-pad-t: 1em; │
│ --page-pad-r: 1em; │
│ --page-pad-b: 1em; │
│ --page-pad-l: 1em; │
│ --page-w: 100%; │
│ } │
│ │
│ body { │
│ display: block; │
│ background: #fff; │
│ } │
│ │
│ .top-header, │
│ .article-pagination, │
│ .toast-region { │
│ display: none !important; │
│ } │
│ │
│ pre.ascii, │
│ .figure-frame, │
│ .table-frames, │
│ .code-frames, │
│ blockquote.callout { │
│ break-inside: avoid; │
│ } │
│ } │
└────────────────────────────────────────────────────────────────────────────────────┘
┌─ SCSS ───────────────────────────────┐
│ // Design tokens, reset, body. Component sty │
│ les live next to each │
│ // component; page chrome is in layouts/Arti │
│ cleLayout/ArticleLayout.scss. │
│ │
│ :root { │
│ // Palette │
│ --paper: #fafaf7; │
│ --ink: #111111; │
│ --ink-soft: #4a4a48; │
│ --backdrop: #e9e7df; │
│ │
│ // Type │
│ --mono: "IBM Plex Mono", ui-monospace, Men │
│ lo, monospace; │
│ // ASCII frame font - must be perfectly mo │
│ nospace for box-drawing. │
│ // Cascadia Mono ships with uniform glyph │
│ widths across letters, │
│ // digits, and box-drawing chars; IBM Plex │
│ Mono does not. │
│ --mono-frame: "Cascadia Mono", Menlo, Mona │
│ co, monospace; │
│ // Fluid scale: 13px on tablet+ down to 10 │
│ px around 375px viewport. │
│ // Frame width is 86 cells, so the entire │
│ page (cells + paddings) scales │
│ // proportionally with --fs and stays alig │
│ ned. │
│ --fs: clamp(10px, calc(7px + 0.8vw), 13px) │
│ ; │
│ --lh: 1.45; │
│ │
│ // Frame geometry - defaults to desktop (8 │
│ 6 cells); the mobile media │
│ // query below flips it to 48 cells. Must │
│ stay in sync with FRAME_W / │
│ // MOBILE_FRAME_W in src/lib/config.ts. │
│ --frame-cells: 86; │
│ // Cascadia Mono "0" advance ? 7.617px at │
│ 13px font-size ? 0.586em. │
│ // Pinned to em (not 1ch) so prose (IBM Pl │
│ ex) and pre.ascii (Cascadia) │
│ // resolve to the exact same px width - ke │
│ eps body paragraphs from │
│ // extending past the right edge of the AS │
│ CII rules. │
│ --cell-w: 0.586em; │
│ --cell-h: calc(var(--fs) * var(--lh)); │
│ --frame-w: calc(var(--frame-cells) * var(- │
│ -cell-w)); │
│ │
│ // Page chrome - paddings in em so they sc │
│ ale with --fs. │
│ --page-pad-t: 2.15em; │
│ --page-pad-r: 2.75em; │
│ --page-pad-b: 2.5em; │
│ --page-pad-l: 4.3em; │
│ --punch-w: 2.15em; │
│ --page-w: calc(var(--frame-w) + var(--page │
│ -pad-l) + var(--page-pad-r)); │
│ } │
│ │
│ // Mobile: switch to the 48-cell frame, drop │
│ the punch column, and scale │
│ // the font so the frame plus a 1em gutter o │
│ n each side fits the │
│ // viewport. 48 cells ? 0.586em/cell = 28.12 │
│ 8em, plus 2em for gutters = │
│ // 30.128em total - solving fs ? 30.128em == │
│ 100vw gives the cap below. │
│ // 1em ? 12px at our mobile fs, matching the │
│ 12-16px gutter common to │
│ // Material / Apple / Tailwind. The max(...) │
│ padding falls back to │
│ // centering if the frame ever falls short o │
│ f the viewport (e.g. near │
│ // the 700px breakpoint where fs hits the 15 │
│ px cap). │
│ @media (max-width: 700px) { │
│ :root { │
│ --frame-cells: 48; │
│ --fs: min(15px, calc(100vw / 30.128)); │
│ --page-w: 100%; │
│ --page-pad-l: max(1em, calc((100vw - var │
│ (--frame-w)) / 2)); │
│ --page-pad-r: max(1em, calc((100vw - var │
│ (--frame-w)) / 2)); │
│ } │
│ } │
│ │
│ // Show only the matching frame variant. Com │
│ ponents emit both wide and │
│ // narrow at build time; one is hidden per v │
│ iewport. Print always gets │
│ // the wide frame - stated here in the media │
│ condition itself, so print │
│ // never needs to override display (a revert │
│ /!important there would │
│ // wipe the block display the rule spans rel │
│ y on and un-clip them). │
│ @media (min-width: 701px), print { │
│ .frame-narrow { │
│ display: none !important; │
│ } │
│ } │
│ │
│ @media screen and (max-width: 700px) { │
│ .frame-wide { │
│ display: none !important; │
│ } │
│ } │
│ │
│ [data-theme="dark"] { │
│ --paper: #282828; │
│ --ink: #d9c7a5; │
│ --ink-soft: #d4be98; │
│ --backdrop: #32302f; │
│ } │
│ │
│ * { │
│ box-sizing: border-box; │
│ } │
│ │
│ // Reserve the scrollbar gutter on both edge │
│ s: page height differences │
│ // otherwise shift the centered page (and th │
│ e fixed docs card, whose │
│ // position math uses 100vw) sideways on eve │
│ ry navigation. Symmetric │
│ // gutters keep (100vw - page) / 2 equal to │
│ the page's true left edge. │
│ // Screen only - paper has no scrollbars, an │
│ d a reserved gutter would │
│ // skew the print width solve. │
│ @media screen { │
│ html { │
│ scrollbar-gutter: stable both-edges; │
│ } │
│ } │
│ │
│ // Kept in the DOM for screen readers (e.g. │
│ the semantic table behind an │
│ // ASCII-rendered one) but visually removed. │
│ .sr-only { │
│ position: absolute; │
│ width: 1px; │
│ height: 1px; │
│ padding: 0; │
│ margin: -1px; │
│ overflow: hidden; │
│ clip: rect(0, 0, 0, 0); │
│ white-space: nowrap; │
│ border: 0; │
│ } │
│ │
│ body { │
│ margin: 0; │
│ font-family: var(--mono); │
│ font-size: var(--fs); │
│ line-height: var(--lh); │
│ font-variant-numeric: tabular-nums lining- │
│ nums; │
│ background: var(--backdrop); │
│ color: var(--ink); │
│ display: flex; │
│ justify-content: center; │
│ } │
│ │
│ // Print: ink on paper, literally. Force the │
│ light palette and the wide │
│ // frame, drop the screen chrome (nav, searc │
│ h, punch column, toasts), │
│ // and keep frames from splitting across pag │
│ e breaks. │
│ @media print { │
│ :root, │
│ html[data-theme="dark"] { │
│ --paper: #fff; │
│ --ink: #000; │
│ --ink-soft: #444; │
│ --backdrop: #fff; │
│ --frame-cells: 86; │
│ // Same solve as the mobile query: 86 ce │
│ lls ? 0.586em + 1em gutter │
│ // each side = 52.4em, and fs ? 52.4 == │
│ 100vw makes the frame fill │
│ // the printable width exactly. In paged │
│ media 100vw is the page │
│ // area, so this adapts to letter, A4, o │
│ r anything else. │
│ --fs: calc(100vw / 52.4); │
│ --page-pad-t: 1em; │
│ --page-pad-r: 1em; │
│ --page-pad-b: 1em; │
│ --page-pad-l: 1em; │
│ --page-w: 100%; │
│ } │
│ │
│ body { │
│ display: block; │
│ background: #fff; │
│ } │
│ │
│ .top-header, │
│ .article-pagination, │
│ .toast-region { │
│ display: none !important; │
│ } │
│ │
│ pre.ascii, │
│ .figure-frame, │
│ .table-frames, │
│ .code-frames, │
│ blockquote.callout { │
│ break-inside: avoid; │
│ } │
│ } │
└──────────────────────────────────────────────┘