How I work

I'm a product designer who builds end to end. The story, the interface, the code, the launch. I take a product from the first conversation to the thing running in production. Most of the time that's with other people: Matt on Copy, the designers I lead at work, engineers and PMs on a sprint team, sometimes it's just me.

I've always built with engineers. On calls, annotating designs, in walkthroughs, pairing. Now I'm in the code with them too. And I write the rules and skills the whole team works from, designers and AI both, so the standard holds as we grow.

This year, in my own labs I've built: Copy, an async walkie-talkie with Matt Moss, live on the App Store. Stockcaster, an AI stock-research spatial canvas, building in public right now. And Try/Keep, an iOS app for tracking what friends recommend.

Then there's Sibi, where I lead a design team who all ship production code. I still design and build hands on there myself: a self-serve onboarding flow that has new customers signing up and ordering the same day; Buscar, where you ask questions of your data in plain language and get the right chart or report back; and a command-palette search that helps customers find what they're looking for and get anywhere in the app, which lifted conversion six points in an A/B test. All of it in production, through the same PR review and the same repo the engineers use.

Buscar, Sibi's analytics layer. A plain-language question returns charts, a flagged-assets table, and suggested follow-up questions.
Buscar, Sibi's analytics layer. Ask a question in plain language, get the right chart or report back.

This is my current stack: Figma, Claude Code, the Figma MCP, Codex, and a vault of custom AI tools built for myself. The rest of this page is how.

The story comes first

Before I design anything, I write the job to be done, the story. One person, one moment, why they'll care.

The story comes out of listening. User interviews, customer support patterns, Mixpanel data and sessions, and TestFlight feedback.

The story has a point of view. I build minimum lovable products: a thin, opinionated slice that works end to end and is good enough that people actually want it. Small but complete. I keep iterating once it's real, every decision tracing back to the problem and the people who have it.

The loop

Once I have the story, I sketch it, maybe in Figma, maybe as a rough prototype I can put in someone's hands. The repo and its docs come after that. Same shape whether it's a Sibi feature or a product in my own labs.

Sketches and lightweight code prototypes. I start out planning and sketching on paper, quick mockups in Figma, or a rough prototype in Claude Code just to feel the flow before I commit fully to that direction. Interactions read differently when you can touch them: a flow that looks clean as a static mock can be annoying by the third tap, and I'd rather find that out in a throwaway. It's also the first thing I share with teammates or the people I'm building for, because watching someone use it tells me more than presenting a mock.

The repo gets a CLAUDE.md and a three-doc structure.

project.md               what it is, who it's for, the wedge
design.md                visual system, voice, motion, gestures, persistence
implementation-notes.md  dated log of off-spec decisions

CLAUDE.md helps the coding agent understand the project and the rules I've set for it. Hooks keep them current. When I wrap a session, the docs update so they don't drift. The brief I'd write for a teammate is the same one the AI needs. Writing clearly for humans turns out to work for AI too. This is the scaffolding that lets the AI work like a co-worker who already knows the codebase. It's also what lets me come back to a project after two weeks and remember what I was thinking.

