AI Built the UI. Now Try Changing It.
A polished screen is easy to generate. Changing it exposes the decisions underneath. Why I built a pattern-driven frontend system with reusable components, composition rules, and context for coding agents.
The first render tells you surprisingly little about the system behind it.
Ask an AI agent for a customer dashboard. It can give you a convincing screen with a sidebar, filters, a table, and an edit drawer. Sometimes the code behind it is good, too.
Then someone changes the brief.
Users need to select customers across pages. The selection must survive a search. The edit drawer should return keyboard focus to the right place when it closes. If someone types a new search before the previous request finishes, older results must not replace the newer ones.
The screen still looks almost the same. The engineering problem doesn’t.
I use AI to build interfaces, and I expect to keep using it. What interests me is what happens after the first working screen. Can I change the feature without untangling decisions I made three prompts ago? Can another developer understand those decisions without asking the agent to explain its own code?
That’s why I started building what I think of as a pattern-driven frontend system. It’s my own working foundation of components, layout rules, common screens, and guidance for how those pieces should fit together. I also make that guidance available to the coding agents I use.
I keep the components in Storybook so I can inspect their states and document their use. The value, for me, lies in the decisions I can reuse across features.
The change request is where the architecture shows
Take that customer table. Which part of the application owns selected customer IDs? If the table unmounts when a filter changes, should selection disappear? Where does the current search query live? Who prevents an older response from overwriting a newer one?
There isn’t one universal answer. I might keep selection in the feature layer and let a query library manage server data. A different product might need another approach. What matters is making the boundaries deliberate.
Now add editing. The interface shows a new customer name before the server confirms the update. If the request fails, the application must restore the previous value or reconcile the server’s response. That’s an optimistic update. It takes more than a spinner and a success message.
An AI agent can implement these behaviors. A skilled developer can, too. But a working demo doesn’t tell me whether responsibilities are clear, whether edge cases have tests, or how safely I can extend the feature.
I don’t judge generated UI only by how closely it matches a design. I also want to know where the decisions live. When a simple requirement changes, how much of the feature must I understand before touching it?
That matters more to me than the number of lines an agent wrote or how fast it produced the first screen.
Frontend patterns deserve more attention
We tend to discuss architecture through the backend: services, data models, queues, and dependency boundaries. Those choices matter. But a strong spine doesn’t mean the whole body works. Movement and coordination depend on other systems, too.
A sound backend won’t fix a frontend that loses keyboard focus, mixes server and local state, or shows contradictory loading and error states. Frontend architecture shapes the part of the product people actually use.
I’ve explored these problems in my frontend design pattern series. I’m less interested in collecting pattern names than understanding when each one earns its place.
A compound component can let developers assemble a dialog from coordinated pieces. That’s often clearer than a single component with dozens of props. But it only helps if those pieces share a clear contract and useful defaults. Otherwise, I’ve traded one awkward API for several smaller ones.
A state machine can clarify complex transitions. It won’t automatically prevent request races or solve cache invalidation. Those need other decisions and checks. A basic control may not need a state machine at all.
Patterns give me options. Judgment tells me when to use them.
The customer table shows why this matters. Selection ownership, request coordination, and dialog focus can involve different concerns and different layers. I don’t want one giant table component to own everything. I also don’t want a new abstraction for every small interaction.
I want to reuse the reasoning behind a component, not just its markup.
Building a frontend system I can actually reuse
I didn’t start from a blank design file. A principal designer had already created a Figma component library with typography guidance, layout rules, and development notes. I used that work as a design reference.
From there, I built the engineering side myself. I wrote the component implementations, chose frontend patterns, documented how components compose, and connected the guidance to a local MCP server and agent instructions. I built this independently for my own work, and I haven’t released it publicly.
Translating the design into code was a lesson in itself. A design file is a polished artifact, too. When I documented every token and component, I found 120 places where a written description had drifted from the value actually in use: an outdated colour, a spacing rhythm the reference screen didn’t follow, a contrast ratio quoted from an older version. None of that shows on the canvas. It only shows when someone has to turn the design into working code.
The system covers more than buttons and inputs. Today it has 30 component families, 10 shared hooks, and a 43-icon set, along with design tokens for light and dark themes, layout guidance, composition rules, application shells, footers, and full reference screens. The important part, to me, is explaining how the pieces should work together.

A reference screen built only from existing components. The names are placeholders.
A button story can show sizes and variants. Composition guidance can explain which shell to use, how a form fits into it, and when a feature should use an existing compound component instead of assembling one from scratch.
Take a dialog. A screenshot shows the result. It doesn’t explain how the trigger and content relate, who owns the open state, how focus moves, or what happens when the user closes it. Those are component decisions, and I want them written down close to the code.
The patterns from my series show up in the implementation, not just the documentation. Tabs, form rows, and the application shell are compound components that share state through context. Async buttons track one single status instead of separate loading and error booleans, ignore a second click while a request is pending, and never update state after the component unmounts.

