Our AI kept rebuilding a button that already existed — until we wrote it down
By Max Tymoshyn10 min read
What the AI actually sees when it opens your project
A few weeks ago I asked AI to put a floating book-a-call bar on another part of one of our own sites, and it built a second one: same look, near enough, its own copy of the styling, sitting somewhere else in the code. We already had that bar — bottom of the page, you click it, you book a call. It wasn't laziness and it wasn't a bug, the AI just had no way of knowing the first one existed, because all it could see was our CSS. Now there are two, so the day somebody wants that bar to say something different, that's two edits and an argument about which one is real. Multiply that across a year of pages and you end up with four versions of one button — and nobody willing to delete three of them.
This is the part we were slow to work out. AI doesn't see components. It sees CSS.
If the only thing in front of it is a block of styling, it has to work backwards: (1) read the code, (2) turn it back into words in its own head, and (3) decide from those words what the thing probably is. It does that every single time, from scratch — there's no reason two runs should land on the same answer.
So we stopped making it guess and started handing it the words up front.
Every component in our projects now has a short MD file next to it, written in plain English. What this piece is. What it's for. Where it belongs. When not to use it. The floating bar is the booking prompt, there's one per page, it shows up after the offer, and it isn't a general-purpose banner.
AI doesn't see components. It sees CSS, and it has to translate that back into words before it can use anything. We write the description in MD format for each component, so it doesn't have to.
Nothing about the design changed to make that work. The code didn't move. We added a layer of description on top of a project that already existed — and that's the whole difference between AI recognising our booking bar and inventing one that looks like it.

Written-down components tell the AI where each piece belongs
Stopping it from writing the same bar twice is the obvious win, and it took us a while to notice there was a bigger one underneath it.
Once each piece says what it's for, AI stops picking components by how they look and starts picking them by what they do. It knows which one belongs in which situation on the site, where it's meant to sit, and which ones it should leave alone. That knowledge used to live in the heads of the two or three people who built the thing, plus a design file nobody was pointing a machine at.
Write it down once and the machine has the same working understanding of the product that they do.

Two files, doing two different jobs
There are two documents in the project, and they answer different questions.
The first holds the values: the colours, the type, the spacing, the margins and paddings. It's the short list of what this site is allowed to look like.
The second describes what the pieces are for. Not what they look like — what each one does, where it's normally used, and when it shouldn't be. That's the part nobody used to write down, because until recently the only readers were people who already knew.
Both sit in the project itself, next to the code, in ordinary words. That matters more than the format.
The index is the part people skip
A grown-up front end has a couple of hundred pieces in it. You can't hand a machine two hundred descriptions and ask it to hold all of them in mind before it writes a line — the request gets enormous and the useful part gets buried.
So there's one small file at the top: a table. Each row is a piece, one line on what it does and when it's used, and the path to where its longer description lives. The descriptions themselves don't sit in a folder of their own — each one lives next to the piece it describes, and the table is what knows where they all are.
The machine reads that one table, picks the piece it needs, then opens only that piece's file. Cheap, and it makes reuse the default. It chooses from what exists first and only writes something new when nothing in the list fits.
We already run the same trick on our meeting archive. Every project has a short index of its calls — date, one line on what it was about — and the full transcripts sit in one central store. AI reads the index, decides which call is worth opening, and opens that one. Indexes written for the machine, so it can work out where to dig instead of being handed everything.

