Primitives
Package: @zuilib/primitives · React 18 · Headless UI v2 · Tailwind v4 (optional at runtime)
@zuilib/primitives is the component layer of ZUI: forty-odd primitives
that companies build their own design system on. Every visual decision in
them reads a token from @zuilib/tokens, so
customisation is CSS-variable overrides on :root or on any subtree, and
nothing else. Behaviour and accessibility come from Headless UI; the
components add the ZUI styling, sizes, form-state wiring and data-slot
hooks for your own CSS.
pnpm add @zuilib/primitives
import '@zuilib/primitives/zui.css' // or one of the Tailwind entries, see Getting started
import Button from '@zuilib/primitives/button'
What you get
| One import per component | @zuilib/primitives/button, /input, /dialog, …: tree-shakes to what you use. Compound parts are statics (Select.Option) and named exports |
| Tokens only | No raw colours, radii or sizes in the components. Override --primary, --radius, --control-height-md and every button, field and card follows |
| Headless UI state | data-open, data-checked, data-focus, data-disabled, data-invalid on the parts they belong to, plus data-state where Headless UI has nothing |
data-slot on every part | [data-slot="select-option"]: target a part from your own CSS without depending on class names |
| Forms | Field gives its control the id, label, description, error and disabled state; every field has invalid, size, fullWidth, name |
| RSC-safe | Every file starts with 'use client'; import from a server component and it just works |
| Dark mode | The dark class on <html> (or any wrapper) swaps the token values; the components do nothing special |
| Translatable | Every built-in string is a prop and a key of LabelsProvider, so an app translates once |
| Telemetry, opt-in | TelemetryProvider receives open / select / change events from Dialog, Menu, Tabs, Combobox, CommandPalette and tracked Buttons; nothing is emitted without it |
Start with Getting started for the stylesheet
choice, then Theming for how a brand is applied. The
Registry page covers installing single components as
source with the zui CLI.
Small screens and touch
The primitives adapt below the sm breakpoint (40rem) and on a coarse
pointer without a prop:
- Overlays fit the phone. A Dialog is the viewport minus a 1rem gutter with a scrolling body; a start / end Drawer is full-width and a top / bottom one at most 85dvh; the CommandPalette becomes a full-width sheet pinned to the top of the viewport. Drawer, the
fullDialog and toasts pad pastenv(safe-area-inset-*). - Panels stay on screen. Select, Combobox, Menu, Popover and Tooltip panels never exceed
100vw - 2remand cap their height atmin(240px, 100dvh - 2rem). - 44px targets. On a coarse pointer, tabs, menu items, list options and the Table sort button are at least 44px tall; Button, the close and clear buttons, the Checkbox box, Radio indicator, Switch track and Slider thumb take a 44 × 44 hit area without changing size.
touchTargetClassesandtouchHitAreaClassesfrom@zuilib/primitives/lib/sizesgive your own controls the same. - No zoom on focus.
size="sm"fields type at 16px belowsm, so iOS does not zoom when they take focus; desktop stays at 14px (mdwas already 16px). - Layout steps down. Below
sm,mdandlgCard (and Accordion) padding step down to the next token; Table's container ismin-w-0 max-w-fulland scrolls horizontally on its own; Tabs lists scroll horizontally with a hidden scrollbar on phones.
Inventory
Actions
- Button: variants, tones, sizes, loading, polymorphic
as - AIButton: Button with a sparkle and a generating state
Forms
- Fieldset: grouped fields with a shared label and disabled state
- Field: label, control, description and error wired together
- Label, FieldDescription, FieldError: the field text parts
- Input, Textarea, TextareaField
- NumberInput, PinInput, SearchInput
- NativeSelect: the native select, styled
- Select, Combobox: custom selects, single and multiple
- Checkbox, RadioGroup, Switch
- Slider: a native range input on the tokens
- FileUpload: drop zone and file list
- Form: the react-hook-form binding (
Form,FormField,useAutosave)
Overlays
- Dialog: modal with backdrop, title and description
- Drawer: a side panel
- Popover: anchored panel
- Menu: actions list with keyboard navigation
- Tooltip: hover / focus hint
- CommandPalette: ⌘K launcher with groups, recent items and a global shortcut
Navigation
Feedback
- Alert: inline status message
- Progress, Spinner, Skeleton
- EmptyState
- Toast: notifications on Sonner, styled through the tokens
Data display
Layout
Typography
Companions
@zuilib/data-grid (server-driven grid),
@zuilib/charts, @zuilib/ai and
@zuilib/apps builds on these components and shares the
same tokens. The text editor too.