• SaaS

Building a design system on shadcn: what you still need to define

18 Oct ‘269 Min Read

Last updated on 26 Oct ‘26

Insights

shadcn/ui gives you accessible components, CSS-variable theming and code you own. A checklist of the eight decisions your design system still needs on top. shadcn/ui is not a finished design system; it is the foundation you build one on.

shadcn/ui is not a finished design system; it is the foundation you build one on. Its documentation says so plainly: "This is not a component library. It is how you build your component library." It gives you accessible components, theming built on CSS variables, and component code in your own repository. Building a design system on shadcn means writing down what it leaves to you: the eight decisions in the checklist below, which make the system belong to your product.

What shadcn gives you, and what it leaves to you

AreaWhat shadcn/ui providesWhat you still define
Behaviour and accessibilityComponents built on a headless library: Base UI by default for new projects, with Radix still supported and React Aria as a third option. These libraries handle ARIA attributes, keyboard navigation and focus management.Visible focus styles, colour contrast, accessible names, and the behaviour of patterns you assemble from several components
Colour and themeSemantic theme tokens such as `background`, `foreground` and `primary`, set as CSS variables, with dark mode overriding the same tokensTokens for what things mean in your product, beyond the default set
ComponentsThe component source, copied into your project and edited directlyWhich components are standard and which are yours, and how added variants are named and recorded
LayoutTailwind CSS breakpoints, five by default, applied mobile-firstWhat each component and page pattern does at each screen size
FigmaA list of community-made Figma kitsWhich file is approved, and how its variables map to the CSS variables in code
UpdatesA command-line `--diff` to compare your files with the registryWho checks for upstream changes, how often, and who decides what gets merged
DistributionA way to run your own registry of components, hooks, pages and configWhat goes into it and who approves an addition

The shadcn command line describes a preset as something that "packs your entire design system config into a short code": colours, theme, icon library, fonts and radius. That is visual configuration. Everything in the right-hand column is a decision about your product, and none of it fits in a preset.

If you are still choosing between adopting a published system, extending open-source code such as shadcn, or building your own, start with what to build, in what order. This page assumes the choice is made.

What Tcules defined on top of shadcn for Phoenix

Phoenix, a decentralised exchange built by Ellipsis Labs on Solana, used shadcn as its foundation; we joined through Kopper as part of the design team. The work was deciding where the product stops being generic. We separated the standard components engineering already had from the trading-specific parts it needed (item 2 below), turned theme colours into tokens (item 1), documented custom variants and wrote screen-size rules down (items 4 and 5), and moved work into one source-of-truth page only once it was ready for development (item 7). We did not own the codebase, and what was built after the January 2026 handoff belonged to the in-house team, so the case shows decisions and handoff, not adoption.

The checklist: eight decisions to write down

In our view, the risk with shadcn is not the code: a well-made set of primitives looks like a finished system, so product decisions drift into each screen instead of being made once. A developer who needs a "pending" badge can add a colour class to the badge file or inline on one screen; both pass review, neither is recorded, and soon the same meaning is spelled two ways (our inference from how copied code behaves, not a measured finding). Each item below starts from what the documentation provides and ends with what to write down.

1. Tokens for your product's meanings