Our designer builds the interface inside the running app
The thing I didn't expect is who this helps most.
On one client project our designer now builds the interface inside the actual application. Not a picture of it — the running app, on her own branch. She has the project set up locally and a skill configured that knows the rules of this codebase, so she can ask for a screen and get it assembled out of the components we already have.
That only works because of the two documents. She isn't guessing what a card is, and neither is the AI working alongside her. It knows which component belongs where and which ones it isn't allowed to touch, so she can move quickly inside a complicated product without breaking the system underneath it.
What changed in practice is what she stopped doing. She used to draw a lot of states in Figma. Now, if there are three ways a popup could work, she builds all three in the app and sends three links. People click through them and say which one they want. Everybody is looking at the real thing instead of a picture of the real thing, and the two that lose were never a week of screens in the first place.
Designers have been trying to vibe-code for a while, and it usually stalls in the same two places. The first is that what they build ends up beside the product rather than in it — a separate HTML file that looks right and behaves like nothing — so they never find out whether the idea actually works, and they drift back to Figma. The second is the one every designer I've asked about this says out loud: it's great until something breaks, and they don't want to be the person who has to debug it. Both of those are fixed by the same thing, and it isn't friendlier tools. It's the app being able to explain itself to whatever is writing the code, and somebody else owning what ships.
Our designer builds a few working versions of a flow inside the running app, instead of drawing state after state in Figma. A developer takes the branch from there.
The boundaries are dull, and they're what make it safe to try. Her branches carry a prefix of their own. She can use the components and change the colours; she can't reach the services, the data, or the logic behind them. The main branches are locked, so nothing goes live by accident, and when a branch carries her prefix a check runs on it that fails if anything outside her lane got pulled in. Every one of those branches is marked as needing review before it goes anywhere.
Then she opens a pull request, and the developer picks that branch up and owns the code that ships. The business logic, the tests, the parts a designer should never be on the hook for — that's his. She hands over a working interface and a decision about how it should behave; he puts the intelligence behind it.
Nobody has to remember any of this either. Setting the project up asks each person their role once, and the workflow follows from the role: which branches, which lane, which rules. It's the difference between a policy and a habit.
The handover between design and development used to be a document. Now it's a branch. It's faster, less gets redone, and far less gets lost in between.

Your design file is the wrong place for the answer
The obvious objection is that all of this already exists in Figma. It does — and Figma is still where the design gets decided. It's a poor place for a machine to read it from.
The reason is dull: it isn't structured. A machine pointed at a design file has to interpret it, and interpretation means a slightly different answer each time. What you want on the other end of a request is a value, not a reading.
Here's the version of this we actually hit. On a project with several near-identical shades of the same colour, the AI stops and asks the developer: this one is slightly different from a variable you already have — do I make a new variable, or use the existing one? He reuses it. He has to, because by now there are too many to hold in mind. Every screen that answers that question differently leaves behind another near-duplicate, and the set gets harder to use each time.
So the values move into a structured file in the project. The design file keeps deciding. It just stops being the thing that gets read.
A document nobody updates is worse than no document
The failure mode of all of this is obvious once you've been burned by it: the description drifts away from what the code actually does, and now it's confidently wrong.
Two things keep it honest.
The first is to describe only what lives in your own project. It's tempting to write down how the whole product works — what the numbers on the dashboard mean, what happens after the form is submitted. Most of that lives on someone else's side and changes without telling you, and in a year those files are garbage nobody trusts. You can't usefully describe what a progress bar means. You can describe how a progress bar works.
The second is to make updating it part of the work rather than a good intention. Before a change goes in, something checks: you touched these pieces, their descriptions need to move too. We already run that kind of check between an interface's documentation and how it actually behaves — the two drift apart constantly, and the only reason we ever know is that something looks.
What to ask for
If you're paying a team to keep building pages, three questions are worth putting in writing.
- Is there a list of what this site is already made of, in plain words, that whoever builds the next page reads first?
- Where do the colours, type and spacing actually live — only in the design file, or in the project?
- What stops the next page from adding a fourth version of something we already have?
Any team can answer those in a paragraph each. Vagueness in the third one is the expensive kind.
The part we haven't solved
Doing this on one project is straightforward. Doing it once, for a whole company, is not.
Every team that starts this ends up inventing its own version — ours, the client's, the other studio on the same project — and three approaches that don't join up are worse than one imperfect one. That isn't a design problem or a tooling problem. Somebody has to own it across the company, and that's an operations job. I don't have a clean answer for it yet.
Worth saying what this isn't, though: it's a layer of description added to a project that already exists. Nothing gets rebuilt to get it.
TL;DR
AI doesn't know what your site is made of. Point it at the code and it works out what everything means from scratch, every time — which is why it cheerfully builds a second copy of a button you already own.
So a design system now has a second reader, and that reader needs three things inside the project: the values it's allowed to use, a plain-words description of every piece and what it's for, and a small index so it can find the right one without being handed all two hundred. Then AI assembles what you own instead of adding to it.
The part that surprised me is who this helps. Describe the pieces well enough for a machine and a designer can build working screens inside the real app, on her own branch, while a developer still owns the code that ships. Not because the tools got friendlier — because the site can finally say what it's made of.
We made the same move on our own site — we rebuilt norml.studio on Next.js and Sanity — and everything above is why a page there takes an afternoon. If AI can't reach your site at all, that's the earlier problem, and no amount of writing things down gets past it.
See more Norml in Google Search
Add Norml as a preferred source