Codync
A free, open-source 1:1 alternative to Grok Bot and Muse. Message Claude Code, Codex, Cursor and 40+ coding agents as persistent bots from your iPhone, Mac or Linux desktop. Group chats, threads, appr
Open-source React component library built with Tailwind CSS: scoped light and dark themes, accessible primitives, and ESM + CJS builds.
Developer toolsTypeScript · CSSGitHub @bzyncOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Composable React UI library with Tailwind CSS v4 and scoped theming.
Taken from the repository README
Loading the full README.
Read the full README ↗Composable React UI components with scoped, fully customizable light and dark themes.
@bzync/rui (react ui) is the component library behind Bzync.
It ships 100+ components plus a small SVG chart set, a token-driven theming system, and
production-grade lifecycle primitives — with zero runtime dependencies.
dependencies is empty. react, react-dom, and
framer-motion are peer dependencies you already control.ThemeProvider renders a .rui-theme scope, so you can run
multiple themes on one page or theme a subtree without touching <html>.palette prop — per theme.prefers-reduced-motion support.sideEffects metadata
for tree-shaking, and readable (unminified) published output."use client"; server components can
import and render them directly.npm install @bzync/rui framer-motion
pnpm add @bzync/rui framer-motion
yarn add @bzync/rui framer-motion
react and react-dom (^18.2.0 || ^19.0.0) and framer-motion (^13.1.0) are peer
dependencies. framer-motion powers the animated overlays (Modal, Drawer, Popover,
Snackbar); it is kept off the critical path and loaded only by the components that use it.
1. Load the styles. Pick the line that matches your app. Tailwind v4 apps need the first one: it adds the token names your own classes use.
/* Tailwind CSS v4 app: in your CSS entry, after tailwindcss */
@import "tailwindcss";
@import "@bzync/rui/tailwind.css";
// No Tailwind in your app: import the compiled stylesheet once, near the root
import "@bzync/rui/styles.css"
Importing only
styles.cssinto a Tailwind app looks like it works: the token classes the library uses itself (bg-success,bg-accent-500/10) are compiled in. Any other token class (bg-accent-50, bareborder-success,dark:bg-accent-500/15) renders nothing, with no warning.tailwind.cssimportsstyles.cssandtheme.cssin the right order, so this can't happen.
2. Wrap your app in ThemeProvider.
import { Button, ThemeProvider, ThemeToggle } from "@bzync/rui"
const violet = {
50: "#f5f3ff", 100: "#ede9fe", 200: "#ddd6fe", 300: "#c4b5fd",
400: "#a78bfa", 500: "#8b5cf6", 600: "#7c3aed", 700: "#6d28d9",
800: "#5b21b6", 900: "#4c1d95", 950: "#2e1065",
} as const
export function App() {
return (
<ThemeProvider
applyToRoot
palette={{ accent: violet }}
lightPalette={{ tokens: { "--color-bg": "#fafafa", "--color-surface": "#fff" } }}
darkPalette={{ tokens: { "--color-bg": "#09090b", "--color-surface": "#18181b" } }}
>
<ThemeToggle />
<Button>Custom primary</Button>
</ThemeProvider>
)
}
Every component accepts className (and native element props where applicable), so
one-off changes compose with the defaults instead of fighting them.
Import the core stylesheet once, near the application root:
import "@bzync/rui/styles.css"
It defines the semantic design tokens, the .rui-theme scope, the dark-mode variant,
and component styles. It does not download or bundle webfonts — @bzync/rui uses
system fallbacks by default to keep the stylesheet small and avoid unexpected network
requests. Load any font in your application and override --font-sans, --font-display,
and --font-mono on .rui-theme (or via ThemeProvider's fonts option).
Required for Tailwind apps. styles.css is compiled CSS, so your app's Tailwind build
does not learn the library's token names from it. To write bg-success,
text-danger-strong, border-info/25, bg-accent-500, shadow-floating, and so on in
your own markup, import tailwind.css (styles plus token names) into your Tailwind v4
entry, as in the Quick start:
@import "tailwindcss";
@import "@bzync/rui/tailwind.css";
If you already load styles.css from JavaScript, import just the token names instead:
@import "tailwindcss";
@import "@bzync/rui/theme.css";
theme.css only registers the names (@theme reference) and emits no CSS. The values
still come from styles.css and ThemeProvider at runtime, so palette overrides apply
to your utilities too.
ThemeProvider establishes a theme scope. Place one near the root, or wrap any subtree
to give it its own theme.
import { ThemeProvider } from "@bzync/rui"
<ThemeProvider
defaultTheme="system" // "light" | "dark" | "system"
storageKey="app-theme" // localStorage key, or false to disable persistence
applyToRoot // also toggle the `dark` class on <html>
palette={{ accent, neutral, radius, fonts, spacing, shadows, colors, tokens }}
lightPalette={{ /* overrides applied only in light mode */ }}
darkPalette={{ /* overrides applied only in dark mode */ }}
>
{children}
</ThemeProvider>
Controlled usage is supported by passing theme and onThemeChange instead of
defaultTheme.
Palette tokens are emitted as a small generated stylesheet keyed on the provider's scope
(and on :root with applyToRoot), not as inline style. Every mode's values are in it,
selected by a .dark class on the scope or on any ancestor. A pre-paint script that sets
<html class="dark"> therefore gets dark tokens in the server-rendered HTML, before
hydration resolves the stored or system theme.
Setting palette.accent also points --color-primary, --color-primary-hover and
--color-focus-ring at accent 600/700/500 inside the scope. Those derived tokens have
zero specificity, so explicit colors.primary/colors.focusRing or any app rule aimed at
the scope (.rui-theme { --color-primary: … }) wins. If your app defines them on :root
instead, pass deriveAccentTokens={false} so the scope doesn't shadow them.
Palette shape (ThemePalette, all fields optional):
| Field | Type | Purpose |
|---|---|---|
accent | Partial<Record<50‥950, string>> | Primary/action color scale |
neutral | Partial<Record<50‥950, string>> | Grayscale / surfaces / borders |
colors | ThemeColors | Named semantic colors (bg, surface, foreground, border, status colors, focus ring…) |
radius | Partial<Record<"sm"‥"2xl" | "full", string>> | Corner radii |
fonts | ThemeFonts | sans, display, mono families |
spacing | Record<string, string> | Spacing scale entries |
shadows | Partial<Record<"xs"‥"2xl", string>> | Elevation shadows |
tokens | Partial<Record<\--${string}`, string | number>>` | Any CSS custom property, escape hatch |
import { useTheme } from "@bzync/rui"
function Example() {
const { theme, resolvedTheme, setTheme, toggleTheme } = useTheme()
// theme: "light" | "dark" | "system" (the user's selection)
// resolvedTheme: "light" | "dark" (after resolving "system")
return <button onClick={toggleTheme}>Now: {resolvedTheme}</button>
}
useTheme must be called inside a ThemeProvider.
import { ThemeToggle } from "@bzync/rui"
<ThemeToggle
showLabel
lightLabel="Light"
darkLabel="Dark"
lightIcon={<SunIcon />}
darkIcon={<MoonIcon />}
/>
Renders a button with aria-pressed and an aria-label that reflects the current state.
Author custom components against the semantic tokens rather than raw palette values:
bg, surface, surface-raised, surface-muted, foreground, muted-foreground,
border, border-strong, primary, primary-foreground, destructive, the status
colors, and focus-ring. They adapt to light/dark and to any palette override
automatically.
Each status color (success, warning, danger, info) has a -strong companion,
a readable text/icon shade derived from it: darker in light mode, lighter in dark mode.
Every status-colored component (Badge, StatusDot, Alert, Callout, Tag,
Timeline, Progressbar, Stat, Text, toasts, ...) uses the same recipe, so
palette.colors.success and friends retint all of them:
| Role | Classes |
|---|---|
| Tinted chip | bg-success/10 text-success-strong border-success/25 |
| Solid fill (dot, bar) | bg-success |
| Text or icon | text-success-strong |
One tone vocabulary. Tone-bearing components share the names
default | success | warning | danger | info | muted (exported as the Tone type), so a
single status → tone map can feed Badge, Tag, Callout, Alert, Text and
StatusDot (which also keeps its presence states such as online). Button has a
danger variant. The older names still work as deprecated aliases: error → danger
(Badge, Tag, Callout, Alert, Progressbar, Timeline, toasts, Select option colors) and
destructive → danger (Button).
accent-strong does the same for accent text (selected items, links, eyebrows), and
surface-sunken is the recessed well behind code blocks and terminals. Components use one
mode-aware class per property rather than a light dark:dark class pair, so a consumer's
own Tailwind layer can't re-assert the light half in dark mode.
Modal (and ConfirmDialog), Drawer, Select, Autocomplete, Overlay and chart tooltips
render into document.body, so a dialog opened from a table row can't be clipped or
out-stacked by an overflow, transform or contain ancestor. Pass portalled={false}
to a Modal to render it inline instead.
They keep the nearest ThemeProvider's dark mode and palette through ThemePortal, which
wraps them in a display: contents copy of the theme scope. You don't need
applyToRoot for this. ThemePortal renders nothing on the server or during hydration,
so open and closed overlays hydrate without a mismatch; the portal appears right after.
Use ThemePortal for your own portaled overlays too:
import { ThemePortal } from "@bzync/rui"
<ThemePortal>
<div className="fixed inset-0 z-(--z-modal) bg-surface">...</div>
</ThemePortal>
Add the rui-light class to keep a subtree on the light tokens while the rest of the
scope is dark — for content whose colors must match what gets exported or printed.
ThemeProvider re-applies your lightPalette inside it. Signature uses it so dark ink
stays legible on its signing pad.
<div className="rui-light bg-surface text-foreground">Always light</div>
Every layer the library renders uses one z-index scale, defined as CSS variables in
styles.css:
| Token | Default | Used by |
|---|---|---|
--z-sticky | 1100 | AppShellHeader, sticky Footer, SkeletonTopbar |
--z-modal | 1300 | Modal, Drawer, CommandPalette, Overlay |
--z-popover | 1400 | Select, Autocomplete, Popover, DropdownMenu, DatePicker, TimePicker |
--z-toast | 1500 | SnackbarProvider toasts |
--z-tooltip | 1600 | Tooltip, chart tooltips, focused skip links |
Floating panels sit above dialogs so a Select inside a Modal or Drawer is never
painted over, toasts stay visible over an open dialog, and tooltips are always on top.
Overlays on the same layer stack by DOM order, so a Drawer opened from a Modal
covers it. To fit the library into your app's own scale, override the variables on
:root. For your own overlays, use the Z_INDEX constant
(style={{ zIndex: Z_INDEX.modal }}) or z-(--z-modal) in Tailwind.
The root entry re-exports everything for convenience:
import { Button, Modal, DataTable } from "@bzync/rui"
Per-component subpath entries let a bundler pull in only what an application uses:
import { Button } from "@bzync/rui/button"
import { Modal } from "@bzync/rui/modal"
import { BarChart } from "@bzync/rui/charts"
Both forms are tree-shakeable — the package sets "sideEffects": ["**/*.css"] and ships
ESM — but subpath imports keep dependency graphs smallest and are the recommended default
for libraries and performance-sensitive apps.
Client components are marked with the "use client" directive, so React Server
Components and frameworks like Next.js App Router can import and render them directly.
ThemeProvider reads localStorage only on the client and resolves "system" with
matchMedia; render it in a client boundary and pass a defaultTheme so the server and
first client paint agree.
.rui-theme scope is disabled when the user has
prefers-reduced-motion: reduce set.IconButton, InfoButton, CopyButton) require an accessible
label.SkeletonCard, SkeletonTable, SkeletonList, SkeletonStat)
are busy role="status" regions with a visually hidden "Loading…" label. To announce
a whole loading layout once, wrap it in <SkeletonRegion label="Loading invoices"> and
pass label={false} to the skeletons inside.m elements with the domAnimation feature
set rather than the full motion component, so apps don't bundle drag, pan or layout
animation code they never use.Import any of these from the root or from @bzync/rui/<kebab-name>.
| Component / export | Notes |
|---|---|
ThemeProvider | Token-driven theme scope; light/dark, controlled or uncontrolled, persistence, applyToRoot |
useTheme() | { theme, resolvedTheme, setTheme, toggleTheme } |
ThemeToggle | Accessible light/dark switch with custom icons and labels |
ThemeSwitcher | variant: segmented | dropdown | switch | radio | icon-button, size, includeSystem, showLabels, value / onChange |
SnackbarProvider / useSnackbar() | Programmatic toast notifications |
CommandProvider / CommandPalette | App-wide searchable command palette |
cn() | clsx + tailwind-merge class combiner |
| Component | Key props |
|---|---|
Button | variant: primary | secondary | ghost | outline | danger | link (destructive is a deprecated alias), size: sm | md | lg | icon, loading, icon, iconPosition |
ButtonGroup | orientation, aria-label; wraps Button children with role="group" |
IconButton / InfoButton | label (required for a11y), icon |
CopyButton | value (text to copy), label, timeout |
Toggle / ToggleGroup / ToggleGroupItem | pressed / defaultPressed, onPressedChange, type: single | multiple, variant, size, orientation, loop |
BillingIntervalToggle | value: monthly | yearly, onChange |
InlineEdit | value / defaultValue, onSave, onCancel, validate, multiline, saveOnBlur |
| Component | Key props |
|---|---|
Input | label, hint, error, prefix / suffix, size |
Textarea | label, hint, error, rows |
PasswordInput | Input props plus showStrength, requirements, visible / defaultVisible, onVisibleChange |
SearchInput | onSearch, debounceMs, shortcut, loading, clearLabel |
MaskedInput / CurrencyInput / PhoneInput | mask; currency, locale, decimals, allowNegative; country, countries, onCountryChange |
TagInput | value / defaultValue, onChange, separators, validate, transform, maxTags, allowDuplicates |
NumberInput | value / defaultValue, min, max, step, onChange |
OtpInput | length, value, onChange, label |
Select | options, label, multiple, groups, searchable, onChange |
Autocomplete | options, multiple, custom filter, single/multi triggers |
Checkbox | label, checked / defaultChecked, onCheckedChange |
| / |
| Component | Key props |
|---|---|
Badge / Tag | variant, dot; Tag adds onRemove |
Icon / Icons | name, size: xs‥xl | number, tone, spin, label; or Icons.<Name> components |
Image | src, alt, aspectRatio, fit, placeholder, blurDataURL, fallbackSrc, rounded |
Banner | tone, variant, title, action, dismissible, storageKey, sticky |
Avatar / AvatarGroup / AvatarGroupOverflow | name, src, size, initials fallback; group spacing, count |
Card (CardHeader / CardTitle / CardDescription / CardBody / CardFooter) | composition |
Callout / Alert | title, variant; Alert adds dismissable, onDismiss |
Stat / StatusDot | Stat: label, value, trend, trendValue; StatusDot: status, label, variants, dotClassName, labelClassName |
Tooltip | content, trigger child, maxWidth (sizes to content, wraps at the cap; default 20rem) |
Link | href, variant |
| Component | Key props |
|---|---|
Modal (ModalHeader / ModalTitle / ModalDescription / ModalBody / ModalFooter) | open, onClose, title, size: sm‥7xl | full, ModalBody scrollable |
Drawer (DrawerHeader / DrawerBody / DrawerFooter) | open, onClose, title, size, focus trap |
Sheet | open / onOpenChange, snapPoints, defaultSnap, onSnapChange, modal, dismissible |
ResponsiveDialog (ResponsiveDialogTrigger / ResponsiveDialogContent / ResponsiveDialogHeader / ResponsiveDialogTitle / ResponsiveDialogDescription / ResponsiveDialogBody / ResponsiveDialogFooter / ResponsiveDialogClose) | modal on desktop, bottom sheet on mobile; open / defaultOpen, onOpenChange |
Overlay / OverlayBackdrop | low-level primitive: open, onClose, portal, closeOnEscape, closeOnOutsideClick, disableScrollLock |
ConfirmDialog | open, onConfirm, onCancel, title |
Popover / PopoverContent | trigger, open / onOpenChange |
HoverCard | content, side, align, openDelay, closeDelay, open / onOpenChange |
DropdownMenu | trigger, items: { label, onClick }[] |
| Component | Key props |
|---|---|
AppShell (AppShellHeader / AppShellBody / AppShellMain / Footer) | app frame; sticky, scrollable, fixed |
Container | size: sm‥xl | full, gutter |
Stack / Inline / PageHeader | spacing and header primitives |
Navbar / Sidebar / Topbar / BottomBar (+ TopbarTitle / BrandLink) | items: NavigationItem[], activeId, onSelect |
NavigationMenu | items, openDelay, closeDelay |
Resizable / ResizablePanel | direction, defaultSizes / sizes, onResize, minSizes / maxSizes, storageKey, withHandle |
NavigationLink | id, label, href, icon, badge, active, compact |
Breadcrumb | items |
SEO (+ SeoImage / StructuredData / BreadcrumbJsonLd / ArticleJsonLd) | document head tags and JSON-LD |
Slot | asChild composition primitive |
| Component | Key props |
|---|---|
DataTable<T> | columns: ColumnDef<T>[] ({ key, header, cell, sortable?, searchable?, align?, width? }), data: T[], searchable, searchPlaceholder, pageSizeOptions, density, loading, emptyMessage, onRowClick, unstyled — built-in sort, search, and pagination. T must have an id. |
SortableList / Kanban | items + onReorder; columns + onChange; renderItem / renderCard, getLabel; keyboard and pointer reordering |
DndContext / Droppable / Draggable / DragOverlay / DragHandle | low-level drag-and-drop primitives with keyboard support |
PanoViewer / PanoTourDialog / PanoTourButton / PanoTourEditor | 360° panorama tours with hotspots and narration, plus an editor |
import { Button } from "@bzync/rui/button"
<Button variant="primary" size="lg" loading={isSaving} onClick={save}>
Save changes
</Button>
<Button variant="outline" icon={<PlusIcon />} iconPosition="left">
New item
</Button>
import { FormField } from "@bzync/rui/form-field"
import { Input } from "@bzync/rui/input"
<FormField label="Email" htmlFor="email" required error={errors.email}>
<Input id="email" type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
</FormField>
import { Select } from "@bzync/rui/select"
<Select
label="Environment"
options={[
{ label: "Production", value: "prod" },
{ label: "Staging", value: "staging" },
{ label: "Development", value: "dev" },
]}
value={env}
onChange={setEnv}
/>
Inside a <form>, give it a name (and form for a form elsewhere on the page) to submit
the selection natively, one name=value entry per selected value, as with Input.
required is enforced by native validation, and an uncontrolled Select returns to its
defaultValue when the form resets:
<form action={createServer}>
<Select name="region" required label="Region" options={regions} />
<Select name="tags" multiple label="Tags" options={tags} />
</form>
Autocomplete works the same way: name submits the selected option value(s), not the
typed text, and required requires a selection. Editing the text away from the chosen
option's label clears an uncontrolled selection, so a stale value is never submitted.
import { Modal, ModalBody, ModalFooter } from "@bzync/rui/modal"
import { Button } from "@bzync/rui/button"
const [open, setOpen] = useState(false)
<Modal open={open} onClose={() => setOpen(false)} title="Delete project" size="sm">
<ModalBody>This action cannot be undone.</ModalBody>
<ModalFooter>
<Button variant="ghost" onClick={() => setOpen(false)}>Cancel</Button>
<Button variant="danger" onClick={confirmDelete}>Delete</Button>
</ModalFooter>
</Modal>
import { SnackbarProvider, useSnackbar } from "@bzync/rui/snackbar"
function Root() {
return (
<SnackbarProvider>
<App />
</SnackbarProvider>
)
}
function SaveButton() {
const { show } = useSnackbar()
return <Button onClick={() => show({ title: "Saved", variant: "success" })}>Save</Button>
}
useSnackbar() returns { show, dismiss, dismissAll }. show(opts) returns the toast id;
pass a stable id in opts to replace an existing toast in place instead of stacking.
import { DataTable } from "@bzync/rui/datatable"
type Row = { id: string; name: string; role: string; seats: number }
<DataTable<Row>
data={members}
columns={[
{ key: "name", header: "Name", cell: (r) => r.name, sortable: true, searchable: true },
{ key: "role", header: "Role", cell: (r) => r.role, sortable: true },
{ key: "seats", header: "Seats", cell: (r) => r.seats, align: "right", sortable: true },
]}
searchable
pageSizeOptions={[10, 25, 50]}
onRowClick={(row) => open(row.id)}
/>
The chart set is a separate subpath entry so it stays out of the main graph unless used. Every chart renders plain SVG and takes data arrays — no canvas, no chart engine.
import { BarChart, LineChart, DonutChart } from "@bzync/rui/charts"
<BarChart data={[{ label: "Jan", value: 42 }, { label: "Feb", value: 55 }]} />
Available: BarChart, StackedBarChart, LineChart, MultiLineChart, AreaChart,
DonutChart, ScatterChart, GanttChart, HeatmapChart, RadarChart, FunnelChart,
WaterfallChart, Sparkline, Gauge. See
src/components/charts for exact per-chart props.
Lifecycle-correct hooks (mount/update/unmount with cleanup), re-exported from the root:
| Hook | Purpose |
|---|---|
useIsMounted() | Guard async setState after unmount |
useIsomorphicLayoutEffect() | useLayoutEffect on the client, useEffect on the server |
usePrevious(value) | Previous render's value |
useUpdateEffect(fn, deps) | Effect that skips the first render |
useEventCallback(fn) | Stable callback identity with fresh closure |
useControllableState(opts) | Controlled/uncontrolled state pattern |
useMediaQuery(query) | Subscribe to a media query |
useAbortSignal() | Abort in-flight work on unmount |
useFocusTrap(ref, active) | Trap focus within a container |
useOutsideClick(ref, handler) | Detect clicks outside an element |
Utilities: cn(), focus helpers, Portal, and assertion helpers from @bzync/rui/utils;
ErrorBoundary from the root; createSafeEffect and mount helpers from @bzync/rui's
lifecycle exports; KEY, DURATIONS, and FOCUSABLE_SELECTOR constants.
@bzync/rui is written in TypeScript and ships .d.ts files for the root and every
subpath entry. Prop types, variant unions (ButtonVariant, ButtonSize, …), and the
theming types (Theme, ThemePalette, ThemeColors, ColorShade, …) are all exported.
No @types/* package is required.
^18.2.0 || ^19.0.0matchMedia,
and standard DOM APIs; no polyfills are bundled."use client" boundary described above.dependencies: none. clsx/tailwind-merge logic is inlined; icons are inlined SVG.sideEffects is limited to **/*.css, so unused components are dropped by any
tree-shaking bundler.@bzync/rui follows semantic versioning. While the major version
is 0, minor releases may contain breaking changes; pin a version or a tight range and
review the release notes before upgrading. Releases are published from CI with npm
provenance attestations.
Full component documentation and live, themeable demos are published at
rui.bzync.com. A push to main deploys the
latest docs via GitHub Pages.
Bug reports, accessibility fixes, documentation improvements, and focused component contributions are welcome. See CONTRIBUTING.md for the development setup, the verification gate, and the release process, and CODE_OF_CONDUCT.md.
Report vulnerabilities privately using the process in SECURITY.md. Please do not open public issues for security reports.
ISC © 2026 Bzync
@bzync/rui is maintained by Rayan Reynaldo, Founder of Bzync
(www.bzync.com).
If @bzync/rui is useful to you, you can support its continued development on
Buy Me a Coffee.
RadioRadioGroupvalue, onChange, options |
Switch | label, checked, onCheckedChange |
Slider | value / defaultValue, min, max, step, label |
RangeSlider | value / defaultValue: [min, max], min, max, step, minStepsBetweenThumbs, marks, showTooltip, formatValue, onChangeEnd |
Rating | value / defaultValue, max, size, readOnly, onValueChange |
DatePicker | label, value, onChange |
DateRangePicker | value / defaultValue, presets, numberOfMonths, minDate / maxDate, clearable |
DateTimePicker | value / defaultValue, format: 12 | 24, showSeconds, minuteStep, presets, minDate / maxDate |
Calendar | value: Date, onChange, month/week views |
TimePicker | value / defaultValue, format: 12 | 24, minuteStep, showSeconds, min / max, clearable |
FileUpload | label, accept, multiple, onFilesChange |
ColorPicker | value / defaultValue, presets, swatches, showAlpha, showEyeDropper, size |
Signature / SignaturePad | modes: draw | upload, strokeColor, colors, strokeWidth, enableEditor (background removal, crop, zoom), signatureRef (clear, undo, toDataURL, toSVG, openEditor) |
Label | htmlFor, required, hint |
FormField (FieldLabel / FieldDescription / FieldError / FieldContent) | label, htmlFor, required, hint, error — wraps a control |
Form | form, onSubmit, onInvalid, errorSummary |
Stepper | steps, activeStep |
Kbd | keyboard-key children |
Typography: Heading / Text / Prose / Time |
Heading: as h1‥h6, size, tone, weight, balance; Text: variant, size, number/date/currency formatting; Time: renders <time datetime> |
Code / InlineCode / CodeBlock / CodeEditor | code, filename, showLineNumbers, value / onChange |
Blockquote | variant, size, cite, source / sourceHref |
Currency | value: number | bigint, currency, locale, accounting, tone, size |
DescriptionList (DescriptionItem / DescriptionTerm / DescriptionDetails) | columns: 1 | 2 | 3, density, orientation |
List / ListItem, Timeline, Tree | item collections; Tree is expandable |
Table (TableHeader / TableHead / TableBody / TableRow / TableCell) | styled primitive table |
Divider / Separator | orientation, variant, spacing, optional label |
ScrollArea | orientation: vertical | horizontal | both, hideScrollbar, keyboardNavigable |
AspectRatio | ratio: number |
Collapsible (CollapsibleTrigger / CollapsibleContent) | open / defaultOpen, onOpenChange, disabled |
VisuallyHidden | screen-reader-only content; focusable |
Skeleton (+ SkeletonAvatar / SkeletonCard / SkeletonTable / SkeletonText / SkeletonTopbar) | loading placeholders |
Spinner / Progressbar | size; value, max |
EmptyState / ErrorState | title, description, error, action slot |
RichText / RichTextEditor | rendered content and editor |
AuthBackdrop | decorative auth-screen background |
DevMode | enabled, breakpoints, hotkey, floating, storageKey — development-only viewport/breakpoint HUD |
ContextMenu | items, ariaLabel, asChild; opens on right-click or Shift+F10 |
Menubar | menus, ariaLabel |
Command / CommandPalette / CommandProvider | searchable command palette |
SnackbarProvider / useSnackbar() | queue and dismiss toasts programmatically |
NotificationCenter | items / defaultItems, onItemsChange, onItemClick, onMarkAllAsRead, onClearAll, categories, trigger |
Tour | steps, open / onOpenChange, step / onStepChange, mask, onFinish, onSkip |
Lightbox | images, open / onOpenChange, index / onIndexChange, loop, thumbnails |
Tabs (TabsList / TabsTrigger / TabsContent) | value, onValueChange |
Accordion | items: { id, trigger, content }[] |
Pagination | page, totalPages, onPageChange |
Terminal / TerminalBlock / TerminalEmulator | virtual filesystem, runnable shell commands |