Contact
Web craft

The build check that passed while the editor broke

Four internal registries agreed. Twelve published pages still used a value Keystatic rejected.

4 minute read

An engineer removes a module from a machine while catalogued components lie beside it

Twelve published pages looked fine on the public site. Open one in Keystatic, though, and the editor refused it: tone="plate" was in the content but absent from that block’s list of allowed tones.

I had a green build check for this exact area. It compared the Keystatic schema, Astro renderer, component registry and editor catalogue; it never opened a page. All four definitions agreed with each other while the content disagreed with them.

I added plate to the two affected blocks, made the check read actual content from every site, then briefly restored the bad schema to see it fail. That last step mattered: another green result, without a failing reproduction, would have told me very little.

The failure

Tone on this site is a shared list of six surfaces — default, muted, accent, contrast, gradient and plate — defined once and used by every block that paints a background. Except that two blocks didn’t use the shared list. They’d been written with their own five-item lists, before plate existed, and nobody updated them when it was added.

Twelve pages used tone="plate" on one of those two blocks. The site rendered them perfectly: the renderer looks the tone up in a map, the map has all six, the CSS class exists. Every page built, every check passed, the pages looked right.

Then someone opened one in the editor and got this:

“Field validation failed: body: Unexpected error: Error: tone: Must be a valid option”

The published content used an option the schema did not allow. The site rendered correctly while Keystatic refused to open the page.

The missing option was a one-line fix. Both blocks now draw from the shared list, so that particular mismatch cannot recur. The more important change was to check:block-registry: after comparing the four registrations, it now reads every site’s MDX and verifies that values such as tone="plate" exist in the corresponding select field.

Other checks, with narrower claims

check:token-purity keeps colours in src/styles/tokens.css. It catches a literal colour in governed source before that one-off value spreads to a second component. The check knows where a colour may be declared; it has no opinion about whether the chosen colour looks good.

check:design-tokens does the arithmetic for foreground and background pairs in each preset and scheme. A pair below its WCAG AA threshold fails. I still open the page: gradients, image overlays, focus states and labels need checks that token arithmetic cannot perform.

The block registry check has a similarly limited job. It confirms that a Keystatic block has an Astro renderer and editor catalogue entry under the same name, then checks select values used by the content. It does not judge whether the block is useful or whether its copy is good.

The public site ships no client framework

The site is built with Astro. Keystatic uses React in the CMS build, but React is absent from the public output. Navigation, the table of contents and the theme toggle use small page-scoped scripts written for those behaviours.

This trades framework convenience for a smaller public runtime and scripts with explicit scope. It suits a site made mostly of text and images. An application with substantial client state should make a different decision.

When a check isn’t worth writing

Checks cost maintenance. A check that fails for the wrong reason gets disabled, and a disabled check is worse than no check, because it still looks like coverage in the list. So a rule earns a script only when three things are true: it’s objective, it gets violated by accident rather than by decision and it’s cheap to run.

I still read the copy and look at the rendered page. A script can count words in a heading; it cannot tell me whether the heading tells the truth. I would rather leave that judgement visibly with an editor than add a passing check that nobody should trust.

Sources

  1. Understanding SC 1.4.3: Contrast (Minimum) — W3C — The AA thresholds the token check enforces. Checked 20 August 2026.
  2. Content collections — Astro docs — The schema layer the content is validated against at build time. Checked 20 August 2026.

Want a build somebody else can still work on in three years?

Tell us what the site has to do. We'll tell you what it doesn't need — and you keep the repository either way.

Share article