From Button to a Design System
What you learn in this chapter
So far, using Button as the subject, you have learned many techniques. In the final chapter, you learn the perspective of bringing them into "a single design system". Let's confirm that what you gained with Button has been the design principles of a design system all along.
What was condensed into Button
In the introduction, I wrote that "Button packs in the elements of a design system". Here is the answer key.
| What you learned with Button | The design-system principle |
|---|---|
Design tokens (bg-primary) | Manage color and spacing by meaning and keep them consistent |
| Variants with cva | Define the kinds of a component declaratively |
| Safe merging with cn | Let consumers customize safely |
| Slot / asChild | Compose components |
| TypeScript types | Guarantee usage with types |
| focus-visible / role | Build in accessibility from the start |
These are not just for Button. They are "pillars of good design" common to all components.
The three layers of a design system
You can organize a design system by thinking in roughly three layers.
③ Patterns / compounds
Forms, card + button, dialogs…
② Primitive components
Button, Input, Badge, Card, Alert…
① Design tokens
Color, spacing, corner radius, typography (@theme / OKLCH)
※ The lower layer (①) supports the upper layer (③)
- ① Tokens: the foundation of everything. The values like
--primaryyou learned in the design tokens chapter - ② Primitives: the smallest-unit components that use tokens. Button and Input
- ③ Patterns: larger groupings that combine primitives
Button is exactly ②. It sits on top of ① and gets incorporated into ③.
Where does consistency come from
In the advanced chapter, you built Button, Badge, and Alert with the same pattern. Consistency comes from the following two kinds of "commonality".
1. Common tokens
All components reference the same --primary or --destructive.
--primary (defined in one place)
↓ everything references it in common
Button / Badge / Alert … (all reference bg-primary)
Just changing --primary once changes the lead color of all components at the same time. This is the power of putting tokens at the center.
2. Common patterns
Every component uses the same way of building: cn + ...props as the foundation, plus cva and asChild as needed. When the way of building is uniform, you can build new components without hesitation, and readers understand them right away.
File structure and naming
shadcn/ui's standard structure directly reflects the three layers of a design system.
src/
├─ index.css # ① design tokens (@theme / :root / .dark)
├─ lib/
│ └─ utils.ts # the cn function (a tool shared by all components)
└─ components/
└─ ui/ # ② primitive components
├─ button.tsx
├─ badge.tsx
├─ input.tsx
├─ card.tsx
└─ alert.tsx
Naming also has a consistent set of rules.
- Tokens:
name/name-foreground(the background and the text on top of it) - Variants:
variant(the look) /size(the size) data-slot="button"and the like: markers for styling and identification
When the rules are set, you can tell the role just by looking at the code.
Documentation
As a design system grows, you want a place where you can list "what components exist and what variants they have". A representative tool is Storybook.
// button.stories.tsx (a sketch)
export const AllVariants = () => (
<div className="flex gap-2">
<Button variant="default">Default</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="outline">Outline</Button>
</div>
)
With such a catalog, you can talk with designers and other developers "while looking at the same thing", and you prevent reinventing the wheel (each person building a similar button).
Operating and extending it as a team
Here are the points for using and growing a design system as a team.
- New colors go into tokens first: instead of writing
bg-green-500directly in a component, define a--successtoken and then use it (the exercise in the hands-on practice chapter was this) - New looks go into a cva variant: when extending an existing component, just add one line to
variants - Accessibility from the start: do not put off
focus-visible,role, andaria-* - Be aware of the impact range of token changes: that one change ripples across the whole is an advantage, but when you change it, confirm the impact range
Start small and grow the design system
You do not need to build a perfect design system from the start. Like this book, start from a single Button and add the components you need with the same pattern, and it naturally grows into a consistent system.
Button only → + Input / Badge → + Card / Alert → forms and dialogs…
(start small) (expand gradually)
The next steps
After finishing this book, you can broaden your learning in directions like the following.
- shadcn/ui's complex components: Dialog, DropdownMenu, Form, and others that need integration with Radix UI
- Implementing dark mode: switch the
.darkclass withnext-themesand the like - Integration with design tools: align Figma's design tokens with the code's tokens
- Forms: validation with React Hook Form + Zod
All of them lie on the extension of "tokens, cva, cn, composition, and types" you learned in this book.
Book summary
Well done. Through this book, you learned the following.
- Design tokens: manage color and spacing by meaning (the design tokens chapter)
- cva: manage variants declaratively (the cva chapter)
- cn: merge classes safely with clsx + tailwind-merge (the cn chapter)
- ref: access the DOM in React 19 (the ref chapter)
- Slot: compose elements with asChild (the Slot chapter)
- TypeScript: type-safe components (the TypeScript chapter)
And finally, you saw that they connect into a single design system.
You can design reusable components in line with design-system best practices.
This was the goal set in the introduction. From the small entry point of Button, you should have reached its full picture. Please make use of it in your real projects.