Skip to main content

Hand the designer what ships

20 October 2026 · AI-built UI · the engineering team

The story

Our website's pages are generated. A template lists the sections of a page in order, each with its styling tokens and the fields it reads, and a content file holds the words. A build step joins the two, checks the result, and renders it. It works well, and it left us with a design problem: the people shaping those templates want to see them in the design team's own workbench, beside the components they come from, under the same contrast and touch-target sweeps every other screen gets.

The obvious move was to teach the workbench our format. Give it the templates, the content files and the binding rules, and let it put pages together itself. We wrote that idea down and then stopped, because we had already learned this lesson once. A preview that rebuilds what the gate does is a second implementation of the gate, and it drifts. Our build does more than join templates to content: it resolves catalog data into plan cards, renders markdown bodies into blocks, drops optional sections whose data is missing, and refuses pages that would leak anything internal. A workbench that did only the join would show pages we never ship.

So we went the other way. The workbench gets no templates and no binding rules. It gets the build's output.

The pattern

Export the resolved result, not the recipe. One command runs the same gate the build runs and, if it passes, writes what the gate produced: one JSON file per page holding its sections and their final props, the site's navigation and footer, and the list of styling classes those pages use. That is exactly what our renderer receives. The workbench renders it with the shared section components, and what it shows is what the site shows, because it is the same data.

Refuse before you export. If the gate refuses a page, the export writes nothing and exits with the refusal. A design tool fed half a site, or a site the build would not ship, teaches people to trust pictures of pages that will never exist.

Make it deterministic. No timestamps, sorted lists, the same bytes for the same inputs. Two runs over one content tree produce identical directories, so the workbench can cache, diff and review changes like any other file.

Version the format and make the reader check it. The export's manifest names its format and version. The workbench rejects a version it does not know, in a test as well as in the page, so a change on our side fails their build loudly instead of rendering something subtly wrong.

Export the awkward content too. The same command takes a flag that builds from deliberately difficult mock content: an eighty-character title, a feature list of two items and one of six, a product with three plans and no questions, a page with every optional section missing. Designing against the real content alone hides the cases that break a layout.

Only replace what you wrote. The export clears its output directory before writing, which is dangerous with a path someone typed. It replaces a directory only if it is empty or carries a manifest of its own format; anything else is refused and left alone.

The shape is the one from our earlier post on previews, pointed at a different reader. Whenever a second tool needs to show what the first one makes, hand it the made thing. Rebuilding it there is a fork you will maintain forever.

Try it

Find a place where a design tool, a documentation site or a review app re-renders something your build also renders: a component gallery fed by hand-written props, a mock-up of a page your templates produce, a preview that reads your source files. Add one command to your build that writes the resolved output, deterministically and under a versioned name, and refuses when the build would. Point the other tool at that directory and delete its copy of the logic. Our workbench route is still being built on the design team's side. The export is ready for it, and it has already changed how we talk about templates: we now ask whether a layout survives the mock content, not whether it looks right on the page we happened to open.

design-systemspublishingtemplatespreviews