A design system in three layers
Built in three layers, so a decision lives in one of them rather than in all of them. Color is settled in the tokens, behavior in the base, appearance here.
Every decision here is one of three kinds: what a value is, how a thing behaves, how it looks. One layer answers each, so changing a color does not mean opening the file that traps focus. There are four packages, because base-react is built on Radix and the behavior Radix does not cover is kept separately in behavior-react.
style-tokensColor, spacing, radius, type, shadow and motion. One source, four outputs: CSS variables, TypeScript objects, utility classes, and a Tailwind v4 theme.
base-reactEighteen headless components: seventeen wrapping a Radix package each, and Button, which is ours. Keyboard, focus and ARIA, and no styling at all.
behavior-reactInteraction Radix does not cover. usePress, so a link or a div styled as a button still answers the keyboard.
components-reactTwenty-three components plus Theme. The only layer that decides what anything looks like.
Components
Compound where the parts need shared state or layout, flat where they do not. The ones marked compound expose their parts; the rest take props, because a Button.Label would be ceremony.
Body copy at one of ten steps
Level and size, decided separately
Material Symbols glyph
Indeterminate progress
Divider between sections
Four variants, three sizes
Filter, tag, toggle
Reports state, not interactive
Sectioned container
Inline message
Image with fallback
Wires label, control, description and error
Text and multi-line
Including indeterminate
One of a few
Takes effect immediately
One of many
Modal, focus trapped
Label on hover or focus
Actions from a trigger
Roving focus, arrow keys
Collapsible sections
Installation
pnpm add @minuk-hwang-design-system/components-reactUsage
One stylesheet at the root, then a component where it is used.
// Once, at the root of the app. It imports the token stylesheet in turn.import '@minuk-hwang-design-system/components-react/styles.css';
// Per component, and only the JavaScript is per component.import { Button } from '@minuk-hwang-design-system/components-react/button';
export default function App() { return <Button>Publish</Button>;}styles.css carries a reset, so it belongs before any stylesheet of your own. Import it from the top of one, where the order cannot be rearranged.Theme
Theme paints the page and is where the dials live: appearance, accentColor, neutralColor, radius. Every one of them is optional, and left alone the appearance follows the operating system.
import { Theme } from '@minuk-hwang-design-system/components-react/theme';
<Theme appearance="dark" accentColor="purple" neutralColor="slate" radius="large"> <App /></Theme>