Design Engineering
I build the systems that let teams ship consistent, high-quality UI across code, visuals, and content. The case studies below show how.
— John Hiemstra, product engineer
- ~50% less UI dev time from a shared component library
- 5 component systems across enterprise and studio
- 2 production sites on Tailor
What I believe about design engineering
Design-inline systems
My approach to design systems is what I call design-inline. The system develops directly in strongly typed reusable code, Figma stays a just-enough tool for working ideas through, and the system itself becomes the contract both humans and agents build against. Heavy design-tool work creates export cycles and tool-lock; design-inline work ships to users.
Systems as governance, not component dumps
A design system earns its keep through governance, not through how pretty the Figma file looks. Private registry publishing, semantic versioning, deprecation models, and clear contribution paths are what let a system actually hold a quality bar as the team grows. The pretty parts are table stakes; the governance is the product.
Code, visual, and content as one craft
Modern design-engineering sits at the convergence of code standards, visual standards, and content standards. AI-assisted development has made that convergence more urgent, not less — because the tools generate across all three surfaces, the system has to govern across all three too. Skills, evals, and feedback loops are the mechanism; taste is the goal.
Keeping AI output on brand when the brand is a person's voice
The problem
The brand publishes a podcast, a science blog, and the social clips cut from both. Generative tools made each episode's reels, quote cards, titles and summaries fast to produce, and just as fast to get wrong: a placeholder title, a paraphrased quote, a claim with no source. Each one spends the guest's words and the brand's trust.
The decision
I didn't ban the tools, and I didn't trust free generation and hope a reviewer caught the drift. I wrote the standards down as gates: code that fails a run loudly before anything irreversible happens — a render, an upload, a schedule write — and a studio of role-specialized agents, each with a named checkpoint. A direct quote is never edited, only cut. A factual claim ships only with a source that says it. A person reads every quote before anything publishes.
The system
Each gate has one definition, and every phase that needs it calls the same one. A title that breaks a rule goes back to the model with the broken rule named; if the rewrite fails, code repairs it; if that fails, a plain title stands in. The run records every change, so the person reading sees what changed and why.
def assert_real_title(episode_title: str, episode_code: str) -> None:
"""RULE: never advance/render/ship an episode with a missing or placeholder
title. …
Fail loudly here instead — before the counter bump and schedule write."""
if not is_real_title(episode_title):
raise GateFailure(
f"{episode_code}: refusing to proceed — episode_title is "
f"missing/placeholder ({episode_title!r}). …"
)ensure() never fails a run. A heading that breaks a rule is fixed, in order:
1. the model rewrites it, told exactly which rule it broke (up to `tries` asks);
2. code repairs it: "&" to "and", the banned marks removed, or the title's own half before/after a colon or dash;
3. a plain title that always passes: "<first name> in Conversation", or "In Conversation".
Every change is recorded on the quote (`title_check`) and in the report, so the read shows what was changed and why.What it proved
Adoption evidence
The pipeline runs on its own server and turns each episode into reels, quote cards, a blog draft and a show summary. Gates are added where a failure proves the need: an episode once finished its first phase titled “TBD,” and every phase now shares one title gate because of it.
What I'd do differently
Write the gates before the first run, not after the first failure. Most of the gates in this pipeline exist because something got past us once.
Owning the whole stack of design decisions
The problem
Every custom site for a small business started from scratch. Each one rebuilt its own design system, and a lesson learned on one client stayed in that client's repo.
The decision
I built Tailor, a platform I own end to end: React 19 and React Router 7, server-rendered, on the front; FastAPI and Postgres behind; and a scaffold that generates a complete, independent site for each client. I passed on a hosted page builder, and on shipping client packages the client would have to patch later. The client's repo is entirely theirs. A lesson from any build becomes a Tailor release every site gets.
The system
A strictly typed component surface, governed by a written house UI canon of 24 numbered rules: contrast, hit areas, five options at most in any menu, titles and actions that never scroll away. Decisions are recorded as architecture decision records, and work passes a local quality gate before it merges. I build it with Claude Code and a team of role-specialized agents — product, frontend, backend, test, security, design — each working to the same written policies.
What it proved
Adoption evidence
Two production sites run on it, one of them an online shop taking live orders. From the first commit on April 30 to October 14, 2026: 3,124 commits, 112 architecture decision records, and version 1.0.0 released on October 1. The page you are reading is a Tailor page.
What I'd do differently
Test the checks as hard as the code. A deploy check once misread its own output and reported a failure that wasn't there; the rollback it set off took a live shop offline for thirteen minutes. Every check now prints evidence that it ran and what it read.
One brand kit, every client deliverable
The problem
John Hiemstra Creative produces branded client documents: proposals, reports, audits and presentations. Each one had to read as the practice's own work, not a template, without a production designer on staff to lay it out.
The decision
I built a shared base instead of a template per document: one stylesheet, one component set, one pagination engine, and a skill per document type that carries its structure and voice. I passed on a page-layout tool, and on scale-to-fit. Content that doesn't fit flows onto the next sheet; it is never shrunk.
The system
common.css sets the US-Letter sheet, the masthead and the footer; components.css adds the shared blocks — stat rows, bars, figures, fail-and-fix tables. pack.py paginates, and the render scripts compose the sheets into a PDF. The proposal, report and content-development skills all stand on that base.
What it proved
Adoption evidence
Proposals, reports and content-development documents all extend the same base without forking it. A line of ingredient references for the family wellness brand reuses the sheet geometry and the assembly script under its own brand sheet.
What I'd do differently
Write the voice rules alongside the layout rules. The pagination canon was set in July; the canon for how we present our own work came in October, after a long round of steering a draft from internal and self-congratulatory to sharp and useful to the reader.
Working standards
Code
- TypeScript in strict mode
- UI composed from data and config, not one-off components
- Component conventions documented beside the code
- Shared components owned and versioned; no package a client is left to patch
- Releases tagged; significant decisions recorded
- A local quality gate before every merge: tests, a cold review, a security review
- Automated accessibility checks in the gate
- No dependency added without a recorded reason
Visual
- Type, spacing and color as tokens
- Spacing on a consistent base unit
- Semantic color tokens above the raw palette; light accents never carry text
- WCAG 2.2 AA contrast throughout
- Motion restrained, and switched off when a reader asks for reduced motion
- Print layouts designed, not left to the browser
- Five options at most in any menu; titles and actions never scroll away
Content
- Voice rules written per surface
- Every factual claim carries a source that says it
- A direct quote is never edited, only cut
- Content lives beside the code it describes
- AI output passes gates before a person reads it, and a person reads it before it ships
- Reviewer load is a design input
- Content changes versioned like code
Contact
John Hiemstra · product engineer · Sarasota, FL