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 gateOpen / 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.
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.
| 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.
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.
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.
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.