Skip to content

Repository files navigation

Foundry, rendered through json-render

The Foundry Tailwind agency template, drawn from JSON specs through a typed component catalog instead of hand-written JSX.

Eight pages. Every one of them is a spec file. Seventeen of the twenty-one sections are decomposed further, so their content comes from a data document rather than from the markup. A gate in this repo proves the result is byte-identical to the hand-written React it replaces.

bun install
bun dev     # the spec-rendered site on :3000
bun check   # the tests and the parity gate

Open / and /reference side by side. The first is assembled from src/render/specs/home.json; the second is the original React route. They should be the same pixels.

Why this exists

An interface that an AI generates has to be safe and predictable. Letting a model write React gives it a vocabulary in which every mistake is expressible: a wrong colour, a broken layout, an invented component, a class that quietly leaves the design system.

Constraining it to a catalog removes that vocabulary. A model can choose which sections appear, in what order, with which of a closed set of props, and what words go in them. It cannot emit a class, a colour, a spacing value or a component that does not exist. The design cannot drift, because there is nothing to drift with.

This repo is the proof that the constraint costs nothing: the constrained renderer draws the unconstrained design, exactly.

How it is put together

Pages, each a checked-in spec 8
Sections in the catalog 21
Shared primitives 12
Sections decomposed to data-bound parts 17
Sub-part types those declare 79
Design tokens, light and dark 50
src/
  components/        the theme: 21 sections, 12 primitives
  fixtures/          the work spine the sections read
  theme.css          every design decision, as tokens
  app/               the original React routes, kept as the parity reference
  render/
    catalog.ts       the vocabulary: types and their zod props
    registry.tsx     type -> React component
    parts/           one module per section: its props, its React, its fragment
    specs/           one spec per page
    state/           the content, as a generated JSON document
    expand.ts        splices a section's fragment into a page spec
    codegen.ts       a spec back into an ordinary React route file
    parity.check.ts  the gate

A part holds one section's three faces in one file: the props a spec may carry, the React that draws it, and the fragment that expands it into smaller typed pieces. Sub-parts are declared in the same module, so decomposing a section touches one file.

A fragment is a section's subtree, built from its props in TypeScript before any element exists. An enum decides structure at build time; only what varies per item becomes a condition in the spec.

The state document is generated from the section files, never hand-edited. A test regenerates it and fails on a byte of drift, so the rendered site and the data cannot disagree.

codegen turns a spec back into a route file in the theme's own house style, byte-identical to the checked-in one for seven of the eight pages. The eighth is the component styleguide, which is deliberately left whole. This is what keeps the output a normal React project: a generated page can be handed over as source, not as a runtime dependency.

The gate

bun check renders each page twice, once from its spec and once from the hand-written route, and compares the markup after normalising React's generated ids.

ok home — 40627 bytes identical
ok work — 35690 bytes identical
...
ok parity — 8/8 routes

Two details that were paid for. The comparison uses renderToString rather than renderToStaticMarkup, because the static renderer drops React's text separator comments, and a separator splits one text node into two, which a browser shapes differently. Separators that sit beside whitespace or a tag boundary are dropped, because those cannot change anything. That rule was fitted to screenshot measurements rather than reasoned into place.

A pixel comparison across eight routes, four widths and both modes runs at 60 of 64 pairs identical. The residue is one and eight pixels of antialiasing on pages seven and fifty-nine thousand pixels tall.

What is not here

The theme's plain HTML edition and its agent skills live in the Foundry repo. This one is about the rendering layer.

Class strings are deliberately absent from the spec format. They live in the registry parts, and a lint requires every one of them to already exist in the section file that part decomposes. A spec carrying class strings would put back every raw value the token file exists to forbid.

Licence

MIT, for the code. The three typefaces ship under the SIL Open Font License 1.1 and their licences travel in src/fonts/. Built by tailthemes.com.

About

The Foundry Tailwind agency template rendered from JSON specs through a typed component catalog. Eight pages, each a spec; 17 sections decomposed to data-bound parts. A parity gate proves it renders byte-identical to the hand-written React. Next.js 15, React 19, Tailwind v4. MIT.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages