feat(site): phase 1 — the foundation #4
Reference in New Issue
Block a user
No description provided.
Delete Branch "feat/phase-1-foundation"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Phase 1 of
PLAN.md§13: the Astro + Node adapter scaffold, the token file, typography, the layout shell, header/footer, the documentation theme and sidebar,platform.json, and the two check scripts.Stacked on #3 only conceptually — this branch is cut from
mainand merges independently in either order.The palette is sampled, not invented
§11 asks for gold and cyan "derived from the artwork by sampling, not guessed, and both held to WCAG AA against the ground". So that is literally what happened:
runic-emblem.pngwas decoded, its 494,059 opaque pixels binned by hue, and the mean of each bin taken.The ring turns out to be 65% of the emblem's saturated pixels, across hue 25–45°. The portal and its glow sit at 180–210°. Every value in
tokens.csscarries its measured contrast against#0e1318:--gold-deep#946b3c--gold#c8a368--gold-bright#e4cb90--portal-deep#0b6398--portal#15b4de--portal-bright#1bd6f1--danger#ff4e43Where a sampled value fails a ratio it is restricted rather than brightened, which is why the annotation doubles as the usage rule. The ground, panels, steel-blue accent and the live/maintenance signal colours are the product's own tokens, byte for byte, so a theme written for a Runic Gateway deployment is legible here.
The two checks, and proof that they bite
A check that has never failed is not a check, so both were deliberately broken before being trusted.
checkTokens.mjs— fails on any colour literal insrc/outside the token file. Verified it catches a hex value and a named colour dropped into an.astro<style>block, and that it does not flag the#0e1318that appears in its own explanatory comments (it strips comments per file type first — otherwise it would have been its own first false positive).checkFacts.mjs— re-reads all 14 externally-sourced facts from their authorities over the Gitea API. All 14 currently agree:Verified it fails when
platform.jsonis edited to claim protocol 3 — which is exactly the mistake §1 was written about, so that is the one regression test worth having. It reads the protocol from three independent sites (the sidecar'sPROTOCOL_VERSION, the overlay'soverlay.toml, the published bundle manifest) because a bump that lands in only some of them is the failure CLAUDE.md warns about.It also enforces D13: no email address anywhere in
src/orscripts/— the address lives only inbrand-default/brand.json, so replacing it later stays a file copy. Verified that one fires too, and it prints its own remedy rather than the version-mismatch advice.The token requirement is deliberate: anonymous raw fetches fail on this Gitea instance, and a fact check that silently skips itself would ship a stale version quietly. CI maps the existing org-level
REGISTRY_TOKENintoGITEA_TOKENfor that step — no new secret needed. The mapping lives in the workflow so the script keeps asking for what it actually wants, a Gitea token, rather than this org's particular secret name.Things found while building, worth knowing
output: 'server'with per-pageprerender = true. Astro 7 expresses that shape the other way round —output: 'static'with an adapter prerenders everything and lets individual routes opt out. Same runtime result, and the default is now the safe one: a page added without thinking is static, not accidentally server-rendered. The two routes that will opt out are phase 2's/brand/*and phase 5's/api/beta-signup. Flagging rather than silently deviating — say the word if you want the config written the other way.src/lib/brand.mjsdocuments a tension phase 2 has to resolve. §7 promises that changing the site name or the contact address is a mounted-file edit; §6 prerenders the pages, and a value read at build time is baked into the HTML where no mount can reach it. Assets are fine — they are served per request. Text is not. The three options are written down at the call site, in the order they are worth trying, so phase 2 does not discover it late. One of them contradicts §7 and would need your agreement rather than a quiet decision.<img>. It is drawn incurrentColorso it inherits--goldand follows a mountedtheme.cssfor free — but an SVG loaded through<img>is a separate document with nothing to inherit from, and it rendered black on black in the docs header. Starlight'sSiteTitleis overridden instead, which also means both chromes now carry one lockup..starlight-aside--*set the border and heading but left the background stock indigo — that comes from--sl-color-<hue>-low. Mapping the five scales fixes asides and everything else semantic in one place.127.0.0.1refuses,localhost/::1answer. Phase 12's container will needHOST=0.0.0.0explicitly.The bug CI caught, which local checks structurally could not
The first push went red, and it was a real defect rather than a CI quirk.
.gitignorecarrieddata/without a leading slash, and git matches such a pattern at any depth — so it silently swallowedsrc/data/platform.json, the single file every page andcheckFacts.mjsread.The working tree still had the file, so
npm run verifypassed locally with all 14 facts green. CI cloned fresh andastro checkfailed on the missing module. Both bind-mount patterns are now anchored to the repository root, and the.gitignorecomment says why.It is fixed and re-verified the way it should have been the first time:
git archive HEADinto a clean directory,npm ci, then the full CI sequence there — proving the commit rather than the tree that was hiding the problem. That is worth doing on any first commit that introduces a.gitignore.Verified
npm run verify— tokens, facts,astro check(0 errors across 14 files), production build, both in the working tree and against a clean export of the commit. Then served the built output and walked it in a browser:/,/docs/and a 404 all correct, Pagefind search index built, both typefaces served from our own origin, and no external requests of any kind in the emitted HTML, so §6'sdefault-src 'self'holds with nothing to argue about (D9). Light and dark both checked on the docs.Mobile layout and the accessibility pass are phase 10/11 and not claimed here.
AI-assisted contribution
Drafted by Claude (Claude Code), per the org's AI-usage disclosure policy. Commits carry the
Co-Authored-Bytrailer.