Plans get reviewed before code lands. I write a feature spec: synopsis, designer plan, engineer plan, constraints to keep in mind. I have Claude Code help me plan out the coding implementation. Then every plan gets a review. /staff-engineer, a skill a friend wrote, does it the way he would. (A skill is a job I've written up once so Claude Code runs it the same way every time.) On Stockcaster it caught a state bug: dismissing a card left the detail rail rendering a card that no longer existed on the canvas. An eight-line fix before anyone ever saw it. Bigger changes get a second opinion from Codex, since a different AI catches different things. It's honestly kind of fun to referee: well, Codex says this, but Claude thinks that.

Most of the time goes to the Figma → MCP → Claude Code loop. The Figma MCP pulls my designs straight into Claude Code, so I can iterate on padding, type, spacing, or whole components without leaving the design loop. I bounce between the two depending on what the idea needs: Figma when I'm working out layout and spatial relationships, Claude Code when I need to feel it running and tune against real data. I run /staff-designer against the screen to think through alternatives or get a second-opinion critique. I built it on fifteen years of critiquing my own work, so it pushes the way I would. /typeset does the same for type.

Real people get it early. If it's an iOS build, it goes to TestFlight as soon as it holds together. In the web app at work, it ships behind a feature flag, internally first, then to customers. Try/Keep's testers kept asking for smart recommendations. Then I explained the trade-off: your data stays yours, no algorithm in the way. They preferred control once they understood that. Copy's beta showed it too. Engagement was living in group channels, but the empty channel plate read as private 1:1. We redesigned that surface before launch to show you could add a solo or a group, and change it any time. Shipping is where the real feedback starts.

Every session ends with a build-log entry. A /wrap-branch skill drafts it from what changed in the code. I review, edit, ship. It all lives in one searchable vault, so what I learn building Copy shows up at Sibi and the other way around. A pattern that crosses projects becomes the next thing to write about, or the next thing to build.

The skills get sharper

When a skill misses, the miss gets written back in, as a new rule or an eval case the next run has to pass. A recent one: /staff-designer graded a clean onboarding screen as a problem because its rubric scored every principle out of four, and a blank box invites a made-up number. The rule now: no score at all, and placeholder or in-flight code can't produce a finding. My review skills get the same iteration loop as the products they critique.

Where it scales

At Sibi the same loop runs across a team. Onboarding is the recent example. Sibi started enterprise: sales-led deals, white-glove setup. That worked, and it landed big accounts. It also meant a smaller property manager had no way to just show up and say "I want in." Our own leads said the demand was there. I added it up and took the case to the founder.

The first version was a stopgap built to learn. A Typeform behind "get a demo," separate flows for clients and pros, every lead piped to Slack so CS and sales saw the same standardized lead the second it landed. It ran for over a year, and the submissions kept teaching us who was actually showing up: small businesses that wanted to start the same day, no sales call. The messaging had been built for enterprise. The data said meet them where they are.

The real flow started as a proof of concept I built solo, the same loop as above. Once the team could see it working, we resourced it: a PM and an eng team. I built the front end, Nick built the backend, and we went back and forth until it was live. On its way to production, the code ran through two skills: a PR-review skill Nick wrote to hold it to the team's coding standards, and a staff-designer skill I wrote to check the front end against the design.

The Sibi self-serve onboarding flow on the Setup Supplier Network step, where a new customer picks the manufacturer categories they buy from.
The Setup Supplier Network step of the self-serve onboarding flow, built solo in the Figma → Claude Code loop before a PM and eng team picked it up.

It's live. Signups run about 3x the pace of the leadgen flow it replaced, zero paid spend. Customers have gone from signup to placed order with no human helping them through, which was the whole point.

The other thing the loop does at work: design decisions go into the system as rules, so they apply to everyone's code, mine included. The designers I lead ship to production now, through the same repo and review the engineers use. Most of them learned to ship code here, and the rules are what hold the bar across the team without routing every change through me.

The Sibi production codebase has its own CLAUDE.md. Here's the tracking rule, verbatim:

# CLAUDE.md (excerpt) — Sibi monorepo

## Tracking Requirements

New pages, user-facing features, and modifications to existing flows
MUST include Mixpanel tracking before the work is complete. Add these
without being asked:

- Primary action events — key CTA clicks, with `originated_from`
  identifying the page/context
- Completion events — success states like form submissions or item
  additions
- Modification coverage — when changing existing UI (new options,
  renamed actions, altered flows), update or add tracking so the
  change is measurable.

Track to measure outcomes, not for the sake of tracking.

Our design system package has its own CLAUDE.md for component and typography rules. Casing was a recurring argument. Title case or sentence case. I made the call once, sentence case, and wrote it into the system. The repo enforces it now, so I don't re-litigate it every time it comes up:

# packages/sprinkles/CLAUDE.md (excerpt) — Sibi design system

## Casing

Use sentence case for all UI copy: headings, section titles, buttons,
labels, menu items, table headers, form labels, empty states, toasts,
dialog titles.

Capitalize only the first word. Examples: "Start an order",
"Fulfillment method", "Ship to home", "Deliver from store".

The only exceptions are brand names (Sibi, iOS, Stripe) and acronyms
(SIBI, PO, SKU, ZIP). Internal feature, domain, or product-area
names are not proper nouns.

Never use Title Case or ALL CAPS for UI copy.

PR review checks both before a human reads the diff. Tracking gaps we used to ship stopped shipping. The sentence-case argument used to come up every few weeks. Now it doesn't.

The other thing CLAUDE.md does at team scale: it lets me audit what's already there. Same loop in reverse. I point Claude at an existing flow with the design system rules loaded ("where have we broken these?") and it comes back with specific findings. Last sweep across success and error toasts surfaced six files where the duration was wrong or missing. Fixed them in the same session.

The audit also sharpens the spec. When findings come back vague, the rule was under-specified. "Copied to clipboard" shouldn't auto-dismiss after three seconds. It's lighter than a save confirmation. So I added a rule: dismiss duration matches the weight of the action. The spec gets smarter as I refine it.

My design system is words

One of my recent projects is Copy. The whole design system comes out of one constraint: the walkie-talkie is a real physical device that happens to live on your phone. Notifications come through a recessed LED. Messages render on the channel plate. Controls are physical: knobs, switches, dials, with haptics.

THEMING.md in the Copy repo opens with the two questions every new skin has to answer before anyone picks colors:

# THEMING.md (excerpt) — Copy

## Defining Your Skin

Before picking colors and components, answer two questions about
what your skin is. They're independent of each other, and together
they determine which shared primitives you mount and which design
patterns apply.

### Is the chassis physical or a display?

A physical chassis is a real industrial-design object the user
could pick up — a knob (wt01), a Braun radio (rams), a console
face (goto). Real devices have indicator LEDs molded into the
chassis surface.

A display chassis behaves like a screen — sci-fi readouts, CRT
terminals, lifestyle interfaces (bladeRunner, flux, tronUprising).
Any rendered element can change state computationally without
breaking the illusion.

### How do color variants work?

Tint variants (wt01, goto, rams) apply a blend-mode overlay over
your base colors. Same dimensions, fonts, shadows — different
color cast.

Palette variants (flux, bladeRunner) swap the entire ThemeColors
object per variant. Each variant is a full repaint.

That's the opening of a much longer file. Seven skins share the same screen logic, but each one looks and feels like a completely different physical object. Getting them there took real fiddling. WT-01, a skin inspired by Teenage Engineering hardware, kept coming back with shadows flatter than the design. I caught what was happening: Claude had decided our version of React Native couldn't render layered shadows, so it was quietly simulating them. It could. Now it does. That went into the repo as a rule: React Native renders these natively, so use boxShadow, copy the values straight from Figma, and skip the shadow libraries.

The same file turns design instinct into buildable instructions. The rule for the "new skin unlocked" indicator is judgment written down: it has to read as part of the device, molded into the chassis like a real indicator light. So the spec tells you to picture your skin as a real object on a desk and ask where the manufacturer would have put the LED.

Four iPhones side by side, each running Copy in a different skin: the WT-01 silver hardware skin, a red LED display readout, a dark console face, and a Braun-inspired white radio. The same screen, four physical objects.
Four of Copy's skins. Same screen logic, four different physical objects.

The same idea governs the prose the product generates, too. Stockcaster's design.md tells the AI to swap the ticker out of any line it writes. If it could be about any company, rewrite it. Specific, or it doesn't earn the slot.

Shipped

My own products. Everything here went to real users.

At the end of the day

I think like a product designer. I'm sketching, thinking through the problem even when I'm not at work. I care about typography and spacing and color more than is reasonable. With these new tools, I get to see the whole design through. Every interaction, every part of the product, down to the live code.

I can build a real product or feature and it actually looks like the idea I had in my head. I can come back to a project after a month or two and the people working on it with me (and the AI) already know where we left off, because the docs are the context.

I'm always up for talking to people building interesting things. Hit me up here.