Build write-up · 3 min read
How I built this site
Twenty-seven pages of plain HTML, no framework and no build step, held to four written specs by twelve checks that measure the rendered page. It’s the closest thing to a production codebase I own.
- Role
- Everything: the design, the code, the checks that hold them together
- Size
- 27 pages, 3 token files, 4 specs, 41 scripts
- Status
- Live. Every merge deploys; there is no staging in front of it
My approach
-
01Tokens
- Three token files: color, type, and the page shell
- Four specs that are the rule, not the record of one
- A fourteen-step type scale and four fluid ramps between its steps
-
02Pages
- Hand-written HTML with the shell copied in, not included
- A third of the markup is comments saying why
- One generated file, and a check that rebuilds and diffs it
-
03Checks
- Twelve checks; six of them render pages in a real browser
- Contrast measured on the resting page and in every forced state
- Every number the specs assert, recounted from the tree
-
04Ship
- A merge is live in about a minute, with nothing in between
- Guards that refuse rather than warn
- AI wrote most of the scripts, to rules that came from failures I found
The problem
A portfolio can claim design systems, accessibility, and real code and still sit on a template with a contrast failure in the hero. Then it says the opposite. So this site had to be built to the standard it claims for the work.
That standard is three things I ask of any production codebase:
- The rules are written down
- The page is measured against them, not eyeballed
- A change that breaks a rule is refused before a stranger sees it
Tokens first, and the specs are the rule
Three files hold every value the site may use: the colors, the type scale, and the spacing and radii of the page shell. A color that isn’t in the palette isn’t a color.
Four specs sit over those files: typography, color, spacing, and motion. When a spec and the stylesheet disagree, one of them is a bug, and all four have been found stale at least once.
About a third of the markup is comments saying why: what was tried, what it measured, and what sent it back. I think a rule with no reason attached is the first thing the next change deletes.
The design system itself, every value read liveWhat the checks measure
Twelve checks run on every merge. Six read the tree and finish in about a second.
The other six open the pages in Chrome and measure what rendered: every color a reader can see, every forced state, and every type size against the scale.
One prototype page draws its key states from JavaScript, only when a reader presses something. For a while, every check said the page passed while measuring a sixth of it.
So a registry now names every state a reader can reach, and each check presses its way into every one. A selector that matches nothing fails the run. Its first pass found a live hover at 1.29:1.
I watch the counts as closely as the verdicts. The state check once passed clean at 416 rules and at 617, and the gap was a third of the site going unmeasured.
What AI actually did
An AI working in a terminal wrote most of the scripts, to rules I set. Nearly every rule came from a failure I found first:
- The pixel-reading contrast check exists because a caption shipped at 3.88:1 and the ordinary check could not see it
- The state registry exists because a page passed on a sixth of itself
- The token check exists because the specs drifted from the stylesheet three times before anyone read them against it
What that changed isn’t who decides. It’s how fast a bad idea can be shown to be bad. A rule I’d once have written down and hoped to remember is now a script that refuses.
The design is mine at every step: the scale, the palette, the order of the page, and every sentence. So is the judgment about what a check should hold the site to.
This way of working has a cost. The site is slower to change than a template, because a change that moves a number has to move the sentence that states it.
For a site whose subject is checking a claim everywhere it’s made, I think that’s the right price.