fix(site): rebuild the page layout on one grid, and close the owner's feedback pass - #145
Open
tnunamak wants to merge 21 commits into
Open
fix(site): rebuild the page layout on one grid, and close the owner's feedback pass#145tnunamak wants to merge 21 commits into
tnunamak wants to merge 21 commits into
Conversation
Seven things, all verified against rendered pixels rather than source. ONE TEXT MEASURE. Body prose was 544px on the concept pages, 612px on the specification, and one-offs at 952px and 680px, which is why line wrapping read as inconsistent from page to page. --container-measure is now a single 600px rebound under [data-surface="concept"], inside the 60-75 character convention for both surfaces' fonts. The container, the rail and the grid are untouched. FLY.IO AND RAILWAY WERE ILLEGIBLE. Those two tabs carry prose rather than a command, and it rendered on the dark code ground at 1.39:1 against a 4.5 requirement. Prose that is not a command now sits on the page background at 8.1:1. The Docker tabs are unchanged at 7.31:1. ONE WEIGHT IN THE CODE BLOCK. The image line rendered at 600 while every other line was 400, which read as a font bug rather than emphasis. The block is one weight now. DESKTOP HAD NO WAY TO MOVE AROUND THE SPECIFICATION. tableOfContent was disabled on the strength of a comment claiming the per-page contents lived in the left rail; the rail lists documents, not sections, so there was no section navigation at all on a 113,000px page. The fumadocs TOC column is live at 1280px and up. Finding that uncovered a real cascade bug. The spec route pulls two independent Tailwind builds, and the concept build's unconditional .flex utility loads after fumadocs' chunk, so fumadocs' own max-xl:hidden lost at every width. The TOC was never actually hidden below 1280px, only collapsed to a 16px sliver with leaking text. Fixed with an unlayered rule that cannot race the same way. MOBILE HEADER ROW. The sidebar drawer icon floated alone at the right edge above the section dropdown. The two rows now match height and share one seam, and the icon sits above the dropdown's own leading icon. THE DOCUMENT TREE. "Profile" now reads "Collection Profile". Reference Topology, Reference Implementation Notes, Auth Design and Change Tracking come off the specification rail onto /specification/reference-materials, which carries noindex, is disallowed in robots.txt, is absent from the sitemap and is linked from nowhere. Every one of those documents keeps its URL. DATES CANNOT SILENTLY ROT AGAIN. The site already derived them from the spec-*.md headers; the headers themselves were stale, spec-core by 130 days and all ten by some margin. `pnpm spec:dates` fails when a declared Date is older than the last substantive commit to that file, where substantive excludes header-only and whitespace-only diffs so stamping cannot retrigger itself. `--write` stamps them in one command. Wired into lefthook and the spec-check workflow, whose checkout needed fetch-depth 0 or the check would have silently passed on a shallow clone. The ten stale dates are reported, not rewritten: restamping a normative document is the owner's call. Verified: 196/196 tests, typecheck clean, spec:check and generated-artifacts clean, no horizontal overflow at 320/390/768/1024/1280/1440/1920 across five routes. Signed-off-by: Tim Nunamaker <tnunamak@gmail.com> Assisted-by: AI
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
All ten root specs carried a Date older than their last substantive commit, spec-core by 130 days. Stamped to 2026-08-14. Stamping surfaced two defects in the check itself, both found by running it rather than by reading it: The site renders each spec through a committed header sidecar that repeats the root's Date inside a Callout, and spec-check.ts compares the two. Stamping only the root traded a red spec:dates for a red spec:check. --write now mirrors into the sidecar. And the stamp replaced the whole Date line, which destroyed the editorial tails several of these carry: "(revised from 2026-03-30)", "(original); superseded 2026-04-12". That is real provenance, and spec-check compares the full string, so dropping it both lost information and failed the comparison. Only the ISO date is replaced now, on both sides. Signed-off-by: Tim Nunamaker <tnunamak@gmail.com> Assisted-by: AI
Five things, each checked against a screenshot rather than a number. The last round passed a contrast measurement while shipping a visibly broken panel, which is the failure this commit is mostly correcting. THE FAVICON was still teal while the rest of the site had moved to blue. It was the first thing asked for and it was missed entirely. It now carries the same two-tone mark the masthead, apple icon and OG card already ship. No background plate. Research found the shops split two ways: Stripe and Linear fill the canvas with an opaque brand plate, while GitHub, Vercel, Anthropic and Notion go bare but drop to near-black or near-white to survive tab chrome. A flat saturated hue cannot clear 3:1 on both light and dark chrome — brightening the blue for Chrome dark breaks it on Chrome light every time. But a two-tone mark only needs one tone to carry: copper holds the light chromes at 4.17 to 5.20, blue holds the dark ones at 2.62 to 3.71. Rendered at 16, 32 and 48px on real tab-strip colours and looked at: the counter stays open at 16px and the glyph reads on both. THE PROSE PANEL WAS NESTED INSIDE THE CODE PANEL. Fixing the Fly.io and Railway contrast by giving the paragraph its own light background left a 600px light box inside a 726px dark one, so a thick dark frame showed around it. The panel itself is now light when a tab carries prose instead of a command; Docker and Docker Compose keep the dark code ground. ENABLING THE TABLE OF CONTENTS SQUEEZED THE SPECIFICATION. Rail plus TOC plus prose no longer fit the container, and body prose fell to 488px against the 600px site measure. Prose is back to 600px. THE TOC WAS BIGGER THAN THE RAIL IT SITS OPPOSITE. Measured 15px on 24px against the rail's 13px on 17.55px. An earlier reading of "15px on both" came from sampling the rail's first link, which is its GitHub header rather than a nav item. The TOC now matches the rail exactly. THE DOCKER COMMAND keeps its line breaks: the single-line form measures 713px against 594px of available width, so collapsing it would force a horizontal scrollbar. The real defect was that the image line was not indented to match the other continuations. It is now. Verified: 196/196 tests, typecheck clean, spec:check and spec:dates pass, no horizontal overflow across seven widths and five routes. Signed-off-by: Tim Nunamaker <tnunamak@gmail.com> Assisted-by: AI
…yout Four things, each looked at rather than measured and assumed. THE FAVICON is the production single-p mark on a deep blue plate. Teal was the old colour and is gone; the two-tone split-p is gone with it. The plate is the answer to a question worth recording. No flat colour clears 3:1 on both light and dark tab chrome: the old teal managed 2.84 on Chrome light, and every candidate blue, grey or near-black failed at least one real chrome. A plate makes glyph-vs-plate contrast fixed and chrome-independent, at 8.18:1 here. That is also what the shops do. Fetching and decoding their actual favicon files shows Stripe and Linear ship an opaque brand plate with a neutral glyph, while GitHub, Vercel, Anthropic and Notion go bare but drop to near-black or near-white and give up brand colour. Nobody ships a bare saturated brand hue, which is what we were doing. DARK MODE WAS BROKEN ON THE SPECIFICATION and the body never left light mode while every child switched. The provider set only `data-theme`, and fumadocs reads a plain `.dark` class, so its own body and sidebar colours stayed light under dark content. The provider now sets both. THE FLY.IO AND RAILWAY COPY described settings that are not on screen. Reading the deploy files, both sentences were also wrong on the facts. Railway's template prompts for exactly one value, a password, and generates the database, the encryption key and the public address itself. Fly is not "more setup than fits in one command" at all: it is a single fly launch, and the real blocker is that Fly requires a payment method on the account before it will finish creating the app. Both now say the true thing. THE SPECIFICATION WAS SQUEEZED because the rails and the prose competed for one 1080px container. The rails now sit near the page edges, which is the pattern Cloudflare uses on a comparably long technical reference: it pins both rails to the viewport and measures only the prose, so adding a table of contents costs the article nothing. Better Stack and Dub keep a container, but at 1130px and 1152px, both wider than ours was. Prose holds the 600px site measure at every width from 1280 up. Verified: 196/196 tests, typecheck clean, spec:check, spec:dates and generated-artifacts all pass, and no horizontal overflow across seven widths, five routes and both colour schemes. Signed-off-by: Tim Nunamaker <tnunamak@gmail.com> Assisted-by: AI
The article's right edge moved as the reader scrolled. Prose held its measure while other blocks did not: at 1440 diagrams ran 838px against 600, and at 2560 they ran 1958px against 720. Mermaid renders into a bare centring div with no width of its own, and the measure was applied to a list of prose tags that never included it. Every direct child of the doc body now shares the measure, and an SVG scales down inside it rather than the box growing to fit. Measured from 360 to 2560: blocks and prose are the same width at every step from 768 up. The measure is also centred in its column now. It was pinned left, so the gap either side ran 296px apart at 1440 and 776px at 1920, and the whole document hugged the rail with a void beside it. Cloudflare's docs, which this layout follows, stay within 12px of centre at both widths. Content types, decided against that same reference rather than by taste: prose and code share one edge there (648 and 646 at 1440, 784 and 782 at 2560), and their measure grows with the viewport before capping around 780 rather than pinning to one value or growing without limit. Ours now grows to 720px above 1600. Code blocks keep wrapping instead of scrolling sideways, which is where we depart from them: they set white-space: pre and scroll. A normative spec is read rather than copied line by line, and a horizontal scrollbar hides half of the line a reader needs. It costs height - our tallest block is 4662px - but that block is 197 lines with a longest line of 152 characters, so most of the height is content rather than wrapping, and it would still be around 2900px unwrapped. No height cap either: a max-height on a 197-line sample puts a scrollbar inside a scrollbar. The favicon glyph also sat at 73% of its plate, above the 55-70% a plated mark normally occupies, and read as cramped against the edges. It is 66% now, on the site favicon, the console favicon and the app icon. Verified: 196/196 tests, typecheck clean, and no horizontal overflow across 100 combinations of ten widths from 320 to 2560, five routes, and both colour schemes. Signed-off-by: Tim Nunamaker <tnunamak@gmail.com> Assisted-by: AI
The article's horizontal edges were set per element - prose here, tables there, the hero somewhere else - and had drifted apart: measured at 1920, the table started at 525, the prose at 595 and the hero at 703. Replace that with a single named grid on the document. Prose occupies a `measure` track; tables take a small symmetric outset from it. Nothing carries its own centring, so the reading column stays optically centred between the two rails at every viewport (gap skew is now 0px at 1280, 1440, 1920 and 2560; it was 286px and 766px before). The root cause of the skew was #nd-page's padding-inline-start: 48px against padding-inline-end: 0. Every centred descendant inherited a column 48px narrower on one side. Table width is measured against shipped docs sites rather than assumed: Stripe holds tables at the prose width exactly (797 vs 799 at 1440), GitHub likewise (720/720), and MDN - the only one of four that outsets - goes 77px wider. An earlier revision here gave tables a 140px break, wider than any of them; this uses 80px total and goes flush below 1024. Also fix both panes lurching 22px on first scroll: --fd-banner-height was 3rem while the real masthead measures 70px, so the rail and TOC sat below the header at rest and snapped to the declared offset once scrolled. The masthead now publishes its measured height. Assisted-by: AI Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
…rail The four reference documents - implementation notes, change tracking, auth design and reference topology - were moved off the specification rail onto a noindex index page a reader has to navigate to by hand. Only that index was actually hidden. All four documents it links were fully indexable and listed in the sitemap, so a crawler that never saw the index still found, crawled and ranked every one of them. Give the documents the same treatment as their index: noindex/nofollow in generateMetadata, a robots.txt disallow, and no sitemap entry. All three are derived from REFERENCE_MATERIALS_SLUGS rather than repeated, so the rail and the crawl policy cannot drift apart. The pages stay live at their URLs, which is the point - they are reachable, just not competing with the specification. The existing suite passed throughout because the fixtures contained only the index page, never the four documents. Adding them to the fixture makes the leak reproducible: reverting either fix now fails the suite (verified - 2 tests red before, 5 green after). Assisted-by: AI Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
A 1px border on the sidebar's trailing edge was added to make the wide gutters read as structure. Surveying shipped docs UIs shows two coherent patterns and this was neither: a rail on a FILLED panel whose edge a line terminates (Exa, Base44, GitBook, Cloudflare), or a transparent rail separated by whitespace and type weight alone (Mintlify, OpenAI Platform). This rail is transparent, so the border was a line with nothing behind it. It also made /specification inconsistent with /self-host and /participate, which carry the same rail with no line. Assisted-by: AI Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
Table rules now use one selector shape (.pdpp-docs-body > .pdpp-docs-table-scroll) across the grid rule, the scrollport rule and the phone override, so the later ones stay genuine overrides rather than lower-specificity rules that happen to come after a higher one — a real ordering hazard, not only a lint error. Also drops a dead null-guard on the masthead ref (the <header> is rendered unconditionally, so the ref is always populated by the time the effect runs) and renames a shadowed loop variable. Assisted-by: AI Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
tnunamak
force-pushed
the
fix/site-feedback-0807
branch
from
August 14, 2026 22:53
069d7bb to
4eaf0d2
Compare
The dark-mode fix earlier in this branch made the shared provider write both `data-theme` and a plain `class`, because fumadocs' own components key their dark styling off `.dark` while the brand tokens read the data attribute. Console and site each keep a copy of the assertion against that one shared provider file, and only the site's copy was updated - so the console's still pinned the single-attribute form and failed CI (1 of 1772 tests). Match it to the provider as shipped. Assisted-by: AI Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
Option B, for comparison against the current single-column layout. /self-host and /participate capped at a fixed 1080px with no upper relief, so past ~1600px they stopped using the screen: at 2560 the shell held 1080 of 2560px while /specification spanned 2486, and clicking between them moved the whole page including the masthead. Measured on shipped sites 2026-08-14: none caps this way at 1920 — Tailscale's community page and Proton's both run the full 1920, Vercel's docs 1905, Tailscale's docs 1680. Community pages that carry label-plus-sentence entries (Proton, Vercel, Tailscale) put them in multi-column grids rather than a single stack. So: the container gains relief above 1600px, and .pdpp-features and the plain .pdpp-ruled-list go to two columns above 900px. The numbered ruled-list variant stays single-column — columns would break the reading order of ordered steps. Last-row rules are suppressed per row, not per last child, which no longer means the same thing in a grid. Wide blocks are bounded on the shared document track: capping tables individually does nothing, since they carry max-width:100% of a section that is already narrower. Right-edge spread at 1920 is 300px (prose 600 against figures 900 - the intended prose/figure relationship) rather than the 448px an unbounded version produced. Assisted-by: AI Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
/self-host and /participate capped at a fixed 1080px with no upper relief, so past ~1600px they stopped using the screen: at 2560 the shell held 1080 of 2560px while /specification spanned 2486, and clicking between them moved the whole page including the masthead. Surveyed 24 protocol/standards and open-source infrastructure pages at 1920 (W3C, IETF, MCP, Matrix, OpenAPI, OAuth.net, ActivityPub, OpenTelemetry, CNCF, Kubernetes, Rust, Deno, Astro, SQLite, Let's Encrypt, Node.js and others). Only 2 of the 16 that cap at all cap at or below 1080; median 1312, and eight run the full viewport. 1400 sits inside that band, and buys margin rather than line length - the reading measure is unchanged. The same survey settled a question this commit deliberately does NOT act on. An earlier draft put the heading-plus-sentence lists into two columns. Multi-column blocks in this class are overwhelmingly link cards (17 of 24 found), and the standards bodies keep comparable prose lists in a single column even with room for two: IETF participate runs 4 items at 17 words in a 1300px block, Kubernetes contribute 7 items at 28 words in 1204px, OpenTelemetry a 19-item list at 1220px. Our lists are 13-17 words per item with one link between them; .pdpp-channels, which IS all links, is already a row. So the lists stay stacked. Wide blocks are bounded on the shared document track, because capping tables individually does nothing - they carry max-width:100% of a section that is already narrower. Assisted-by: AI Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
The figure bound was 900px against a 600px measure, putting tables at exactly 1.50x prose and code at 1.28x. Surveyed 24 protocol/standards and docs sites at 1920 (W3C, IETF, ActivityPub, Matrix, MCP, OpenAPI, OAuth.net, OpenTelemetry, Kubernetes, Rust, Deno, Astro, SQLite, Node.js, Cloudflare, Stripe, GitHub, MDN, Better Stack, Django, PostgreSQL, Terraform): table width is a median 1.00x of prose (n=6, max 1.50x), code 1.00x (n=11, max 1.25x), images 0.76x (n=7, never wider than prose). Nothing exceeded 1.50x anywhere. So the old value sat at the ceiling of the observed range, matched only by Terraform, while flush is the modal answer - GitHub holds 135 code blocks at exactly prose width, PostgreSQL 37, and Deno, Node.js and OpenTelemetry likewise. 720 puts tables at 1.20x and code at 0.98x, inside the band at every viewport. Right-edge spread on these pages drops from 300px to 120px, and that residual is now the intended prose/figure relationship rather than an unexamined value. /specification was already inside the band (tables 1.11x, code 0.92x, diagrams 0.62x) and is unchanged. Assisted-by: AI Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
The specification surface went full-bleed while the marketing pages sat in a container, so the wordmark moved 575px between them at 2560 and the spec rail floated at x=32 against its own wordmark at x=607. The full-bleed override was justified in a comment as "Cloudflare Docs' pattern — both rails pinned to the viewport." That was a measurement error: it read Cloudflare's rail CONTAINER, which is full-bleed, not the link text inside it, which sits at x=16 against a wordmark at x=24. Re-measured across 11 docs sites that have a rail, 9 align the rail's link text to the wordmark within 16px (Cloudflare 24/16, Stripe 4/12, Deno 18/18, Astro 24/16, MCP 272/256, GitHub 16/24, Terraform 24/16, OpenTelemetry 12/21, Better Stack 405/391) and NONE pins rail text to the viewport edge. So every page now uses one grid: the left column carries whatever that page needs (spec front-matter and document list, or a Contents list), the right column carries a TOC only where there is a document to index, and the wordmark, rail and content all open on the same x everywhere. Aligning the rail means the rails cost the reading column real width (centre track = container - 480px), which squeezed the measure to 440px at 1440 - under the 60-75ch convention and narrower than any of the 24 sites surveyed. That is paid for by raising the SHARED container to 1560 rather than a docs-only override, because a docs-only widening reintroduces the moving masthead: Better Stack, Tailscale, OpenTelemetry, MCP and Kubernetes all keep one container across surfaces and hold the wordmark at an identical x on both. Measure is back to 600/720, gap skew stays 0 at every viewport, and every text element still shares one edge. Assisted-by: AI Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
An earlier commit on this branch replaced five dark-mode tokens while describing itself as a dark-mode repair. It was not one. The repair was in theme-provider.tsx, which now sets the `.dark` class fumadocs reads alongside data-theme; that alone made dark mode engage. The token change was separate and wrong. Callum's dark footer (PR #85) is a designed inversion — a LIGHT panel (#98bde1) carrying dark text (#17181a / #2c3138 / #495765), the reverse of light mode. It only looked broken because dark mode never engaged, so the panel rendered light-on-light. Replacing it with a conventional dark panel was not a fix, it overwrote the designer's intent, and it measured worse: his values give 9.05 / 6.67 / 3.78 against the replacement's 6.05 / 4.62 / 3.10. Every token in this file is now byte-identical to Callum's, verified by diff against 849ced2. Local light and dark footers now match production exactly; the only remaining difference is the body background going dark under dark mode, which is the class fix doing its job. Assisted-by: AI Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
…t rail No behaviour change from the previous commit — the container is 1560 for every surface as before. This records the two alternatives that were built, measured and reverted, so neither gets retried blind. The problem: /specification runs three tracks and needs 1560 to hold a 600px measure past 480px of rails, while the concept pages run two, so the surplus lands as ~705px of one-sided air at 1920 — wider than any of the twelve reference pages measured (322-615). Tried and reverted (1): move the concept contents rail to the right, as nine of ten short docs pages do. Outer edges balanced to 10px, but the gap moved to the middle of the page and the contents list detached from the heading it indexes, floating 200px above the title. Worse, not better. Tried and reverted (2): a narrower concept-only container. Balanced the air to 118px, but moved the masthead 190px between /specification and /self-host — the exact defect an earlier commit on this branch fixed. What the reference class actually does is keep ONE container and let the two-track page carry the extra air. Astro is the decisive case: a 2-track and a 3-track page from the same container, wordmark at an identical x on both (24/24), the 2-track page simply wider on the right (232 vs 315). Better Stack, MCP, OpenTelemetry and Kubernetes hold one wordmark position across surfaces too. Assisted-by: AI Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
… door
The wider container and the figure bound were both written for pages that
carry a rail, but they were scoped to the whole concept surface, so the
front door got them too. It has neither a rail nor a TOC — it is a single
centred hero — and the result was bad: the container went to 1560 and at
1440 exceeded the viewport, so the page started at x=0; the figure bound
capped the hero at 720px inside a 1080px container, stranding it in the
left half with ~690px of dead space; the data columns clipped mid-word
("artist Grou", "title Weath"); and the three CTAs wrapped onto two rows.
Both rules now require a rail to be present. The front door is back to
production's geometry exactly — container 175-1255 at 1440 and 415-1495 at
1920, document track 207-1223 at max-width 100%, three CTAs on one row —
verified by measuring both origins side by side.
Railed pages are unaffected: they still take 1560 above 1600px, and their
32px inset at 1440 sits mid-range against nine reference docs sites
(median 21, min 16 — Cloudflare, Astro, Deno and Terraform all at 16).
Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
…tainer The previous commit gated the wider container on `.container:has([data-slot=pdpp-concept-rail])`. That matches the page but not the masthead, which is its sibling — so on /self-host the document track widened while the nav stayed put, splitting the wordmark from the rail by 175px and breaking the single-masthead-position property an earlier commit established. Moving the :has() onto [data-surface="concept"], which contains both, makes them move together. Wordmark and rail are now aligned (delta 0) and identical across /specification, /self-host and /participate at 1440, 1920 and 2560, while the front door — which has no rail — keeps production's 1080 container. The two container rules are split and ordered low-specificity first: grouped, or written the other way round, they put a (0,3,0) selector after a (0,4,0) one, which Biome rejects and which is a real ordering hazard. They select disjoint surfaces, so neither overrides the other. Verified: 196/196 tests, typecheck clean, biome clean on every file this branch touches, and no overflow/HTTP/console errors across 40 page/viewport/mode combinations. Assisted-by: AI Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
…ction The previous commit justified the front door's 1080px container as "production's" width. That is the status quo, not a reference, so it got measured: of 16 protocol and infrastructure landing pages at 1920 on 2026-08-15, only OAuth.net (960) and Matrix (1000) cap at or below 1080. The median is 1280, hit exactly by Deno, Astro and Let's Encrypt, with Kubernetes 1200, Rust 1152, W3C 1312, IETF 1300, OpenTelemetry 1320, Tailscale 1440, MCP 1472, Terraform 1600 and SQLite uncapped. Our hero lede was correspondingly narrow at 564px against a 704px median. --container-page moves to 1280, the median. Raised on the token rather than per-page: a first attempt scoped it to the page container alone, which widened the hero but not the masthead and footer that share the class, leaving the hero hanging 100px OUTSIDE the chrome framing it. Masthead, hero and footer now hold one left edge on the front door (107/107/107 at 1440, 347/347/347 at 1920). The rationale lives in the file header because that file's own contract forbids comments inside @theme, and site-surface-ownership.test.ts pins the value to keep it declared there rather than as a runtime primitive — that pin is updated with a note that it guards the location, not the number. Railed surfaces are unaffected: they override to 1560 in components.css. Assisted-by: AI Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
The railed-surface override at 1560 was justified by a measurement of docs-vs-docs pairs. The real question was landing-vs-docs, and measured across eleven such pairs on 2026-08-15 the answer is different: every same-origin site holds the masthead at an identical x (Kubernetes, Tailscale, OpenTelemetry, Let's Encrypt, MCP, Better Stack; Matrix differs by 8px), and the only four that move it — Deno, Astro, Cloudflare, Stripe — serve docs from a separate subdomain, so it is not a shift any reader experiences inside one site. pdpp.dev is one origin with one masthead component, so ours should not move. It was moving 75px. Two narrower fixes were built and reverted first. Keeping the railed override aligned the rail to the wordmark but left the masthead moving. Pinning the chrome back to the landing width fixed the masthead but pushed the rail 75px outside it. Both are false trades: the three same-origin sites that hold one masthead position also align rail to wordmark within 16px, because a single container drives both. Dropping the override entirely holds all three properties at once — one masthead position, rail aligned to the wordmark, gap skew 0 — at the cost of a 640px measure at 1920 instead of 720. That is inside the class (Better Stack 616 on a comparable three-track page, GitHub 720, Cloudflare 784, OpenTelemetry 976), and the rail-to-TOC span of 1216px sits between Better Stack's 1146 and MCP's 1408. Verified: 196/196 tests, typecheck clean, biome clean on every file this branch touches, no overflow/HTTP/console errors across 40 page/viewport/mode combinations. Assisted-by: AI Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
Three defects in the front door's ambient record columns, all visible in the running build. MOTION was 41s / 33s / 22s assigned by column index, so the columns sped up left to right and the eye read one mechanism with a speed dial rather than three independent streams — the outer columns differed by nearly 2x. Durations are now 31s / 43s / 37s: a narrower band, prime-ish so the columns drift in and out of phase instead of settling into a repeating pattern, and deliberately not ordered by index, with the slowest in the middle. Each column also starts part-way through its own cycle via a negative animation-delay; without that all three begin perfectly aligned on load, which is the most mechanical moment of all. DUPLICATES: the component renders 17 rows per column and cycles with `rowIndex % stream.length`, but every stream held only 8 rows, so rows 1-8 repeated as 9-16 and the reader saw "title Weather" twice a few lines apart. Each stream is now 17 rows of real fields from the specification's worked examples. OVERFLOW: at 600px "artist Stars of the Lid" (23 characters) painted over the neighbouring column. The column clipped its own box but the row was an unbounded nowrap line. Rows now clip with an ellipsis, and the data keeps pairs at or under 19 characters so that path is a backstop rather than the norm. Verified across 375/600/1024/1440/1920: zero adjacent duplicates and zero rows painting past their column, where before both were present. Assisted-by: AI Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the owner's feedback pass on the public site, and rebuilds the page
layout on measured evidence rather than taste.
The layout work started from a real defect: the article's horizontal edges were
set per element, so at 1920 the table began at 525, the prose at 595 and the
hero at 703 — three ragged edges down one page. That is now one named grid.
Prose holds a measure track, figures take a bounded outset, and nothing carries
its own centring, so the reading column stays optically centred between the
rails at every viewport (gap skew is 0px at 1280/1440/1920/2560; it was 286 and
766). Root cause was
#nd-pageshippingpadding-inline-start: 48pxagainstpadding-inline-end: 0.Every page now uses one layout: wordmark, rail and content open on the same x,
the left column carries whatever that page needs, and the right column carries
a section TOC only where there is a document to index.
Review notes
Callum — one thing to check first. An earlier commit on this branch
replaced five of your dark footer tokens while calling itself a dark-mode
repair. It was not one: the repair was the
.darkclass intheme-provider.tsx, which is what made your inverted footer (light panel,
dark text) finally render as designed. The token change was reverted in
bd69724 and the file is now byte-identical to 849ced2. Your values also
measured better — 9.05/6.67/3.78 against the replacement's 6.05/4.62/3.10.
Widths are measured, not chosen. 24 protocol/standards and docs sites were
measured live at 1920 (W3C, IETF, MCP, Matrix, OpenAPI, OAuth.net,
ActivityPub, OpenTelemetry, CNCF, Kubernetes, Rust, Deno, Astro, SQLite,
Let's Encrypt, Node.js, Cloudflare, Stripe, GitHub, MDN, Better Stack, Django,
PostgreSQL, Terraform). Only 2 of the 16 that cap at all cap at or below our
old 1080; median is 1312. Figures do not outset: table width is a median
1.00x of prose, code 1.00x, images 0.76x, and nothing anywhere exceeded 1.50x —
so the concept figure bound came down from 900 to 720. Full data is in the
dotfiles research corpus.
Two alternatives were built, measured and reverted, and the reasons are in
the CSS so they don't get retried blind: moving the concept contents rail to
the right (balanced the outer edges but stranded the gap mid-page and detached
the list from its heading), and a concept-only container (balanced the air but
moved the masthead 190px between pages). The reference class keeps one
container and lets the two-track page carry the extra air — Astro serves a
2-track and a 3-track page from one container with the wordmark identical on
both.
One container drives the whole site. The final shape is a single
--container-pagewith no per-surface override, because that is what holdsthree properties at once that narrower schemes kept trading against each
other: the masthead sits at the same x on every page, the rail's links line up
with the wordmark, and the measure stays centred. Two narrower schemes were
built and reverted first (a railed-only 1560 override, then pinning the chrome
back to the landing width) — each fixed one property and broke another. The
deciding measurement was landing-vs-docs pairs, not docs-vs-docs: of eleven,
every same-origin site holds one masthead position, and the only four that
move it serve docs from a separate subdomain, which is not a shift a reader
experiences inside one site. Cost: a 640px measure at 1920 rather than 720,
inside the class (Better Stack 616 on a comparable three-track page, GitHub
720, Cloudflare 784, OpenTelemetry 976).
The landing width came from the class, not from production. 1080 was the
status quo, not a reference: of 16 protocol and infra landing pages, only two
cap at or below it and the median is 1280, which is what this uses.
The front door needed scoping, and I missed it on the first pass. The
wider container and the figure bound were written for railed pages but scoped
to the whole concept surface, so the home page — which has no rail — got them
too: at 1440 the container exceeded the viewport and the page started at x=0,
the hero was squeezed into the left 720px of a 1080px container, the data
columns clipped mid-word, and the CTA row wrapped. Both rules now require a
rail. The front door is back to production's geometry exactly, verified by
measuring both origins side by side.
A correction worth stating. The full-bleed spec grid was justified in a
comment as "Cloudflare pins both rails to the viewport." That was a measurement
error — it read their rail container, not the link text inside it, which sits
at x=16 against a wordmark at x=24. Of 11 docs sites with a rail, 9 align rail
text to the wordmark and none pins it to the viewport edge.
Owner feedback, all verified against the running build: favicon on a deep
blue plate; dark mode fixed; grant-line copy; dates wired to spec-core.md and
MAINTAINERS.md through prebuild (verified live by editing MAINTAINERS.md and
watching the generated file follow); "Implement PDPP" retired; Fly.io and
Railway panels legible with corrected copy; Docker line breaks; "Collection
Profile"; 90-entry section nav on desktop and a merged control row on mobile;
the four reference documents moved off the rail to a noindex page that is also
absent from the sitemap and disallowed in robots.txt — that last one was a real
leak, since only the index had been hidden while all four documents stayed
crawlable.
Known and unfixed: search failures are intermittent and I could not
reproduce them, so nothing was changed there.
Verified: 13/13 CI checks green, 196/196 site tests, spec:check and
spec:dates pass, no overflow/HTTP/console errors across 40
page/viewport/mode combinations, and every page reviewed as a screenshot at
375/1440/2560 rather than only swept for errors.
Assisted-by: AI