In this article
Start with a screen the product needs
A purchasing application needs a form for updating a supplier's contact details. The designer adds an email field, a region selector and a Save button in Figma. A developer, perhaps working with an AI assistant, implements the screen. It is a small task, but it contains decisions the next form will need too: field spacing, error messages, button emphasis and what happens while saving.
If each screen solves those decisions independently, the application accumulates several versions of the same interaction. A design system gives the team a shared set of choices and implementations to reuse. That includes visual foundations such as color and typography, components such as inputs and buttons, and patterns for putting them together. It also needs guidance about when those choices apply and a way to evolve them when a product needs something new.
For our form, we can follow the decisions from the design into working components and then into Storybook. Storybook is a development environment that renders components and screens in named example states, called stories. It gives designers and developers a place to inspect the implementation together, including states that are awkward to reach in a running application.
- Design the interactionFields, hierarchy, errors and saving behavior.
- Implement shared decisionsTokens, controls and the form pattern.
- Review in StorybookResting, error and busy states with realistic content.
The examples below use React and the components in the labs workbench. The same division of work applies to an Angular or Vue library: the framework changes the implementation, while the team still needs shared design choices and examples of the resulting behavior.
Agree on the interaction in Figma
Before implementing the form, we need to know more than where the controls sit. What does a user see when the email address is rejected? Can they keep editing while a save is in progress? Where does an error about the supplier's contract appear if there is no contract field on this screen? These questions affect both the design and the code.
For this example, we decide that field errors appear beside their controls, a summary collects the errors at the top, and the Save button shows a busy state during submission. The form keeps the user's entered values after a failure so they can correct them. Those decisions should appear in the design examples and in the implemented states. A single image of the empty form cannot communicate all of them.
Figma remains useful for exploring layout, reviewing the flow and maintaining the design library. Linking the relevant frame to a Storybook story makes it easier to compare the intention with the implementation. Storybook documents design integrations for that workflow. Where a team uses Figma's Code Connect, it can also map design components to examples using its actual code components. These connections help people find the corresponding artifacts; the team still has to keep their behavior aligned.
Put shared visual choices into tokens
The form should use the same accent color, type scale and spacing as the rest of the application. A design token gives one of those choices a name that implementations can reference. A semantic name such as “accent” describes its purpose, so the value can change for another brand or theme without changing every button.
The labs library expresses those choices as CSS custom properties. For example, its solid Button reads a component override with a shared default:
background: var(--uix-button-accent-bg, var(--uix-accent));
color: var(--uix-button-accent-fg, var(--uix-text-on-accent));
If the product supplies --uix-button-accent-bg, that value applies to the button.
Otherwise it uses the shared accent.
The matching foreground token matters because changing a background alone can make the label unreadable.
The token documentation shows the library's primitive values, semantic roles and component overrides.
I would keep the design and code vocabulary close enough that the team can trace a choice between them. That does not require one enormous token list or a token for every CSS declaration. It requires clarity about which choices are shared, who changes them and how a change reaches the components that use them. If Figma and code maintain separate values, include both in the change rather than assuming a shared name synchronizes them.
Give each component a clear job
Now we can implement the form using the shared controls. The Button owns its visual variants and activation behavior. The email field owns its label and field-message wiring. The form coordinates submission state and errors across those controls. The application supplies the supplier data and decides what saving means.
For example, the labs Button already has a loading property:
<Button type="submit" loading={saving}>
{saving ? "Saving" : "Save"}
</Button>
Here saving comes from the surrounding form's state.
The component displays a spinner and marks itself busy while retaining the visible label.
Its implementation suppresses activation during that state.
We can inspect that behavior once in the shared Button and reuse it in other forms.
The component does not decide whether the backend accepted a supplier change; application code has to update saving and handle the result.
The same separation helps with accessibility. A shared field component can consistently associate the label and error message with its input. The screen still needs meaningful labels, sensible reading order and an interaction that works for the user's task. Using accessible components reduces repeated implementation work, but it cannot establish that every composition is accessible.
Make the states easy to inspect in Storybook
To review the Button's busy state, we should not need to arrange for a production save to remain pending.
A Storybook story can render that state directly.
In the labs repository, the story file sits beside the component at packages/ui/src/components/Button.stories.tsx.
This reduced example uses its actual component API:
import type { Meta, StoryObj } from "@storybook/react-vite";
import { Button } from "./Button";
const meta = {
title: "Components/Button",
component: Button,
} satisfies Meta<typeof Button>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Saving: Story = {
args: { loading: true, children: "Saving" },
};
The default export identifies the component.
The named export describes one example, and args supplies the component properties for that example.
Storybook's story format supports both simple property-driven examples and custom render functions for larger compositions.
Open the Button matrix to compare its variants and states. The matrix is a deliberate review surface: the examples appear together so differences in emphasis, size and busy behavior are visible. A useful catalog makes common choices easy to find and includes the states a team actually needs to discuss. The number of stories is less useful than whether they answer those questions.
We can also attach an interaction test to a story.
A play function uses the rendered controls and checks the expected result, such as whether activating Save submits the entered values.
Storybook's interaction-testing tools run that sequence in the component's environment.
Visual checks, interaction tests and accessibility checks cover different properties, so I would combine them with a human review of the screen.
A screenshot alone cannot show whether an error is announced or whether the keyboard reaches a control.
Reuse the form pattern as well as the controls
A Button and two fields still leave the developer to decide how the form presents its errors and actions. When that arrangement recurs, we can make it an explicit pattern. The pattern might be a documented example, a component with slots, or a template used to begin a new screen. The choice depends on how much behavior is shared and how much the screens need to vary.
The labs Form provides a concrete example. It receives errors keyed by field name; its fields and summary use those names to present the messages. This excerpt renders a supplier contact form in an error state, with fixtures replacing a backend response:
<Form
summaryOn="always"
errors={{ email: "That address is already registered." }}
>
<Form.Summary />
<TextField name="email" label="Email" defaultValue="jane@example.com" />
<Select name="region" label="Supplier region" options={regions} />
<Form.Actions>
<Button type="submit">Save</Button>
</Form.Actions>
</Form>
Form, TextField, Select and Button are imports from @labs/ui.
For this example, regions contains the European Union and United Kingdom options, each with a value and label.
The Form matrix and its source show the complete resting, error and busy examples.
These are UI fixtures; they do not save a supplier record.
At page level, we still need a heading, content width and a layout that works with real text. A page story can combine the form with those surrounding components and show long labels, narrow widths and missing data. Brad Frost's Atomic Design already includes templates and pages alongside smaller components, because the parts need to be considered in context. AI-assisted development makes that context useful to another consumer; it does not create the need for it.
I would add reusable patterns where the product shows repetition, while leaving a route for new designs. A design-system team should help product teams make consistent decisions without requiring every screen to have the same arrangement. When a new form needs a different pattern, review whether it is a useful extension or a one-off requirement before expanding the shared API.
Give the AI assistant access to those decisions
Suppose we now ask an assistant to implement the supplier contact form. A screenshot gives it visual information, but not necessarily the package name, the Form API or the existing error pattern. If we want it to reuse those pieces, the request needs a route to the relevant code and documentation.
Implement the supplier contact form using @labs/ui. Read the Form and Button APIs and their Storybook examples first. Reuse the field-error summary and busy state. Add a story for the composed form and check it at a narrow width.
The labs component inventory is generated from source declarations and documentation. It lists components, properties, compound parts and usage guidance. An assistant with repository access can read that file and open the corresponding implementation and stories. The inventory check detects differences between the generated file and the source; the accuracy of a usage recommendation still depends on what the team wrote.
For assistants that access documentation through tools, the labs also provide a small MCP server.
MCP is a protocol for connecting an agent application to tools and context.
This server offers queries such as describe_component and find_component over the same library information.
It helps the assistant locate an existing component before choosing an API from memory.
Its current implementation loads the inventory at startup, so restart it after regenerating that inventory.
Storybook has its own MCP integration for component documentation, story generation and tests. That integration is currently in preview, with framework-specific support, and is separate from the small inventory server used in these labs. Whichever route you choose, the useful outcome is the same: the assistant can inspect the implementation and examples that the team maintains. It should still render the screen and check the result.
Where I would invest in the design system
I see AI-assisted development as another reason to invest in a usable design system. If a team has already decided how its forms behave, I want those decisions available through components and examples that both developers and assistants can find. That is more useful to me than asking a model to recreate the same form from a picture on every task.
For repeated workflows, my preference is a designed screen or reusable pattern with explicit behavior. An assistant can help implement it, select it or fill it with data. I would introduce runtime-generated layouts where the product has a real need for that variability; when the same arrangement keeps recurring, I would turn it into a maintained pattern. That keeps design effort available for the parts that still need a decision.
A practical place to start is the next form your team is already building. Agree on its important states, implement them with the shared controls, and put the composed result in Storybook. Let the next developer or assistant use those examples, then improve the system where they still have to guess.
Explore the implementation · 14 September 2026
- In the workbench, inspect the Button matrix and the Form matrix. Compare the error summary, field messages and busy controls, then use the keyboard to move through the form.
- Open their component source and stories. Compare Button's
loadingproperty and Form's named errors with the examples above. The labs use React and Storybook 10.5; the snippets are excerpts, not a new library to install. - Compare the generated inventory with those APIs. This walkthrough uses existing implementations; it does not claim a measured improvement in generated-screen quality or an implemented Figma synchronization.
Sources and scope · 14 September 2026
- Storybook documents stories, interaction tests and design integrations. Its AI integration is in preview.
- Figma's Code Connect documentation describes mapping design components to their implementations.
- Brad Frost's Atomic Design methodology includes composition through templates and pages. Component and token behavior in the example was checked against the labs repository.
