Kelli Novotny · Product designer · New York
I design and build, with a model in the loop every day. This page was made the same way, on the same token rule as the apps I work on. Below: how the work gets made, two design systems, and where I draw the line.
01 · AI workflow · The three gates
On my current client engagement I design and the founder builds in Flutter with a coding agent, so the thing that turns my design into a screen isn't a person. A colleague asks when something is missing. An agent invents. Three gates keep the build from drifting off the design.
Gate A · Figma
Before a design leaves Figma, every element must be describable in token names. Primitives hold values, semantic tokens hold roles, and what Gate A checks is which layer a thing is bound to. I run it through the Figma MCP and get a violation list back.
Gate B · Handoff
A per-screen work order: exact token names per element plus the exported frame, committed to the repo, because the model can read the repo and can't read my Figma. The prompt ends: if the tokens don't cover it, stop and tell me.
Gate C · Build
A script tallies token violations in the built screen before I look. Exemptions are logged, not hidden. Retired tokens are deprecated with the lint switched on, and never mirrored into Figma, because a dead token in the library is an invitation to use it.
Gate A's last run on one screen found 13 violations, and none of them needed a new token. The values they should have used already existed. The design had drifted off the system, which is what the gate is for. The gates also catch their own blind spots: Gate A once passed a border bound to a primitive, which cleared "bound to a variable" and failed the only question that matters. The check is now "bound to the right layer."
The principle
Design the pipeline for the builder you actually have.
02 · Design systems · Rexx
Rexx is an iOS app for keeping the recommendations people give you, designed, built, and shipped solo with a model in the loop. Every color, radius, and spacing value lives twice: a Figma variable and a Swift enum the components consume. Here is 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 the two homes for drift and won't change either side until I say yes. The footer numbers and the sheet above were read live from the Rexx Figma file on September 21, 2026. One honest note: I shipped Rexx function-first, so the UI is a release behind my own bar, and I know exactly where.
The principle
If a screen can't be described in token names, either the screen drifted or the system is short a token.
03 · Semantic tokens · SafeMom
SafeMom is a pregnancy ingredient checker, and my current engagement. Someone scans a product barcode and gets 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. So the rule lives next to the value, where the thing building the screens can read it.
Each verdict is a set of semantic tokens, not a color: the dot, the fill behind the card, the label on that fill, and a border that is decorative on purpose. Next to each value in DESIGN.md I record the rule that governs it. For accent: always the brand blue, never a verdict. Nothing in the name tells you that. The file does.
The rules hold in both themes, every pair measured rather than eyeballed, and the verdict never rides on color alone: dot, label, and fill agree.
Rules only mean something if something checks them. To make these captures follow the toggle in the corner, I pulled dark-mode frames out of my Figma file, and four things broke. Each had been wrong all along, and light mode never showed it. Two of them:
Six semantic tokens minted, nineteen nodes rebound, light mode pixel-identical. SafeMom's dark theme isn't reachable in the app yet, so all four were caught before any user could see them. A page demo turned into a fix for a paying client, the same afternoon.
The principle
The machine can tell you a button is bound to the wrong layer. It can't tell you the verdict reads calm. That's where its job ends and mine starts.
04 · Product judgment · SafeMom
The same engagement, the part that isn't tokens. The founder vibe-coded a v1 that found real traction and still looked like a prototype. Here that costs more than it would elsewhere: 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.
A scanner gets judged on the scans that work. I drew the three that don't: still loading, a barcode that matched a product but not an ingredient list, and no connection at all. None of them shows a verdict color, because a verdict is a claim and the app hasn't earned one until it has actually checked something. The "based on the label we could read" line comes off those screens too, since no label was read.
Drawing the loading state turned up a gap: the skeleton bars had no token to stand on, so a divider token was doing a surface job. That is the system being short a token, not the screen drifting. surfaceSkeleton was minted with its rule attached: loading placeholders only, never a divider.
The principle
Trust is built from what you leave out.
05 · Context engineering
Most of an AI product's behavior is settled before anyone touches a screen: what the model knows, what it remembers between sessions, what it's allowed to do on your behalf. That's design work. Two receipts.
Rexx · What it's allowed to do
Rexx saves recommendations other people give you. A model that starts making its own would ruin that, so the first line of the system prompt forbids it. It classifies into whatever categories exist and can't invent one: you can add a category, the model can't. I wrote the prompt, the extraction 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, and per-project rules send it to the right corner. Every turn resends the whole context, so the structure that makes retrieval accurate is also the one that makes it cheap.
The principle
The model is a constant. What separates two AI products is what you put in front of it and what you leave out.
06 · Design engineering
Three things I built that aren't screens: the theme layer inside a client's Flutter app, the tooling that checks my own work, and this page. None of it is heroic engineering. It's the part of the job that used to be out of reach and isn't anymore.
In the client's repo
Skills I wrote
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.
The principle
Scripts check the mechanical truths: tokens bound, nothing drifting, nothing missing. Taste is the part I keep.
07 · Point of view · 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 doing instead of taking it over. I don't keep allegiances, I go back and retest, and 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 workshop's full context, organized around what I chose to write down. It adds what the room said without replacing what I wrote. Same note, both layers: mine on top, enhanced beneath.
The principle
Add what the machine knows without replacing what I decided. That's the rule for Granola, and it's the rule for this page.
The short version
Everything here got better because someone questioned it. I'd like more of that.
Kelli Novotny · kellinovotny.com · therexxapp.com