One button, one status: idle, pending, success, error.
I also built a small MCP server over my component guidance and rulebook so I can give coding agents structured context. It has no dependencies and reads the repository live. An agent can list the components, read one component in full, search the docs, and copy a component into another project. The copy follows the component’s imports and brings only the CSS that component actually uses. I use agent instructions alongside it, including rules specific to a project. The goal is to help an agent find an existing component and understand the situations in which I use it before it generates a new one.
That doesn’t mean the agent always follows the rules. Reading a pattern is not the same as implementing it correctly.
The working code still determines what the component does. Types, tests, and review help check its behavior. The suite currently runs 252 tests, including automated axe checks on every component in both themes, a negative control that proves the harness catches a real violation, and a test that fails the build if anyone adds an inline style. Storybook lets me inspect and document component states. It does not enforce architecture. The actual contracts live in the implementation and whatever checks I run against it.
Test Files 27 passed (27)
Tests 252 passed (252)
Those automated accessibility checks pass. Automated checks only catch part of the problem, though. I haven’t done the manual and assistive-technology testing needed to make a broader accessibility claim.
“Bro, we already have AGENTS.md.”
Fair point. I use agent instructions, too. My composition guide lives in an AGENTS.md file.
A well-maintained repository with clear rules, good components, accessible source code, and reliable tests already gives a capable coding agent a strong starting point. I could keep the same composition rules in repository documents without an MCP server. For many projects, that may be enough.
The research on instruction files is mixed. One evaluation found that context files didn’t generally improve task success and raised inference cost by more than 20%. It also found that agents follow the instructions well, and that the files help most when they describe practices an agent couldn’t guess. Another study, across 10 repositories, found a 28.64% lower median runtime and 16.58% fewer output tokens with comparable task completion. None of those findings evaluates my setup, so I don’t treat them as a verdict on this experiment.
My own experience matched the “non-standard practices” point. My first composition guide deliberately left some choices open: whether a form field puts its label above the control or beside it, how plan details stack inside a card, what an editor’s header shows in each subscription state. Agents kept picking the wrong options. Each choice was defensible on its own, but the screens drifted apart. So I wrote the picks down as binding rules, each tied to the section of the guide it settles, and each with a live reference screen in Storybook.

Same fields, two layouts. Both are reasonable. Only one is the rule.
What matters to me is the gap between a written rule and a usable implementation.
An instruction that says “use the approved dialog” helps when the dialog exists, the agent can find it, and its API supports the behavior the feature needs. The component provides the implementation. Documentation explains the intended use. Tests check whether key behaviors still work. These tools support each other, but they aren’t interchangeable.
I have to apply the same skepticism to my MCP server. It could retrieve too much material, miss the useful example, or conflict with project-specific rules. More context doesn’t automatically produce better code.
If ordinary repository search helps an agent make the same decision with less effort, I’d rather learn that than defend a tool I happened to build.
What I still need to prove
I expect this setup to make my own UI work more consistent. I also suspect that access to clear composition rules can reduce the time an agent spends exploring a codebase or recreating components I already have.
I haven’t measured those outcomes. I can’t claim lower token costs, faster delivery, or fewer defects yet.
I’d test the same frontend tasks with three setups: a repository with ordinary agent instructions, that repository with explicit component and composition guidance, and the same setup with access to my system through MCP. That comparison could help me separate the value of the rules from the value of exposing them through MCP.
I’d test change requests rather than screenshot generation. Preserve selection through a filter change. Extend a compound component without breaking an existing use. Change a shared layout rule. Add a keyboard interaction and check the failure state.
Then I’d look at the finished work: correctness, review changes, regressions, elapsed time, and total cost per accepted task. I’d count retrieval, retries, and test runs rather than looking only at the tokens in the final response. I’d repeat the tasks, too. A good demo proves very little on its own.
Maybe the components and written rules provide nearly all the benefit. Maybe MCP makes a meaningful difference. Either answer would help me decide what deserves maintenance.
The next change is the real test
I built this for my own workflow, but the problem isn’t unique to my projects. Every interface becomes harder to change when its important decisions are scattered or undocumented.
When I return to a feature after a few weeks, I want to find the right layout, understand the component boundaries, and remember why I chose a particular interaction pattern. If I hand the code to another developer, I’d want them to find those answers, too. I want a coding agent to have access to the same guidance, without pretending that access guarantees correctness.
It works across projects, too. When several products share the same system, learning it once means I already know my way around each of them.
I’ll still face awkward edge cases. I’ll still make mistakes. I just want fewer routine decisions to disappear into one-off prompts and generated files.
An agent’s context window eventually ends. The feature stays. When the next change request arrives, the person opening the code might be someone else. More often, it’ll be me.
I’d like to understand what I’m changing.
Further reading on agent instruction files: Evaluating AGENTS.md: Are Repository-Level Context Files Helpful for Coding Agents? and On the Impact of AGENTS.md Files on the Efficiency of AI Coding Agents. These are research preprints, not tests of my system.