Kelli Novotny · Product designer · New York
On my current work an AI writes the code that turns my designs into screens. Nobody walks over to ask what I meant, which changed what a spec has to carry and what has to check the result afterwards. It will also tell me the result is great whether it is or not, so working that out is still my job. This page is what came of that.
Two design systems, the checks that keep them honest, and the calls I keep for myself. The case studies live at kellinovotny.com.
01 · Why the work changed · The three gates
On my current engagement I design and the founder builds in Flutter with a coding agent. So the thing turning my designs into screens isn't a person, which changes what the spec has to carry. A colleague asks when something is missing. An agent invents. Three gates keep the build from drifting off the design. The system they check against is in his repo, written by me: six files, 1,136 lines, zero deletions, merged in September.
Gate A · Figma
Nothing leaves Figma unless every element can be named in tokens. Primitives hold values, semantics hold roles, and what I'm checking is which layer a thing is bound to, not just whether it's bound to anything at all. I run the check against the Figma file and get the violations back.
Gate B · Handoff
A work order per screen: exact token names per element, plus the exported frame, committed to the repo, since the repo is what the model can actually read. The last line of the prompt is the one that matters. If the tokens don't cover it, stop and tell me.
Gate C · Build
A script counts violations before I look. Exemptions get logged with the reason attached (one sheet runs 132pt of bottom padding, off-scale on purpose, for safe-area clearance). Retired tokens get deprecated with the lint on and never mirrored into Figma, because a dead token in a library is an invitation.
The last run on one screen found thirteen violations, none of which needed a new token: the design had drifted, not the system.
02 · One system, two places · Rexx
Rexx keeps the recommendations people give you. I designed it, built it and shipped it solo, with a model in the loop the whole way. Every color, radius and spacing value lives twice: once as a Figma variable, once as a Swift enum the components read. Here's one of them. Flip the toggle in the corner.
Figma
color/status/inProgress
Swift
static let inProgress = Color(UIColor { traits in traits.userInterfaceStyle == .dark ? UIColor(...) // #F5A55E : UIColor(...) // #F28C40 })
Ten of the 41 aliases, read from the file: three whites in light become three different darks
A sync skill I wrote checks both sides for drift and won't change either until I say yes. Rexx's own UI is a release behind my bar, and I know exactly where.
RexxLive on the App Store since February 2026 therexxapp.com03 · Rules a machine can follow · SafeMom
SafeMom is a pregnancy ingredient checker, and my current engagement. You scan a product barcode and get back one of three answers: safe, caution, avoid. That answer has to land without raising its voice, in both themes, in a build I don't own, which rules out holding the standard in my head and applying it by eye. So it lives in a file instead, next to the values, where the thing building the screens can read it.
Each verdict is a set of tokens, not one color, and next to each value in DESIGN.md I write the rule that governs it. accent is always the brand blue, never a verdict. Nothing in the name tells you that. The file does.
04 · What it can't decide · SafeMom
Same product, the part that isn't tokens. The founder vibe-coded a v1 that found real traction and still looked like a prototype. That costs more here than it would elsewhere, because someone is deciding whether to believe an app about their pregnancy, and a tool that looks unfinished doesn't get the benefit of the doubt.
Before: the founder's v1
After: the brand it earned
The brand: warm on top, clinical underneath. The structure: the old home screen was a lobby in front of the app's one job, so I cut it.
05 · What it's allowed to know
Most of an AI product's behavior is settled before anyone touches a screen: what it knows, what it remembers between sessions, what it's allowed to do on your behalf. All design decisions.
Rexx · What it's allowed to do
Rexx saves the recommendations other people give you, so a model adding its own would ruin it. The first line of the system prompt forbids that. I wrote the prompt, the schema and the failure modes.
My system · What it remembers
My notes live in one Obsidian folder and Claude reads the same files I do. The index points at notes instead of pasting them in, so a session loads only what it needs. Every turn resends everything, so loading less is both more accurate and cheaper to run.
06 · What I build myself
Not everything I make is a screen. None of it is heroic engineering either. It's the part of the job that used to be out of reach and isn't anymore, including this page, which is checked the same way the apps are.
This page's tokens, read from the stylesheet right now
Flip the toggle. Values and counts are read off the live stylesheet, not typed in.
Claude Code daily, with the Figma MCP for the token audits and the captures. therexxapp.com is Next.js on shadcn and Tailwind, also built in Claude Code, repo public on my GitHub.
07 · What the year taught me · Granola
Most of how I work now is new, and I learned it by getting it wrong first. The tools worth keeping turned out to be the ones that added to what I was already doing instead of taking it over. I don't keep allegiances. Claude Design got written off once and earned its way back. Granola is the one I've liked most this year.
I type half-thoughts while I listen, and Granola hears the whole room. The enhanced note merges the two: the room's full context, organized around what I chose to write down. Same note, both layers, mine on top.
Where this stands
Everything here got better because someone questioned it. I'd like more of that.
Kelli Novotny · kellinovotny.com · therexxapp.com