Building a Design System: React, Storybook, and Design Tokens
Build a React design system from scratch with design tokens, accessible components, Storybook documentation, and theming. The guide that goes past the button tutorial.

Why Build a Design System
Every team that builds more than one product eventually has the same conversation. The button on the dashboard looks different from the button on the marketing site. The spacing is inconsistent. Someone copy-pasted a component three months ago and now there are two versions that have drifted apart. A new developer joins and asks "which one is the right button?" and nobody is completely sure.
A design system solves this by making "the right way" the easy way. Instead of copy-pasting components and hoping for consistency, you build a shared library of components, tokens, and documentation that every project pulls from. The button is defined once. If it changes, it changes everywhere.
This guide builds a React design system from scratch. Not a toy example with one button. A real foundation with design tokens, accessible components, Storybook documentation, theming, and testing. The same approach we use when building design systems for clients.
By the end, you will have a working component library with design tokens as CSS custom properties, a set of foundation components with accessibility built in, Storybook for documentation and visual testing, and light/dark theme support.
Prerequisites
- Node.js 18 or later
- A React project (existing or new). If starting fresh,
npx create-react-app my-design-system --template typescriptornpm create vite@latest my-design-system -- --template react-tsboth work. - Familiarity with React and TypeScript. You should be comfortable creating components with props, using hooks, and working with TypeScript interfaces.
- Basic CSS knowledge. You should understand selectors, properties, and how CSS custom properties (variables) work.
Setting Up Storybook
Storybook is the documentation and development environment for your design system. It renders your components in isolation, lets you interact with them, and serves as the living documentation that stays in sync with your code because it runs your actual components.
Install Storybook in your project:
# Initialize Storybook in your project
npx storybook@latest initThis command detects your project setup (React, TypeScript, Vite/Webpack) and installs the right dependencies. It creates a .storybook/ configuration directory and a src/stories/ directory with example stories. You can delete the example stories. You will write your own.
Start Storybook to confirm it works:
# Start the Storybook development server
npm run storybookStorybook opens in your browser at http://localhost:6006. You should see the example stories. Once confirmed, delete the src/stories/ directory and its contents. Clean slate.
Folder Structure
Your design system components should live in a dedicated directory. Here is the structure we use:
# Design system folder structure
src/
├── design-system/
│ ├── tokens/
│ │ ├── colors.css # Color tokens
│ │ ├── spacing.css # Spacing scale
│ │ ├── typography.css # Font sizes, weights, line heights
│ │ └── index.css # Imports all token files
│ ├── components/
│ │ ├── Button/
│ │ │ ├── Button.tsx
│ │ │ ├── Button.css
│ │ │ ├── Button.stories.tsx
│ │ │ └── index.ts
│ │ ├── Input/
│ │ ├── Text/
│ │ └── Stack/
│ └── index.ts # Public API barrel exportEach component gets its own folder with the component file, styles, stories, and a barrel export. Tokens live in a separate tokens/ directory because they are consumed by every component.
Design Tokens
Design tokens are the values that define your visual language: colors, spacing, font sizes, border radii, shadows. They are the single source of truth for how things look. When a designer says "use the primary color," the token is what they mean.
We define tokens as CSS custom properties because they work everywhere (components, global styles, Storybook), they support theming natively (just redefine them in a different context), and they do not require a build step or runtime JavaScript.
Color Tokens
/* src/design-system/tokens/colors.css */
/* Color tokens as CSS custom properties */
:root {
/* Brand colors */
--color-primary: #2563eb;
--color-primary-hover: #1d4ed8;
--color-primary-active: #1e40af;
/* Neutral palette */
--color-gray-50: #f9fafb;
--color-gray-100: #f3f4f6;
--color-gray-200: #e5e7eb;
--color-gray-300: #d1d5db;
--color-gray-400: #9ca3af;
--color-gray-500: #6b7280;
--color-gray-600: #4b5563;
--color-gray-700: #374151;
--color-gray-800: #1f2937;
--color-gray-900: #111827;
/* Semantic colors */
--color-success: #059669;
--color-warning: #d97706;
--color-error: #dc2626;
/* Surface colors */
--color-background: #ffffff;
--color-surface: #f9fafb;
--color-border: #e5e7eb;
/* Text colors */
--color-text-primary: #111827;
--color-text-secondary: #6b7280;
--color-text-inverse: #ffffff;
}Spacing Tokens
/* src/design-system/tokens/spacing.css */
/* Spacing scale based on a 4px base unit */
:root {
--space-1: 0.25rem; /* 4px */
--space-2: 0.5rem; /* 8px */
--space-3: 0.75rem; /* 12px */
--space-4: 1rem; /* 16px */
--space-5: 1.25rem; /* 20px */
--space-6: 1.5rem; /* 24px */
--space-8: 2rem; /* 32px */
--space-10: 2.5rem; /* 40px */
--space-12: 3rem; /* 48px */
--space-16: 4rem; /* 64px */
}A 4px base unit keeps spacing consistent and creates a visible rhythm in your layouts. Everything is a multiple of 4. This is not arbitrary. It aligns with how most design tools work and makes spacing decisions automatic instead of per-element.
Typography Tokens
/* src/design-system/tokens/typography.css */
/* Typography scale and font settings */
:root {
/* Font families */
--font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI",
sans-serif;
--font-mono: "JetBrains Mono", "Fira Code", monospace;
/* Font sizes */
--text-xs: 0.75rem; /* 12px */
--text-sm: 0.875rem; /* 14px */
--text-base: 1rem; /* 16px */
--text-lg: 1.125rem; /* 18px */
--text-xl: 1.25rem; /* 20px */
--text-2xl: 1.5rem; /* 24px */
--text-3xl: 1.875rem; /* 30px */
--text-4xl: 2.25rem; /* 36px */
/* Font weights */
--font-normal: 400;
--font-medium: 500;
--font-semibold: 600;
--font-bold: 700;
/* Line heights */
--leading-tight: 1.25;
--leading-normal: 1.5;
--leading-relaxed: 1.75;
/* Border radius */
--radius-sm: 0.25rem;
--radius-md: 0.375rem;
--radius-lg: 0.5rem;
--radius-full: 9999px;
}Importing Tokens
Create an index file that imports all token files:
/* src/design-system/tokens/index.css */
/* Import all token files */
@import "./colors.css";
@import "./spacing.css";
@import "./typography.css";Import this in your Storybook preview so tokens are available to all stories:
// .storybook/preview.ts
// Load design tokens for all stories
import "../src/design-system/tokens/index.css";
const preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
};
export default preview;Building Foundation Components
Foundation components are the building blocks everything else is made from. Start small: Button, Input, Text, and Stack. These four cover most UI needs and establish the patterns every future component will follow.
Button
The Button is usually the first component in any design system, and also the one that shows whether accessibility was an afterthought or built in from the start.
// src/design-system/components/Button/Button.tsx
// Button component with variants, sizes, and accessibility
import { type ButtonHTMLAttributes, type ReactNode } from "react";
import "./Button.css";
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: "primary" | "secondary" | "ghost";
size?: "sm" | "md" | "lg";
children: ReactNode;
}
export function Button({
variant = "primary",
size = "md",
children,
className = "",
...props
}: ButtonProps) {
return (
<button
className={`ds-button ds-button--${variant} ds-button--${size} ${className}`}
{...props}
>
{children}
</button>
);
}/* src/design-system/components/Button/Button.css */
/* Button styles using design tokens */
.ds-button {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--space-2);
font-family: var(--font-sans);
font-weight: var(--font-medium);
border-radius: var(--radius-md);
border: 1px solid transparent;
cursor: pointer;
transition: background-color 0.15s ease, border-color 0.15s ease;
line-height: var(--leading-tight);
}
/* Sizes */
.ds-button--sm {
padding: var(--space-1) var(--space-3);
font-size: var(--text-sm);
}
.ds-button--md {
padding: var(--space-2) var(--space-4);
font-size: var(--text-base);
}
.ds-button--lg {
padding: var(--space-3) var(--space-6);
font-size: var(--text-lg);
}
/* Primary variant */
.ds-button--primary {
background-color: var(--color-primary);
color: var(--color-text-inverse);
}
.ds-button--primary:hover {
background-color: var(--color-primary-hover);
}
.ds-button--primary:active {
background-color: var(--color-primary-active);
}
/* Secondary variant */
.ds-button--secondary {
background-color: transparent;
color: var(--color-primary);
border-color: var(--color-primary);
}
.ds-button--secondary:hover {
background-color: var(--color-primary);
color: var(--color-text-inverse);
}
/* Ghost variant */
.ds-button--ghost {
background-color: transparent;
color: var(--color-text-primary);
}
.ds-button--ghost:hover {
background-color: var(--color-gray-100);
}
/* Focus styles for keyboard navigation */
.ds-button:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
/* Disabled state */
.ds-button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
@media (prefers-reduced-motion: reduce) {
.ds-button {
transition: none;
}
}A few things to notice. The ds- prefix prevents conflicts with other CSS in your project. All visual values come from tokens, not hardcoded colors or sizes. The :focus-visible state uses a visible outline for keyboard users without showing it on mouse clicks. The disabled state uses cursor: not-allowed so it is visually obvious. And prefers-reduced-motion disables transitions for users who have asked for reduced motion.
Text
A Text component standardizes typography across your system:
// src/design-system/components/Text/Text.tsx
// Text component for consistent typography
import { type ElementType, type HTMLAttributes, type ReactNode } from "react";
import "./Text.css";
interface TextProps extends HTMLAttributes<HTMLElement> {
as?: ElementType;
size?: "xs" | "sm" | "base" | "lg" | "xl" | "2xl" | "3xl" | "4xl";
weight?: "normal" | "medium" | "semibold" | "bold";
color?: "primary" | "secondary" | "inverse";
children: ReactNode;
}
export function Text({
as: Component = "p",
size = "base",
weight = "normal",
color = "primary",
children,
className = "",
...props
}: TextProps) {
return (
<Component
className={`ds-text ds-text--${size} ds-text--${weight} ds-text--${color} ${className}`}
{...props}
>
{children}
</Component>
);
}/* src/design-system/components/Text/Text.css */
/* Text styles mapping to typography tokens */
.ds-text {
font-family: var(--font-sans);
line-height: var(--leading-normal);
margin: 0;
}
/* Sizes */
.ds-text--xs { font-size: var(--text-xs); }
.ds-text--sm { font-size: var(--text-sm); }
.ds-text--base { font-size: var(--text-base); }
.ds-text--lg { font-size: var(--text-lg); }
.ds-text--xl { font-size: var(--text-xl); }
.ds-text--2xl { font-size: var(--text-2xl); }
.ds-text--3xl { font-size: var(--text-3xl); }
.ds-text--4xl { font-size: var(--text-4xl); }
/* Weights */
.ds-text--normal { font-weight: var(--font-normal); }
.ds-text--medium { font-weight: var(--font-medium); }
.ds-text--semibold { font-weight: var(--font-semibold); }
.ds-text--bold { font-weight: var(--font-bold); }
/* Colors */
.ds-text--primary { color: var(--color-text-primary); }
.ds-text--secondary { color: var(--color-text-secondary); }
.ds-text--inverse { color: var(--color-text-inverse); }The as prop lets you render the correct semantic element. A heading should be <h2>, not a <p> styled to look like a heading. Screen readers navigate by heading level, so semantic HTML matters here.
Stack
A Stack component handles vertical and horizontal spacing between elements. This is the layout primitive that replaces adding margin to individual components:
// src/design-system/components/Stack/Stack.tsx
// Stack component for consistent spacing between elements
import { type HTMLAttributes, type ReactNode } from "react";
import "./Stack.css";
interface StackProps extends HTMLAttributes<HTMLDivElement> {
direction?: "vertical" | "horizontal";
gap?: "1" | "2" | "3" | "4" | "6" | "8";
align?: "start" | "center" | "end" | "stretch";
children: ReactNode;
}
export function Stack({
direction = "vertical",
gap = "4",
align = "stretch",
children,
className = "",
...props
}: StackProps) {
return (
<div
className={`ds-stack ds-stack--${direction} ds-stack--gap-${gap} ds-stack--align-${align} ${className}`}
{...props}
>
{children}
</div>
);
}/* src/design-system/components/Stack/Stack.css */
/* Stack layout component */
.ds-stack {
display: flex;
}
.ds-stack--vertical { flex-direction: column; }
.ds-stack--horizontal { flex-direction: row; }
/* Gap values mapping to spacing tokens */
.ds-stack--gap-1 { gap: var(--space-1); }
.ds-stack--gap-2 { gap: var(--space-2); }
.ds-stack--gap-3 { gap: var(--space-3); }
.ds-stack--gap-4 { gap: var(--space-4); }
.ds-stack--gap-6 { gap: var(--space-6); }
.ds-stack--gap-8 { gap: var(--space-8); }
/* Alignment */
.ds-stack--align-start { align-items: flex-start; }
.ds-stack--align-center { align-items: center; }
.ds-stack--align-end { align-items: flex-end; }
.ds-stack--align-stretch { align-items: stretch; }Stack keeps spacing out of individual components. A Button does not need margin. The parent Stack decides how much space goes between its children. This makes components more reusable because they do not carry layout assumptions with them.
Accessibility from the Start
Accessibility in a design system is not a phase. It is a set of decisions you make for every component. When you get it right at the system level, every project that uses your system inherits accessible components automatically.
Focus Management
Every interactive component needs a visible focus indicator. We already added :focus-visible to the Button. Apply the same pattern to every interactive component in your system:
/* Standard focus style for all interactive components */
.ds-input:focus-visible,
.ds-select:focus-visible,
.ds-checkbox:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}Use :focus-visible instead of :focus. The difference: :focus shows the outline on mouse clicks too, which users find distracting. :focus-visible only shows it during keyboard navigation, which is when it is actually needed.
ARIA Patterns
For complex components like modals, tabs, and dropdowns, follow the WAI-ARIA Authoring Practices. Here is the pattern for a simple Input component with proper labeling:
// src/design-system/components/Input/Input.tsx
// Input component with built-in labeling and error states
import { type InputHTMLAttributes, useId } from "react";
import "./Input.css";
interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
label: string;
error?: string;
hint?: string;
}
export function Input({
label,
error,
hint,
className = "",
...props
}: InputProps) {
const id = useId();
const errorId = error ? `${id}-error` : undefined;
const hintId = hint ? `${id}-hint` : undefined;
const describedBy = [errorId, hintId].filter(Boolean).join(" ") || undefined;
return (
<div className={`ds-input-wrapper ${className}`}>
<label htmlFor={id} className="ds-input-label">
{label}
{props.required && (
<span className="ds-input-required" aria-label="required">
*
</span>
)}
</label>
{hint && (
<p id={hintId} className="ds-input-hint">
{hint}
</p>
)}
<input
id={id}
className={`ds-input ${error ? "ds-input--error" : ""}`}
aria-invalid={error ? true : undefined}
aria-describedby={describedBy}
{...props}
/>
{error && (
<p id={errorId} className="ds-input-error" role="alert">
{error}
</p>
)}
</div>
);
}Notice what is happening here. The label prop is required, not optional. You cannot create an Input without a label because unlabeled inputs are an accessibility failure. The useId hook generates unique IDs so the label's for attribute connects to the input. Error messages use role="alert" so screen readers announce them when they appear. The aria-describedby attribute links the input to its hint and error messages so screen readers read them in context.
This is what "accessibility built in" means. A developer using this Input component gets proper labeling, error handling, and ARIA attributes without knowing anything about WCAG. The system handles it.
Color Contrast
Your token values need to pass WCAG contrast requirements. Text on backgrounds needs a 4.5:1 ratio for normal text and 3:1 for large text. Check your token combinations:
| Combination | Ratio | Passes |
|---|---|---|
--color-text-primary on --color-background | 17.4:1 | Yes |
--color-text-secondary on --color-background | 4.6:1 | Yes |
--color-text-inverse on --color-primary | 4.6:1 | Yes |
--color-error on --color-background | 4.5:1 | Yes |
Check every text/background combination you define with the WebAIM Contrast Checker. A design system with failing contrast ratios means every project using it inherits the violation.
Component Documentation in Storybook
Stories are how you document your components. Each story shows a specific state or use case. Good stories let someone browse your Storybook and understand what every component does, what props it accepts, and how it looks in different configurations.
Writing Stories
// src/design-system/components/Button/Button.stories.tsx
// Button stories showing all variants and states
import type { Meta, StoryObj } from "@storybook/react";
import { Button } from "./Button";
const meta: Meta<typeof Button> = {
title: "Components/Button",
component: Button,
argTypes: {
variant: {
control: "select",
options: ["primary", "secondary", "ghost"],
},
size: {
control: "select",
options: ["sm", "md", "lg"],
},
},
};
export default meta;
type Story = StoryObj<typeof Button>;
export const Primary: Story = {
args: {
children: "Button",
variant: "primary",
},
};
export const Secondary: Story = {
args: {
children: "Button",
variant: "secondary",
},
};
export const Ghost: Story = {
args: {
children: "Button",
variant: "ghost",
},
};
export const Small: Story = {
args: {
children: "Small Button",
size: "sm",
},
};
export const Large: Story = {
args: {
children: "Large Button",
size: "lg",
},
};
export const Disabled: Story = {
args: {
children: "Disabled",
disabled: true,
},
};Story Organization
Organize stories by component, not by page or feature. Each component folder contains its own .stories.tsx file. In your Storybook config, you can organize the sidebar with the title field:
// Story title conventions
// "Components/Button" -> Components > Button
// "Components/Input" -> Components > Input
// "Layout/Stack" -> Layout > Stack
// "Tokens/Colors" -> Tokens > ColorsInteractive Controls
The argTypes in your meta configuration create interactive controls in the Storybook UI. Reviewers can change props in real time without editing code. This is one of the most valuable features of Storybook because it lets designers and developers explore component behavior without setting up a development environment.
Theming
Design tokens as CSS custom properties make theming straightforward. Define a dark theme by overriding the relevant tokens:
/* src/design-system/tokens/colors.css */
/* Dark theme overrides */
[data-theme="dark"] {
--color-background: #111827;
--color-surface: #1f2937;
--color-border: #374151;
--color-text-primary: #f9fafb;
--color-text-secondary: #9ca3af;
--color-primary: #3b82f6;
--color-primary-hover: #60a5fa;
--color-primary-active: #2563eb;
}To switch themes, toggle the data-theme attribute on the root element:
// Theme toggle implementation
// Switches between light and dark themes
function ThemeToggle() {
const toggleTheme = () => {
const current = document.documentElement.getAttribute("data-theme");
const next = current === "dark" ? "light" : "dark";
document.documentElement.setAttribute("data-theme", next);
};
return (
<button onClick={toggleTheme} aria-label="Toggle theme">
Toggle Theme
</button>
);
}Every component that uses tokens automatically adapts. No component code changes. No conditional class names. The tokens change, and the components follow.
Previewing Themes in Storybook
Add a theme switcher to Storybook so you can preview components in both themes:
// .storybook/preview.ts
// Theme switcher for Storybook
import "../src/design-system/tokens/index.css";
const preview = {
globalTypes: {
theme: {
description: "Theme",
defaultValue: "light",
toolbar: {
title: "Theme",
items: [
{ value: "light", title: "Light" },
{ value: "dark", title: "Dark" },
],
},
},
},
decorators: [
(Story, context) => {
document.documentElement.setAttribute(
"data-theme",
context.globals.theme
);
return <Story />;
},
],
};
export default preview;This adds a theme toggle to the Storybook toolbar. Click between light and dark to see every component in both themes.
Testing Components
A design system without tests is a design system that will break. Two types of testing matter most: accessibility tests and interaction tests.
Accessibility Testing
Use jest-axe or vitest-axe to run automated accessibility checks on every component:
// src/design-system/components/Button/Button.test.tsx
// Accessibility and interaction tests for Button
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { axe, toHaveNoViolations } from "jest-axe";
import { Button } from "./Button";
expect.extend(toHaveNoViolations);
describe("Button", () => {
it("has no accessibility violations", async () => {
const { container } = render(
<Button>Click me</Button>
);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
it("calls onClick when clicked", async () => {
const handleClick = jest.fn();
render(<Button onClick={handleClick}>Click</Button>);
await userEvent.click(screen.getByRole("button"));
expect(handleClick).toHaveBeenCalledTimes(1);
});
it("does not call onClick when disabled", async () => {
const handleClick = jest.fn();
render(
<Button onClick={handleClick} disabled>
Click
</Button>
);
await userEvent.click(screen.getByRole("button"));
expect(handleClick).not.toHaveBeenCalled();
});
it("renders all variants without violations", async () => {
const variants = ["primary", "secondary", "ghost"] as const;
for (const variant of variants) {
const { container } = render(
<Button variant={variant}>Test</Button>
);
const results = await axe(container);
expect(results).toHaveNoViolations();
}
});
});Visual Regression Testing
Visual regression tests catch unintended visual changes. Storybook's test runner can compare screenshots between builds:
# Install the Storybook test runner
npm install --save-dev @storybook/test-runnerAdd a test script to your package.json:
{
"scripts": {
"test-storybook": "test-storybook"
}
}# Run visual tests against a running Storybook instance
npm run test-storybookThis runs your stories as tests, checking that they render without errors. For full screenshot comparison, add @storybook/addon-visual-tests or use a service like Chromatic.
Publishing and Consuming the System
Once your design system is ready for use across projects, you need a way to distribute it.
Package Setup
Add the necessary fields to your package.json:
{
"name": "@yourorg/design-system",
"version": "1.0.0",
"main": "src/design-system/index.ts",
"files": [
"src/design-system"
],
"peerDependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
}Your barrel export defines the public API:
// src/design-system/index.ts
// Public API for the design system
export { Button } from "./components/Button";
export { Input } from "./components/Input";
export { Text } from "./components/Text";
export { Stack } from "./components/Stack";Distribution Options
npm registry: Publish to npm (public or private) for the widest compatibility. Every project installs the package and imports components normally.
Monorepo: If all consuming projects live in the same monorepo, the design system is just another package in the workspace. No publishing step needed. Tools like Turborepo, Nx, or Yarn workspaces handle the linking.
Git submodule or package: For small teams, you can reference the design system repository directly in package.json:
{
"dependencies": {
"@yourorg/design-system": "github:yourorg/design-system#v1.0.0"
}
}Versioning
Use semantic versioning. When components change in ways that might break consuming projects (prop renames, removed variants, changed defaults), bump the major version. When you add new components or new props, bump the minor version. When you fix bugs without changing the API, bump the patch version.
Document breaking changes in a changelog. The team consuming your design system needs to know what changed and what they need to update. A design system without a changelog is a design system that people stop updating because they are afraid of what might break.
Common Questions
What people usually ask when building a design system.
Start with 4-6 foundation components: Button, Input, Text, Stack, and maybe Card and Modal. These cover the majority of UI needs. Add components when you need them in real projects, not because they might be useful someday. A design system with 10 well-built components is more useful than one with 50 components that are half-finished.
For a shared design system, plain CSS with a component prefix (like ds-) or CSS modules are the safest choices. They have zero runtime cost and work in any React setup. Tailwind works well for application code but creates coupling between your design system and a specific Tailwind config. Styled-components and Emotion add a runtime dependency and can cause issues with server components. Pick what works for your team, but prefer options with fewer dependencies.
It helps but it is not required to start. The most important thing is that your tokens (colors, spacing, typography) match between Figma and code. You can sync them manually at first and add tooling like Style Dictionary or Tokens Studio later. Start with the code side. The Figma side can follow.
Make it easier to use the system than to not use it. Good documentation in Storybook, easy installation, and components that handle edge cases (loading states, error states, accessibility) out of the box. If someone can build a form faster with your Input component than with a plain HTML input, they will use it. If it is harder, they will not, no matter how many Slack messages you send about it.
Use an existing system when you need to move fast and your design does not need to be highly custom. Build your own when your brand requires a distinct visual identity, when you need full control over accessibility patterns, or when you are maintaining multiple products that need to look and behave consistently. Many teams start with a headless library like Radix for behavior and add their own styling on top. That gives you the accessibility and interaction patterns without starting from zero.