The theming documentation pairs surface tokens such as `primary` with a `-foreground` token for the text on them. The only status colour in the default set is `destructive`, for "destructive actions and error emphasis"; the page's own example of adding a token is a `warning`.

  • Write down every meaning your product shows (success, warning, pending, archived and your domain's own), each as a surface and foreground pair with light and dark values, named for its meaning and checked for contrast.
  • Decide early whether components use CSS variables. The components.json reference says the setting "cannot be changed after initialization"; switching means deleting and reinstalling components.
  • For several brands or themes, see the Design Tokens Community Group's Resolver Module, a Final Community Group Report the group calls stable (it is not a W3C Standard).

2. Where standard stops and product-specific starts

The command line places components using aliases in `components.json`, including one for `ui`. That gives you a folder, not a rule.

  • Write down which components are standard (shadcn-derived, used as shipped or lightly themed) and which are built for your product, keep them apart and labelled, and note which product components wrap a standard one.

3. States for your domain components

Primitives carry interaction states such as open and disabled. They do not know what your data can do.

  • Write down, for each product component, every state and how it looks: loading, empty, error, no permission, partial data, and your domain's own (a table may need "filtered to nothing" as well as "nothing yet").
  • Check the states in the build, not only in the file. On Chestnut, where our designers and engineers built part of the client's front end themselves and compared each feature with its design once built, the build still turned up states that the fully annotated design file had never shown.

4. Every variant you add

shadcn components ship with variants: the Button has default, outline, secondary, ghost, destructive and link. Adding one is a small edit to a file you own, so an added variant can easily go unrecorded.

  • Write down each added variant, what it is for, when to use an existing one instead, and who agreed it.

5. Screen-size rules

Tailwind CSS has five default breakpoints, from `sm` at 40rem to `2xl` at 96rem, applied mobile-first. Breakpoints say where layout can change, not what should change.

  • Write down, for each component and page pattern, what happens at each size you support. Does a data table drop columns, scroll or become a list? Does a side panel become a sheet?

6. Accessibility and content rules for whole patterns

Base UI handles ARIA attributes, keyboard navigation and focus management, and leaves three things with you: "it's the developer's responsibility to visually indicate focus", colour contrast, and accessible names for custom controls. Composed patterns, such as a filter bar or a bulk-action toolbar, raise questions no single primitive's documentation answers.

  • Write down where focus goes after an action, how errors are worded and announced, what a destructive confirmation says, and how icon-only buttons are labelled.

7. The approved Figma file

A Figma kit is a drawing of the shadcn defaults: it carries none of your tokens, components or decisions until someone adds them.

  • Write down which file or page holds approved work, how its variables map to your CSS variables, and the rule that working files are not read as approved.

8. A path for upstream changes

Because you own the copies, upstream fixes do not arrive on their own. The command line can compare a component with the registry (`add --diff`) and preview changes (`--dry-run`, `--view`). Applying a new preset with `shadcn apply` reinstalls your existing components, so take care over files you have edited; partial preset apply (`--only theme`) applies the theme alone.

  • Write down who checks for upstream changes and how often, how edited components are marked so a diff reads correctly, and who decides what is merged.

Choosing a shadcn Figma kit

The shadcn Figma page lists kits, some of them paid, and notes that they "are contributed by the community". Before your team adopts one, check that:

  • its variables use the same names as your CSS variables (`background`, `primary`, `primary-foreground` and so on), with light and dark modes;
  • it matches the shadcn style and component base you installed, and its component variants match the code (the Button's six, for example);
  • someone maintains it as shadcn changes, and its licence lets you edit and extend it;
  • any code a design-to-code plugin generates is reviewed against your written decisions before it is merged, like any other upstream code.

Figma variables hold a value for each mode, such as light and dark, and code syntax lets Dev Mode show a variable's code name, so a developer inspecting a design sees the token rather than a colour value. For the hand-off itself, see keeping a React build true to the Figma design.

Checks for a product already on shadcn

If your product already runs on shadcn, five questions show which decisions are still open:

CheckIf the answer is no
Are all the colours in your product components drawn from tokens, with no raw palette classes such as a typed green?Item 1 is open: a meaning exists without a token
Can a new developer tell, from the folder structure or a single list, which components are standard and which are product-specific?Item 2 is open
For your three most-used product components, can someone name every state and show it?Item 3 is open
Does `add --diff` on an edited component show only changes you can explain?Item 8 is open: edits are unrecorded, so upstream fixes are hard to take
Can anyone on the team name the approved Figma file or page without checking?Item 7 is open

Each "no" is a decision to write down before adding more components, then test in one real product area before rolling it out; that is where our design system implementation work starts.

Reviewed by Devagya Sharma, our Design Engineer.

Turn your shadcn components into a system your team can follow

Let's list the product decisions your shadcn setup still leaves to each screen.

Start a project