twico-ui 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/llms.txt ADDED
@@ -0,0 +1,2420 @@
1
+ # Twico UI — LLM usage guide (llms.txt)
2
+
3
+ Twico UI is a free, MIT-licensed **React component library**: 61 components and 23 standalone
4
+ hooks (plus the `useToast` hook), with dark mode, motion, accessibility, RTL, a density scale, and
5
+ design tokens. **Zero runtime dependencies** (peer: `react`/`react-dom` >= 18).
6
+
7
+ This file is the authoritative guide for writing application code with the **`twico-ui` npm package**.
8
+ The component and hook reference below is generated from the library's own TypeScript types and
9
+ component data, so every component, prop, and example reflects the shipped API. When you generate
10
+ code that uses Twico UI, follow the setup and conventions here, then use the per-component sections
11
+ for exact props.
12
+
13
+ > Not to be confused with `SKILL.md` / `DESIGN-SYSTEM.md`, which are for generating static HTML
14
+ > design mockups from the design-system bundle. This guide is for **real React app code**.
15
+
16
+ ## Install & setup (do this once)
17
+
18
+ ```bash
19
+ npm install twico-ui
20
+ ```
21
+
22
+ Import the stylesheet **once** at your app root — this is the only CSS you import:
23
+
24
+ ```jsx
25
+ import "twico-ui/styles.css";
26
+ ```
27
+
28
+ Then import components and hooks by name from the package root:
29
+
30
+ ```jsx
31
+ import { Button, Input, Dialog, useColorScheme } from "twico-ui";
32
+ ```
33
+
34
+ Rules that matter:
35
+
36
+ - **Do not import any per-component CSS — there is none.** The single `twico-ui/styles.css` carries
37
+ the design tokens, base reset, and self-hosted fonts; each component injects its own scoped CSS at
38
+ runtime. Importing `twico-ui/styles.css` more than once is harmless but unnecessary.
39
+ - The package ships a `"use client"` directive, so it works in the **Next.js App Router / React
40
+ Server Components** out of the box — import it into client trees directly.
41
+ - **SSR-safe**: components never touch `window`/`document` during render.
42
+ - **Peer deps**: `react` and `react-dom` >= 18 (works with React 18 and 19).
43
+ - TypeScript types ship with the package; every component's props type is exported (e.g.
44
+ `ButtonProps`).
45
+
46
+ ## Dark mode
47
+
48
+ Dark mode is a class/attribute on the `<html>` element — add `class="dark"` (or `data-theme="dark"`):
49
+
50
+ ```html
51
+ <html class="dark">
52
+ ```
53
+
54
+ Or manage it in React with the built-in hook (persists to `localStorage`, syncs across tabs/instances):
55
+
56
+ ```jsx
57
+ import { useColorScheme, Button } from "twico-ui";
58
+
59
+ function ThemeToggle() {
60
+ const { isDark, toggle } = useColorScheme();
61
+ return <Button variant="ghost" onClick={toggle}>{isDark ? "Light" : "Dark"}</Button>;
62
+ }
63
+ ```
64
+
65
+ Only the semantic color aliases flip in dark mode; the primitive scales stay constant.
66
+
67
+ ## Theming (design tokens)
68
+
69
+ Everything is styled through CSS custom properties, so you re-skin by overriding tokens at `:root`
70
+ (or any scope) — no component props or config needed:
71
+
72
+ ```css
73
+ :root {
74
+ --brand-500: #8b5cf6; /* rebrand: remap the primary scale */
75
+ --radius-md: 8px; /* tighter corners everywhere */
76
+ }
77
+ ```
78
+
79
+ Token families: `--color-*` (semantic: `primary`, `success`, `warning`, `danger`, `info`, `bg`,
80
+ `surface`, `text`, `border`, …), `--brand-*` / `--indigo-*` (primary scale, 50–950), `--radius-*`,
81
+ `--shadow-*`, `--space-*`, `--text-*`, `--font-*`, `--duration-*` / `--ease-*`. The primitive color
82
+ scales are also importable as JS objects: `import { indigo, slate } from "twico-ui/colors"`.
83
+
84
+ - **Density**: set `data-density="compact"` or `"comfortable"` on any ancestor to retune control
85
+ heights for everything inside — no per-component prop.
86
+ - **RTL**: set `dir="rtl"` on any ancestor; components use logical properties and mirror automatically.
87
+
88
+ ## Controlled vs uncontrolled (read before using inputs)
89
+
90
+ Every form component supports both modes:
91
+
92
+ - **Uncontrolled**: pass `defaultValue`, read changes via `onChange`.
93
+ - **Controlled**: pass `value` + `onChange` and own the state.
94
+
95
+ The `onChange` payload differs by component kind:
96
+
97
+ - **Composite controls** (Select, MultiSelect, Combobox, Slider, Rating, Tabs, DatePicker,
98
+ DateRangePicker, ColorPicker, …) call **`onChange(value)`** — you receive the value directly.
99
+ - **Native input controls** (Input, Textarea, and the toggle family Checkbox / Switch / Radio) call
100
+ **`onChange(event)`** — read `event.target.value` (or `event.target.checked` for the toggles).
101
+ - Currency / CurrencyField are specialized money inputs — see their sections for their exact
102
+ `value`/`onChange` shape.
103
+
104
+ ```jsx
105
+ // controlled text input (native — event payload)
106
+ const [email, setEmail] = useState("");
107
+ <Input value={email} onChange={(e) => setEmail(e.target.value)} placeholder="you@example.com" />
108
+
109
+ // controlled select (composite — value payload)
110
+ const [role, setRole] = useState("editor");
111
+ <Select
112
+ value={role}
113
+ onChange={setRole}
114
+ options={[{ value: "admin", label: "Admin" }, { value: "editor", label: "Editor" }]}
115
+ />
116
+ ```
117
+
118
+ The `Field` component is a shared label/hint/error/required wrapper for any control. The
119
+ `useControllableState` hook implements this exact controlled/uncontrolled pattern if you build your own.
120
+
121
+ ## Overlays (Dialog, Drawer, Menu, Popover, CommandPalette, Tooltip)
122
+
123
+ - **Modal overlays — Dialog and Drawer — are driven by `open` + `onClose`:**
124
+
125
+ ```jsx
126
+ const [open, setOpen] = useState(false);
127
+ <Button onClick={() => setOpen(true)}>Open</Button>
128
+ <Dialog open={open} onClose={() => setOpen(false)} title="Confirm" description="Are you sure?">
129
+ …body…
130
+ </Dialog>
131
+ ```
132
+
133
+ - **Anchored overlays — Menu and Popover — take a `trigger` node** and use `open` / `defaultOpen` +
134
+ `onOpenChange` (uncontrolled by default — just pass `trigger` and `items`).
135
+ - All portal to `document.body`, animate in **and** out, and dismiss on backdrop click / Escape;
136
+ `prefers-reduced-motion` is respected.
137
+ - The `useDisclosure` hook is a convenient open/close state helper for driving these.
138
+
139
+ ## Toasts
140
+
141
+ Wrap your app **once** in `ToastProvider`, then call the imperative `useToast` hook anywhere beneath
142
+ it. `useToast()` returns an object — **destructure `toast`**:
143
+
144
+ ```jsx
145
+ import { ToastProvider, useToast, Button } from "twico-ui";
146
+
147
+ function App() {
148
+ return (
149
+ <ToastProvider limit={4}>
150
+ <Page />
151
+ </ToastProvider>
152
+ );
153
+ }
154
+
155
+ function Page() {
156
+ const { toast } = useToast();
157
+ // toast.success / .error / .warning / .info / .danger(title, options?)
158
+ return <Button onClick={() => toast.success("Saved")}>Save</Button>;
159
+ }
160
+ ```
161
+
162
+ ## Icons
163
+
164
+ The library **ships no icon set** — pass your own icons as React nodes (inline SVG recommended) to
165
+ props such as `leftIcon`, `rightIcon`, `icon`, `header`. Icons inherit `currentColor` and the
166
+ control's size:
167
+
168
+ ```jsx
169
+ <Button leftIcon={<svg width="16" height="16" viewBox="0 0 24 24" /* … */ />}>Add item</Button>
170
+ ```
171
+
172
+ ## Shared prop vocabulary
173
+
174
+ - `size`: `"sm" | "md" | "lg"` (default `"md"`); a few components add `"xs"`/`"full"`.
175
+ - `tone`: semantic color. Status components expose `"primary" | "neutral" | "success" | "warning" |
176
+ "danger" | "info"`; action components (Button, IconButton) expose `"primary" | "danger"`.
177
+ - `variant`: fill style — commonly `"solid" | "soft" | "outline" | "ghost"` (the exact set varies per
178
+ component; see each section).
179
+ - `value` / `defaultValue` / `onChange`: the controlled/uncontrolled triad.
180
+ - `items` / `options`: array-driven content.
181
+ - `label` / `hint` / `error` / `required`: form labelling (or use the `Field` wrapper).
182
+
183
+ **Every component also accepts the standard DOM attributes for its root element** (`className`, `id`,
184
+ `style`, `data-*`, `aria-*`) and forwards remaining props and event handlers via `...rest`. To keep
185
+ this guide focused, the per-component "Props" lists below show only the **component-specific** props
186
+ (the generic DOM passthrough is omitted).
187
+
188
+ ## How to read the reference below
189
+
190
+ For each component: a one-line summary, its import, the component-specific props as
191
+ `` `name` `` — type · *required* or default — what it does, and a copy-paste example. Object-shaped
192
+ types referenced in props (e.g. `SelectOption`, `AccordionItem`, `DatatableColumn`) are illustrated
193
+ in the examples.
194
+
195
+ ---
196
+
197
+ ## Component reference (61 components)
198
+
199
+ ### Buttons & actions
200
+
201
+ #### Button
202
+
203
+ Button is a primary action button with solid, soft, outline, and ghost variants crossed with a primary or danger tone, four sizes (xs/sm/md/lg), loading state, optional left/right icons, and a click ripple effect. Use it for primary and secondary actions throughout the UI.
204
+
205
+ `import { Button } from "twico-ui"`
206
+
207
+ Props:
208
+ - `variant` — `"solid" | "soft" | "outline" | "ghost"` · default `"solid"` — Fill style: solid (primary CTA), soft (tinted), outline, or ghost. Cross with `tone` for color.
209
+ - `tone` — `"primary" | "danger"` · default `"primary"` — Color/intent, orthogonal to variant — primary by default; tone="danger" makes a destructive button in any variant.
210
+ - `size` — `"xs" | "sm" | "md" | "lg"` · default `"md"` — Adds a new "xs" size (~26px tall, smaller than "sm") to the existing union; sm/md/lg are unchanged. xs height is composed locally from space tokens (no global control-h-xs token).
211
+ - `leftIcon` — `React.ReactNode` — Renders an icon node before the label, commonly used to reinforce the action with a leading visual cue.
212
+ - `rightIcon` — `React.ReactNode` — Renders an icon node after the label, often a chevron or arrow signaling navigation or progression.
213
+ - `loading` — `boolean` · default `false` — When true, swaps in a spinner and disables interaction to signal an in-flight action and prevent duplicate clicks.
214
+ - `fullWidth` — `boolean` · default `false` — Stretches the button to fill its container's width, handy for full-width CTAs and stacked mobile action buttons.
215
+ - `as` — `"button" | "a"` · default `"button"` — Chooses whether to render a native "button" or an "a" anchor, letting the button act as a real link when needed.
216
+ - `children` — `React.ReactNode` — The label content rendered inside the button, typically text but accepting any inline node alongside the icons.
217
+ - `href` — `string` — Link destination — only used with as="a"; scheme-sanitized (javascript:/data:/vbscript: render without href).
218
+ - `target` — `React.HTMLAttributeAnchorTarget` — Anchor target — only used with as="a" (e.g. "_blank").
219
+ - `rel` — `string` — Anchor rel — only used with as="a"; pair "noopener noreferrer" with target="_blank".
220
+ - `onClick` — `(e: React.MouseEvent) => void` — Click handler — fires on click/tap and on Enter or Space.
221
+ - `type` — `"button" | "submit" | "reset"` · default `"button"` — Native button type — use "submit" to submit a surrounding form.
222
+ - `disabled` — `boolean` · default `false` — Disables the control — no interaction and a dimmed appearance.
223
+ - `autoFocus` — `boolean` · default `false` — Focus the element automatically when it mounts.
224
+
225
+ ```jsx
226
+ import { Button } from "twico-ui";
227
+
228
+ const PlusIcon = () => (<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M12 5v14M5 12h14" /></svg>);
229
+
230
+ <Button variant="solid" size="md" onClick={() => {}}>Save changes</Button>
231
+ <Button variant="soft" leftIcon={<PlusIcon />}>Add item</Button>
232
+ <Button variant="outline">Cancel</Button>
233
+ <Button variant="ghost">Skip</Button>
234
+ <Button tone="danger" loading>Deleting…</Button>
235
+ ```
236
+
237
+ #### IconButton
238
+
239
+ IconButton is a square or circular button that renders a single icon and always requires an aria-label for accessibility. Use it in toolbars, cards, and dense UI where an icon-only control is clearer than a labeled button.
240
+
241
+ `import { IconButton } from "twico-ui"`
242
+
243
+ Props:
244
+ - `icon` — `React.ReactNode` — Supplies the single icon node to render, such as a Lucide SVG, falling back to children when omitted.
245
+ - `variant` — `"solid" | "soft" | "outline" | "ghost"` · default `"ghost"` — Fill style (same axes as Button): solid, soft, outline, or ghost; ghost by default. Cross with `tone` for color.
246
+ - `tone` — `"primary" | "danger"` · default `"primary"` — Color/intent, orthogonal to variant (mirrors Button) — primary by default; tone="danger" makes an icon-only destructive button in any variant.
247
+ - `size` — `"xs" | "sm" | "md" | "lg"` · default `"md"` — Adds a new "xs" size (~26px square, smaller than "sm") to the existing union; sm/md/lg are unchanged.
248
+ - `round` — `boolean` · default `false` — Renders a fully rounded circular button when true instead of the default squared shape with rounded corners.
249
+ - `aria-label` — `string` · **required** — Provides the required accessible name for this icon-only button so screen readers announce its purpose.
250
+ - `onClick` — `(e: React.MouseEvent) => void` — Click handler — fires on click/tap and on Enter or Space.
251
+ - `type` — `"button" | "submit" | "reset"` · default `"button"` — Native button type — use "submit" to submit a surrounding form.
252
+ - `disabled` — `boolean` · default `false` — Disables the control — no interaction and a dimmed appearance.
253
+ - `autoFocus` — `boolean` · default `false` — Focus the element automatically when it mounts.
254
+
255
+ ```jsx
256
+ import { IconButton } from "twico-ui";
257
+
258
+ const SettingsIcon = () => (<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M4 21v-7M4 10V3M12 21v-9M12 8V3M20 21v-5M20 12V3M1 14h6M9 8h6M17 16h6" /></svg>);
259
+ const HeartIcon = () => (<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M20.8 4.6a5.5 5.5 0 0 0-7.8 0L12 5.7l-1.1-1.1a5.5 5.5 0 1 0-7.8 7.8L12 21l8.9-8.6a5.5 5.5 0 0 0 0-7.8z" /></svg>);
260
+ const TrashIcon = () => (<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M3 6h18M8 6V4h8v2M19 6l-1 14H6L5 6" /></svg>);
261
+
262
+ <IconButton aria-label="Settings" icon={<SettingsIcon />} />
263
+ <IconButton aria-label="Like" variant="soft" round icon={<HeartIcon />} />
264
+ <IconButton aria-label="Delete" variant="outline" icon={<TrashIcon />} />
265
+ ```
266
+
267
+ ### Layout
268
+
269
+ #### Box
270
+
271
+ Generic, token-styled box — the building block for non-flex layout (padding, margin, background, border, radius, shadow) without writing CSS.
272
+
273
+ `import { Box } from "twico-ui"`
274
+
275
+ Props:
276
+ - `p, px, py, pt, pr, pb, pl` — `number | string` — Sets inner padding on the matching side(s), accepting a spacing-scale step (number) or any raw CSS length string.
277
+ - `m, mx, my, mt, mr, mb, ml` — `number | string` — Sets outer margin on the matching side(s), accepting a spacing-scale step (number) or any raw CSS length string.
278
+ - `bg` — `"surface" | "surface-raised" | "surface-sunken" | "bg" | string` — Sets the background to a surface token like "surface" or "surface-raised", or to any custom CSS color value.
279
+ - `border` — `boolean` · default `false` — When true, draws a 1px border using the theme's border token, useful for visually separating the box from its surroundings.
280
+ - `radius` — `string` — Applies a border-radius from the radius token scale by suffix (e.g. "lg") to round the box's corners.
281
+ - `shadow` — `string` — Applies an elevation box-shadow from the shadow token scale by suffix (e.g. "md") to lift the box off the page.
282
+ - `as` — `ElementType` · default `"div"` — Sets which element or tag the box renders as, defaulting to "div" but accepting any semantic HTML element type.
283
+ - `p` — `number | string` — Padding on all sides — a spacing-scale step (number) or any CSS length.
284
+ - `px` — `number | string` — Horizontal padding (inline start + end) — spacing step or CSS length.
285
+ - `py` — `number | string` — Vertical padding (block start + end) — spacing step or CSS length.
286
+ - `pt` — `number | string` — Padding top — spacing step or CSS length.
287
+ - `pr` — `number | string` — Padding right (inline end) — spacing step or CSS length.
288
+ - `pb` — `number | string` — Padding bottom — spacing step or CSS length.
289
+ - `pl` — `number | string` — Padding left (inline start) — spacing step or CSS length.
290
+ - `m` — `number | string` — Margin on all sides — a spacing-scale step (number) or any CSS length.
291
+ - `mx` — `number | string` — Horizontal margin (inline start + end) — spacing step or CSS length.
292
+ - `my` — `number | string` — Vertical margin (block start + end) — spacing step or CSS length.
293
+ - `mt` — `number | string` — Margin top — spacing step or CSS length.
294
+ - `mr` — `number | string` — Margin right (inline end) — spacing step or CSS length.
295
+ - `mb` — `number | string` — Margin bottom — spacing step or CSS length.
296
+ - `ml` — `number | string` — Margin left (inline start) — spacing step or CSS length.
297
+
298
+ ```jsx
299
+ import { Box } from "twico-ui";
300
+
301
+ <Box p={4} bg="surface" border radius="lg" shadow="sm">
302
+ Padded, bordered surface.
303
+ </Box>
304
+ ```
305
+
306
+ #### Container
307
+
308
+ Centers content and caps its width with responsive horizontal padding — the outer wrapper for a page or section.
309
+
310
+ `import { Container } from "twico-ui"`
311
+
312
+ Props:
313
+ - `size` — `"sm" | "md" | "lg" | "xl" | "full" | string` · default `"lg"` — Caps the maximum width using a named token (sm through xl, or full) or any CSS length, defaulting to "lg".
314
+ - `padded` — `boolean` · default `true` — Adds responsive horizontal padding so content does not touch the viewport edges; enabled by default, set false to remove it.
315
+ - `as` — `ElementType` · default `"div"` — Chooses the rendered HTML element or component (such as "section" or "main"), defaulting to "div" for semantic flexibility.
316
+
317
+ ```jsx
318
+ import { Container, Heading } from "twico-ui";
319
+
320
+ <Container size="lg">
321
+ <Heading level={1}>Page title</Heading>
322
+ </Container>
323
+ ```
324
+
325
+ #### Divider
326
+
327
+ Divider is a thin separator rule that visually splits content, either horizontally or vertically. Use it to break up sections, lists, or inline items, optionally with a centered or aligned label (e.g. "OR").
328
+
329
+ `import { Divider } from "twico-ui"`
330
+
331
+ Props:
332
+ - `orientation` — `"horizontal" | "vertical"` · default `"horizontal"` — Direction of the separator rule, either horizontal (default) to split stacked content or vertical for inline items.
333
+ - `inset` — `boolean` · default `false` — Indents the start of a horizontal rule to align it with inset content like list item text.
334
+ - `align` — `"left" | "center" | "right" | "start" | "end"` · default `"center"` — Label alignment when children are provided; logical start/end now accepted and map to left/right (additive — existing left/center/right unchanged).
335
+ - `children` — `React.ReactNode` — Optional label text rendered within the rule, such as "OR", to break content with a caption.
336
+
337
+ ```jsx
338
+ import { Divider } from "twico-ui";
339
+
340
+ <Divider />
341
+
342
+ <Divider>OR</Divider>
343
+
344
+ <span>A</span>
345
+ <Divider orientation="vertical" />
346
+ <span>B</span>
347
+ ```
348
+
349
+ #### Grid
350
+
351
+ CSS grid primitive — a responsive auto-fill grid (minChildWidth) or a fixed column count (columns).
352
+
353
+ `import { Grid } from "twico-ui"`
354
+
355
+ Props:
356
+ - `minChildWidth` — `number | string` — Enables a responsive auto-fill grid where columns wrap once children fall below this minimum width (number is treated as px).
357
+ - `columns` — `number` — Sets a fixed number of equal columns, used when minChildWidth is unset for predictable non-responsive layouts.
358
+ - `gap` — `number | string` · default `4` — Controls spacing between grid cells as a spacing-step number or any CSS length, defaulting to step 4.
359
+ - `align` — `string` — Maps to the CSS align-items value, controlling how children are aligned along the block (vertical) axis.
360
+ - `justify` — `string` — Maps to the CSS justify-items value, controlling how children are aligned along the inline (horizontal) axis.
361
+ - `as` — `ElementType` · default `"div"` — Renders the grid as a different element or tag instead of the default div, for semantic markup.
362
+
363
+ ```jsx
364
+ import { Grid, Card } from "twico-ui";
365
+
366
+ <Grid minChildWidth={220} gap={4}>
367
+ <Card>One</Card>
368
+ <Card>Two</Card>
369
+ <Card>Three</Card>
370
+ </Grid>
371
+ ```
372
+
373
+ #### Stack
374
+
375
+ Flexbox layout primitive — arranges children in a row or column with token-based gaps. Reach for it instead of hand-written flex divs.
376
+
377
+ `import { Stack } from "twico-ui"`
378
+
379
+ Props:
380
+ - `direction` — `"row" | "column"` · default `"column"` — Sets the main-axis flow, stacking children vertically with "column" (default) or side by side with "row".
381
+ - `gap` — `number | string` · default `4` — Sets spacing between children, as a numeric spacing step mapped to --space-* or any raw CSS length string.
382
+ - `align` — `string` — Maps to the align-items value, controlling how children line up along the cross axis.
383
+ - `justify` — `string` — Maps to the justify-content value, controlling how children are distributed along the main axis.
384
+ - `wrap` — `boolean` · default `false` — Allows children to wrap onto multiple lines instead of overflowing when they exceed the container width.
385
+ - `inline` — `boolean` · default `false` — Renders the stack as inline-flex rather than block-level flex, so it sits inline with surrounding content.
386
+ - `as` — `ElementType` · default `"div"` — Overrides the rendered element or tag (default "div"), useful for semantic wrappers like nav, ul, or section.
387
+
388
+ ```jsx
389
+ import { Stack } from "twico-ui";
390
+
391
+ <Stack direction="row" gap={3} align="center" wrap>
392
+ <Button>Save</Button>
393
+ <Button variant="ghost">Cancel</Button>
394
+ </Stack>
395
+ ```
396
+
397
+ ### Typography
398
+
399
+ #### Code
400
+
401
+ Inline code with a mono font and a subtle token-styled surface — use it instead of a bare code tag.
402
+
403
+ `import { Code } from "twico-ui"`
404
+
405
+ Props:
406
+ - `as` — `ElementType` · default `"code"` — Overrides the rendered element, defaulting to a code tag but allowing any tag when the semantics differ.
407
+ - `children` — `ReactNode` — Provides the inline code text or nodes shown in the mono font on the subtle token-styled surface.
408
+
409
+ ```jsx
410
+ import { Code, Text } from "twico-ui";
411
+
412
+ <Text>Install with <Code>npm install twico-ui</Code>.</Text>
413
+ ```
414
+
415
+ #### Heading
416
+
417
+ Heading (h1–h6) with consistent token typography. Use it instead of bare heading tags.
418
+
419
+ `import { Heading } from "twico-ui"`
420
+
421
+ Props:
422
+ - `level` — `1 | 2 | 3 | 4 | 5 | 6` · default `2` — Chooses which h1-h6 tag renders and its default token size, keeping document hierarchy and typography consistent.
423
+ - `size` — `string` — Overrides the font-size token suffix (e.g. "3xl") to decouple visual scale from the semantic heading level.
424
+ - `align` — `string` — Sets the CSS text-align value, letting you left-align, center, or right-align the heading text.
425
+ - `as` — `ElementType` — Renders a different tag while preserving the level's size, useful when semantics and visual weight should differ.
426
+
427
+ ```jsx
428
+ import { Heading } from "twico-ui";
429
+
430
+ <Heading level={1}>Dashboard</Heading>
431
+ <Heading level={3}>Recent activity</Heading>
432
+ ```
433
+
434
+ #### Text
435
+
436
+ Body text with token sizes and semantic color tones. Use it instead of bare paragraph/span tags.
437
+
438
+ `import { Text } from "twico-ui"`
439
+
440
+ Props:
441
+ - `size` — `"xs" | "sm" | "base" | "lg" | "xl"` · default `"base"` — Picks the font-size token from xs to xl, scaling the body text to fit its context, base by default.
442
+ - `tone` — `"default" | "muted" | "subtle" | "primary" | "success" | "warning" | "danger" | "info" | "neutral"` · default `"default"` — Sets the text color — neutral roles (default, muted, subtle) plus the six semantic color intents.
443
+ - `weight` — `string` — Sets the font-weight token suffix like semibold to adjust how heavy the text appears.
444
+ - `align` — `string` — Sets the text-align value, controlling whether the text reads left, center, right, or justified.
445
+ - `as` — `ElementType` · default `"p"` — Chooses the element or tag rendered, defaulting to p so you can swap in span or other tags.
446
+
447
+ ```jsx
448
+ import { Text } from "twico-ui";
449
+
450
+ <Text>Default paragraph text.</Text>
451
+ <Text size="sm" tone="muted">A muted caption.</Text>
452
+ ```
453
+
454
+ ### Inputs
455
+
456
+ #### Checkbox
457
+
458
+ An accessible checkbox with an animated checkmark, optional label and description, and an indeterminate (mixed) state. Use it for binary on/off choices in forms, or as a "select all" control that can show a partially-selected dash.
459
+
460
+ `import { Checkbox } from "twico-ui"`
461
+
462
+ Props:
463
+ - `label` — `React.ReactNode` — Renders text or a node beside the box, serving as the clickable caption that describes what the checkbox toggles.
464
+ - `description` — `React.ReactNode` — Shows secondary helper text below the label to clarify the option's meaning or consequences without crowding it.
465
+ - `indeterminate` — `boolean` · default `false` — Displays the mixed dash state instead of a check, ideal for a select-all that is only partially selected.
466
+ - `size` — `"sm" | "md"` · default `"md"` — Adjusts the checkbox dimensions between sm and md to match the density of the surrounding form layout.
467
+ - `tone` — `"primary" | "success" | "warning" | "danger" | "info" | "neutral"` · default `"primary"` — Sets the accent color of the checked/active state from six semantic intents like success, danger, or info.
468
+ - `checked` — `boolean` — Drives the checked state in controlled mode, requiring an onChange handler to keep the input in sync.
469
+ - `defaultChecked` — `boolean` — Sets the initial checked state for uncontrolled usage, after which the input manages its own value internally.
470
+ - `disabled` — `boolean` · default `false` — Blocks interaction and dims the control when true, signaling the option is currently unavailable to the user.
471
+ - `onChange` — `React.ChangeEventHandler<HTMLInputElement>` — Fires on every toggle with the native change event, letting you read the new checked state and update state.
472
+ - `id` — `string` — Sets the input's id used to associate the label, auto-generated when omitted so the click target stays connected.
473
+ - `className` — `string` · default `""` — Appends extra class names to the wrapping label element for custom spacing, alignment, or layout overrides.
474
+ - `error` — `React.ReactNode` — Error message shown below the control; tints the box red and wires aria-invalid/aria-describedby.
475
+ - `name` — `string` — Form field name submitted with the value.
476
+ - `placeholder` — `string` — Placeholder text shown while the field is empty.
477
+ - `readOnly` — `boolean` · default `false` — Makes the field non-editable while still focusable/selectable.
478
+ - `required` — `boolean` · default `false` — Marks the field required for native form validation.
479
+ - `autoComplete` — `string` — Browser autocomplete hint (e.g. "email", "off").
480
+
481
+ ```jsx
482
+ import { Checkbox } from "twico-ui";
483
+
484
+ <Checkbox label="Subscribe to updates" defaultChecked />
485
+ <Checkbox label="Select all" indeterminate />
486
+ <Checkbox
487
+ label="Notifications"
488
+ description="Get email alerts for new activity"
489
+ />
490
+ <Checkbox label="Disabled option" disabled />
491
+ <Checkbox label="Compact" size="sm" />
492
+ ```
493
+
494
+ #### ColorPicker
495
+
496
+ ColorPicker is a popover-based color input featuring a saturation/value square, a hue slider, a hex text field, and preset swatches, with the value represented as a hex string. Use it when you need users to choose a color (e.g. a brand or theme color), either controlled via value/onChange or uncontrolled via defaultValue.
497
+
498
+ `import { ColorPicker } from "twico-ui"`
499
+
500
+ Props:
501
+ - `label` — `React.ReactNode` — Renders an optional caption above the trigger to describe which color the picker controls, such as a brand color.
502
+ - `tone` — `"primary" | "success" | "warning" | "danger" | "info" | "neutral"` · default `"primary"` — Recolors the hex-input focus accent using one of six semantic intents; defaults to primary.
503
+ - `value` — `string` — Sets the selected hex color in controlled mode, pairing with onChange to keep the displayed color in sync.
504
+ - `defaultValue` — `string` · default `"#6366F1"` — Provides the initial hex color for uncontrolled usage, after which the picker tracks selection internally.
505
+ - `presets` — `string[]` · default `8 default swatches (e.g. #6366F1, #0EA5E9, #14B8A6, ...)` — Lists hex swatch colors shown below the picker for quick one-click selection of common or brand-approved colors.
506
+ - `onChange` — `(hex: string) => void` — Fires with the new hex string whenever the user adjusts saturation, hue, the text field, or picks a preset.
507
+ - `disabled` — `boolean` · default `false` — Disable all interaction (the popover cannot be opened).
508
+ - `hint` — `React.ReactNode` — Helper text shown below when there is no error.
509
+ - `error` — `React.ReactNode` — Error message — turns the field red and replaces the hint.
510
+ - `required` — `boolean` · default `false` — Marks the field required (adds an asterisk to the label).
511
+
512
+ ```jsx
513
+ import { ColorPicker } from "twico-ui";
514
+
515
+ // Controlled
516
+ const [color, setColor] = React.useState("#6366F1");
517
+ <ColorPicker label="Brand color" value={color} onChange={setColor} />
518
+
519
+ // Uncontrolled with custom presets
520
+ <ColorPicker
521
+ defaultValue="#14B8A6"
522
+ presets={["#6366F1", "#14B8A6", "#F43F5E"]}
523
+ />
524
+ ```
525
+
526
+ #### Combobox
527
+
528
+ Combobox is a searchable single-select dropdown (MUI-Autocomplete style) where you type directly in the field to filter options, with support for grouped options and two-line (title + subtitle) entries. Use it when a select list is long enough that users benefit from typeahead filtering rather than scrolling.
529
+
530
+ `import { Combobox } from "twico-ui"`
531
+
532
+ Props:
533
+ - `label` — `React.ReactNode` — Renders a caption above the control to identify the field and improve accessibility for the searchable select.
534
+ - `hint` — `React.ReactNode` — Shows helper text below the field to guide input, displayed only while no error message is present.
535
+ - `error` — `React.ReactNode` — Displays an error message, marks the field invalid, and replaces the hint to flag a failed validation.
536
+ - `required` — `boolean` · default `false` — Adds a required asterisk next to the label, signaling that the user must select a value before submitting.
537
+ - `size` — `"sm" | "md" | "lg"` · default `"md"` — Sets the control height across sm, md, or lg to match the density of the surrounding form.
538
+ - `tone` — `"primary" | "success" | "warning" | "danger" | "info" | "neutral"` · default `"primary"` — Recolors the focus/open accent (border + ring) using one of six semantic intents; defaults to primary.
539
+ - `placeholder` — `string` · default `"Select…"` — Sets the muted text shown in the empty field before the user types to filter or selects an option.
540
+ - `options` — `Array<string | ComboboxOption | ComboboxGroup>` · **required** — Defines the selectable items as plain strings, value/label/description objects, or grouped option collections.
541
+ - `value` — `string | null` — Sets the selected option's value in controlled mode, pairing with onChange to keep the field in sync.
542
+ - `defaultValue` — `string | null` · default `null` — Provides the initial selection for uncontrolled usage, defaulting to null so the field starts empty.
543
+ - `onChange` — `(value: string | null) => void` — Fires with the newly selected value, or null when the selection is cleared, so you can update state.
544
+ - `clearable` — `boolean` · default `false` — Adds a clear button when a value is selected, letting users reset the field back to an empty selection.
545
+ - `disabled` — `boolean` · default `false` — Blocks typing and selection while dimming the field, indicating the control is currently unavailable.
546
+ - `placement` — `"bottom" | "top"` · default `"bottom"` — Open the menu upward instead of down (e.g. near a viewport bottom).
547
+ - `portal` — `boolean` · default `false` — Render the dropdown in a portal (position:fixed on document.body) so it is never clipped by a scrolling/overflow-hidden ancestor; auto-flips up near the viewport bottom.
548
+ - `minWidth` — `number` · default `0` — Minimum popover width in px when portaled (useful when the control is narrow).
549
+ - `name` — `string` — Form field name submitted with the value.
550
+ - `readOnly` — `boolean` · default `false` — Makes the field non-editable while still focusable/selectable.
551
+ - `autoComplete` — `string` — Browser autocomplete hint (e.g. "email", "off").
552
+
553
+ ```jsx
554
+ import { Combobox } from "twico-ui";
555
+
556
+ const [country, setCountry] = useState(null);
557
+
558
+ <Combobox
559
+ label="Country"
560
+ placeholder="Search a country"
561
+ clearable
562
+ value={country}
563
+ onChange={setCountry}
564
+ options={[
565
+ { group: "Asia", options: [
566
+ { value: "id", label: "Indonesia", description: "Jakarta" },
567
+ { value: "jp", label: "Japan", description: "Tokyo" },
568
+ ]},
569
+ { group: "Europe", options: [
570
+ { value: "de", label: "Germany", description: "Berlin" },
571
+ ]},
572
+ ]}
573
+ />
574
+ ```
575
+
576
+ #### Currency
577
+
578
+ Currency is a text input for monetary amounts with a fixed currency defined in code: it renders the currency symbol as a prefix, the currency code as a suffix, and enforces the currency's decimal precision as you type. Use it when the currency is known up front (use CurrencyField when the user should pick one).
579
+
580
+ `import { Currency } from "twico-ui"`
581
+
582
+ Props:
583
+ - `label` — `React.ReactNode` — Renders a label above the input describing the monetary field, accepting any ReactNode for icons or rich text.
584
+ - `hint` — `React.ReactNode` — Shows helper text below the input to guide entry, displayed only when no error is present.
585
+ - `error` — `React.ReactNode` — Displays an error message below the input and marks the field invalid, overriding any hint while set.
586
+ - `required` — `boolean` · default `false` — Marks the field as required and renders an asterisk beside the label; off by default.
587
+ - `size` — `"sm" | "md" | "lg"` · default `"md"` — Sets the input height and padding (sm, md, or lg) to match the surrounding form's density, defaulting to "md".
588
+ - `tone` — `"primary" | "success" | "warning" | "danger" | "info" | "neutral"` · default `"primary"` — Recolors the focus/open accent (border + ring) using one of six semantic intents; defaults to primary.
589
+ - `currency` — `string` · default `"USD"` — Selects the fixed currency by code from the built-in table (e.g. USD, EUR, IDR, JPY), driving symbol and precision.
590
+ - `precision` — `number` — Overrides the number of decimal digits enforced while typing, instead of the currency's default precision.
591
+ - `symbol` — `string` — Overrides the prefix symbol shown before the amount, useful when the built-in symbol for the currency isn't desired.
592
+ - `code` — `string` — Overrides the suffix code displayed after the amount, replacing the default currency code label.
593
+ - `value` — `string | number` — Sets the controlled amount as a string or number, keeping the input value driven entirely by your state.
594
+ - `defaultValue` — `string | number` · default `""` — Sets the initial amount for uncontrolled usage, after which the input manages its own value; defaults to an empty string.
595
+ - `onChange` — `(e: React.ChangeEvent<HTMLInputElement>) => void` — The native change handler receiving the raw input event, useful when you need the underlying DOM event.
596
+ - `onValueChange` — `(value: number | null, formatted: string) => void` — Fires with the parsed numeric value (or null) and the formatted display string whenever the amount changes.
597
+ - `name` — `string` — Form field name submitted with the value.
598
+ - `placeholder` — `string` — Placeholder text shown while the field is empty.
599
+ - `disabled` — `boolean` · default `false` — Disables the control — no interaction and a dimmed appearance.
600
+ - `readOnly` — `boolean` · default `false` — Makes the field non-editable while still focusable/selectable.
601
+ - `autoComplete` — `string` — Browser autocomplete hint (e.g. "email", "off").
602
+
603
+ ```jsx
604
+ import { Currency } from "twico-ui";
605
+
606
+ const [price, setPrice] = useState(0);
607
+
608
+ <Currency label="Price" currency="USD" value={price} onValueChange={setPrice} />
609
+ <Currency label="Harga" currency="IDR" />
610
+ <Currency label="Total" currency="JPY" />
611
+ <Currency label="Custom" symbol="₿" code="BTC" precision={8} />
612
+ ```
613
+
614
+ #### CurrencyField
615
+
616
+ CurrencyField is a currency amount input where the user picks the currency from a searchable dropdown, and that choice drives the prefix symbol, suffix code, and enforced decimal precision. Use it when you need to capture a monetary amount whose currency can be chosen and changed by the user (switching currency re-clamps the amount to the new precision).
617
+
618
+ `import { CurrencyField } from "twico-ui"`
619
+
620
+ Props:
621
+ - `label` — `React.ReactNode` — Renders a label above the field describing the amount, accepting any ReactNode for icons or formatted text.
622
+ - `hint` — `React.ReactNode` — Shows helper text below the field to guide entry, displayed only when no error message is present.
623
+ - `error` — `React.ReactNode` — Displays an error message and marks the field invalid when set, taking visual precedence over the hint.
624
+ - `required` — `boolean` · default `false` — Marks the field as required and renders an asterisk beside the label; off by default.
625
+ - `size` — `"sm" | "md" | "lg"` · default `"md"` — Sets the input and dropdown height (sm, md, or lg) to match the surrounding form's density, defaulting to "md".
626
+ - `tone` — `"primary" | "success" | "warning" | "danger" | "info" | "neutral"` · default `"primary"` — Recolors the focus/open accent (border + ring) using one of six semantic intents; defaults to primary.
627
+ - `currency` — `string` — Controls the selected currency code from your state, keeping the chosen currency driven externally.
628
+ - `defaultCurrency` — `string` · default `"USD"` — Sets the initially selected currency code for uncontrolled usage, defaulting to "USD".
629
+ - `onCurrencyChange` — `(code: string) => void` — Fires with the new code when the user picks a different currency from the searchable dropdown.
630
+ - `currencies` — `Array<string | CurrencyOption>` — Restricts the selectable currencies to the given codes or {value,label} options, instead of the full built-in list.
631
+ - `value` — `string | number` — Sets the controlled amount as a string or number, keeping the entered value driven entirely by your state.
632
+ - `defaultValue` — `string | number` · default `""` — Sets the initial amount for uncontrolled usage, after which the field manages its own value; defaults to an empty string.
633
+ - `onValueChange` — `(value: number | null, formatted: string, currency: string) => void` — Fires with the parsed number (or null), the formatted string, and the active currency code whenever either changes.
634
+ - `onChange` — `(e: React.ChangeEvent<HTMLInputElement>) => void` — Native change handler (receives the event); fires alongside onValueChange, matching Currency. Previously typed-out via Omit and silently dropped at runtime.
635
+ - `name` — `string` — Form field name submitted with the value.
636
+ - `placeholder` — `string` — Placeholder text shown while the field is empty.
637
+ - `disabled` — `boolean` · default `false` — Disables the control — no interaction and a dimmed appearance.
638
+ - `readOnly` — `boolean` · default `false` — Makes the field non-editable while still focusable/selectable.
639
+ - `autoComplete` — `string` — Browser autocomplete hint (e.g. "email", "off").
640
+
641
+ ```jsx
642
+ import { CurrencyField } from "twico-ui";
643
+
644
+ <CurrencyField
645
+ label="Amount"
646
+ defaultCurrency="USD"
647
+ onValueChange={(n, str, code) => {}}
648
+ />
649
+
650
+ {/* limit the currency choices */}
651
+ <CurrencyField
652
+ label="Amount"
653
+ defaultCurrency="EUR"
654
+ currencies={["USD", "EUR", "GBP", "IDR"]}
655
+ />
656
+ ```
657
+
658
+ #### DatePicker
659
+
660
+ DatePicker is a calendar-popover date input with month/year navigation, a month grid, today highlight, min/max bounds, and a clearable value. Use it when you need users to select a single date with optional range limits and custom formatting.
661
+
662
+ `import { DatePicker } from "twico-ui"`
663
+
664
+ Props:
665
+ - `label` — `React.ReactNode` — Optional field label rendered above the input to name the control and associate it for accessibility.
666
+ - `tone` — `"primary" | "success" | "warning" | "danger" | "info" | "neutral"` · default `"primary"` — Recolors the focus/open accent (border + ring) using one of six semantic intents; defaults to primary.
667
+ - `value` — `Date | null` — Controlled selected date, or null when empty; pair it with onChange to own the value in state.
668
+ - `defaultValue` — `Date | null` · default `null` — Initial date for uncontrolled usage when value is omitted, defaulting to null so the field starts empty.
669
+ - `placeholder` — `string` · default `"Select date"` — Text shown in the trigger when no date is selected, defaulting to "Select date" to prompt the user.
670
+ - `min` — `Date` — Earliest selectable date; dates before it are disabled in the calendar grid to enforce a lower bound.
671
+ - `max` — `Date` — Latest selectable date; dates after it are disabled in the calendar grid to enforce an upper bound.
672
+ - `disabled` — `boolean` · default `false` — Disables the control and prevents the calendar popover from opening, useful for read-only or gated form states.
673
+ - `clearable` — `boolean` · default `true` — Shows a clear (×) button when a date is set so users can reset the field, enabled by default.
674
+ - `format` — `(date: Date) => string` — Custom formatter that turns the selected Date into display text, defaulting to a localized medium date string.
675
+ - `weekStartsOn` — `0 | 1 | 2 | 3 | 4 | 5 | 6` · default `0` — Widened from 0|1 to any weekday; controls which day the calendar grid + weekday header start on (0 = Sunday).
676
+ - `onChange` — `(date: Date | null) => void` — Fires with the newly selected Date, or null when the value is cleared, so you can update state.
677
+ - `hint` — `React.ReactNode` — Helper text shown below when there is no error.
678
+ - `error` — `React.ReactNode` — Error message — turns the field red and replaces the hint.
679
+ - `required` — `boolean` · default `false` — Marks the field required (adds an asterisk to the label).
680
+ - `locale` — `string` — BCP-47 locale for Intl-derived month/weekday names and date formatting; omit for the runtime default.
681
+
682
+ ```jsx
683
+ import { DatePicker } from "twico-ui";
684
+
685
+ const [date, setDate] = useState(null);
686
+
687
+ <DatePicker
688
+ label="Start date"
689
+ value={date}
690
+ onChange={setDate}
691
+ min={new Date()}
692
+ weekStartsOn={1}
693
+ />
694
+ ```
695
+
696
+ #### DateRangePicker
697
+
698
+ DateRangePicker is a calendar input for selecting a start and end date with click-start/click-end selection, in-range highlighting, hover preview, and quick presets (Last 7/14/30/90 days). Use it when a form needs a reporting period or any bounded date span.
699
+
700
+ `import { DateRangePicker } from "twico-ui"`
701
+
702
+ Props:
703
+ - `label` — `React.ReactNode` — Optional field label rendered above the control to name the date range input and aid accessibility.
704
+ - `tone` — `"primary" | "success" | "warning" | "danger" | "info" | "neutral"` · default `"primary"` — Recolors the focus/open accent (border + ring) using one of six semantic intents; defaults to primary.
705
+ - `value` — `DateRange` — Controlled range as a { start, end } object; pair it with onChange to own the selection in state.
706
+ - `defaultValue` — `DateRange` · default `{ start: null, end: null }` — Initial range for uncontrolled usage, defaulting to { start: null, end: null } so the field starts empty.
707
+ - `placeholder` — `string` · default `"Select date range"` — Text shown in the trigger when no range is selected, defaulting to "Select date range" to prompt the user.
708
+ - `presets` — `boolean` · default `true` — Shows the quick-preset column (Last 7/14/30/90 days) for fast range selection, enabled by default.
709
+ - `weekStartsOn` — `0 | 1 | 2 | 3 | 4 | 5 | 6` · default `0` — Widened from 0|1 to any weekday; controls which day the calendar grid + weekday header start on (0 = Sunday).
710
+ - `onChange` — `(range: DateRange) => void` — Fires with the updated { start, end } range whenever the selection changes so you can update state.
711
+ - `disabled` — `boolean` · default `false` — Disable all interaction (the popover cannot be opened).
712
+ - `hint` — `React.ReactNode` — Helper text shown below when there is no error.
713
+ - `error` — `React.ReactNode` — Error message — turns the field red and replaces the hint.
714
+ - `required` — `boolean` · default `false` — Marks the field required (adds an asterisk to the label).
715
+ - `locale` — `string` — BCP-47 locale for Intl-derived month/weekday names and date formatting; omit for the runtime default.
716
+
717
+ ```jsx
718
+ import { DateRangePicker } from "twico-ui";
719
+
720
+ const [range, setRange] = useState({ start: null, end: null });
721
+
722
+ <DateRangePicker
723
+ label="Reporting period"
724
+ value={range}
725
+ onChange={setRange}
726
+ weekStartsOn={1}
727
+ />
728
+ ```
729
+
730
+ #### Field
731
+
732
+ Field is the reusable form-field wrapper that supplies the label, required asterisk, helper hint, and error message (with the matching ARIA ids) that Input and Select already use internally. Wrap any control with it to get consistent field spacing, the danger-colored error treatment, and the aria-describedby target without re-implementing the markup.
733
+
734
+ `import { Field } from "twico-ui"`
735
+
736
+ Props:
737
+ - `label` — `React.ReactNode` — Field label rendered above the control.
738
+ - `hint` — `React.ReactNode` — Helper text shown below when there is no error.
739
+ - `error` — `React.ReactNode` — Error message — replaces the hint and renders in the danger color.
740
+ - `required` — `boolean` · default `false` — Marks the field required, adding an asterisk after the label.
741
+ - `htmlFor` — `string` — Id of the control the <label> binds to (sets the label's htmlFor).
742
+ - `size` — `"sm" | "md" | "lg"` · default `"md"` — Spacing scale for parity with Input/Select; exposed via data-size on the root.
743
+ - `id` — `string` · default `auto` — Base id; the hint/error element gets `${id}-desc` for your control's aria-describedby.
744
+ - `children` — `React.ReactNode` — The control(s) the field wraps.
745
+
746
+ ```jsx
747
+ import { Field } from "twico-ui";
748
+
749
+ // Wrap any control for the shared label/hint/error chrome. Give Field an id and set
750
+ // the control's aria-describedby to that id followed by -desc to announce hint/error.
751
+ <Field label="Email" id="email" hint="We'll never share it." required htmlFor="email">
752
+ <input id="email" type="email" aria-describedby="email-desc" />
753
+ </Field>
754
+
755
+ <Field label="Token" id="tok" error="That token is invalid." htmlFor="tok">
756
+ <input id="tok" aria-invalid aria-describedby="tok-desc" />
757
+ </Field>
758
+ ```
759
+
760
+ #### FileUpload
761
+
762
+ FileUpload is a drag-and-drop dropzone with a click-to-browse fallback and a removable list of selected files. Use it when you need users to attach one or more files, either controlled via value/onChange or uncontrolled.
763
+
764
+ `import { FileUpload } from "twico-ui"`
765
+
766
+ Props:
767
+ - `accept` — `string` — Restricts selectable files using a native input accept string such as "image/*,.pdf", filtering the browse dialog and drops.
768
+ - `tone` — `"primary" | "success" | "warning" | "danger" | "info" | "neutral"` · default `"primary"` — Recolors the dropzone focus ring and drag-active border using one of six semantic intents; defaults to primary.
769
+ - `multiple` — `boolean` · default `false` — Allows selecting and attaching more than one file at a time when true; defaults to single-file selection.
770
+ - `disabled` — `boolean` · default `false` — Disables the dropzone and underlying input when true, blocking clicks, drops, and any further file changes.
771
+ - `hint` — `React.ReactNode` — Renders a hint line under the prompt, defaulting to the accepted types or "Any file" when not provided.
772
+ - `value` — `File[]` — Supplies the controlled array of selected File objects, making the component fully managed by parent state.
773
+ - `onChange` — `(files: File[]) => void` — Fires with the updated File array whenever files are added or removed, letting you sync controlled state.
774
+ - `defaultValue` — `File[]` · default `[]` — Uncontrolled initial file list.
775
+ - `label` — `React.ReactNode` — Field label rendered above the dropzone.
776
+ - `error` — `React.ReactNode` — Error message shown below the dropzone; turns the border red (sets aria-invalid).
777
+ - `required` — `boolean` · default `false` — Marks the field required (adds an asterisk to the label).
778
+
779
+ ```jsx
780
+ import { FileUpload } from "twico-ui";
781
+
782
+ const [files, setFiles] = React.useState([]);
783
+
784
+ <FileUpload
785
+ multiple
786
+ accept="image/*,.pdf"
787
+ value={files}
788
+ onChange={setFiles}
789
+ hint="PNG, JPG or PDF up to 10MB"
790
+ />
791
+ ```
792
+
793
+ #### Input
794
+
795
+ A text input with optional label, hint text, error/validation state, and inline leading/trailing icons. Use it for any single-line form field; with type="password" it gets a built-in reveal/hide eye toggle.
796
+
797
+ `import { Input } from "twico-ui"`
798
+
799
+ Props:
800
+ - `label` — `React.ReactNode` — Renders a label node above the control so the field has an accessible, visible name for the user.
801
+ - `hint` — `React.ReactNode` — Shows helper text below the input to guide entry; it is hidden once an error message is present.
802
+ - `error` — `React.ReactNode` — Displays a validation error below the field, switches the control to its red invalid state, and replaces the hint.
803
+ - `required` — `boolean` · default `false` — Marks the field as required and appends an asterisk to the label to signal mandatory input; defaults to false.
804
+ - `size` — `"sm" | "md" | "lg"` · default `"md"` — Sets the control height and padding (sm, md, or lg) to match the density of the surrounding form.
805
+ - `tone` — `"primary" | "success" | "warning" | "danger" | "info" | "neutral"` · default `"primary"` — Recolors the focus/open accent (border + ring) using one of six semantic intents; defaults to primary.
806
+ - `leftIcon` — `React.ReactNode` — Renders an icon node inside the field on the leading edge, useful as a prefix like a search or mail glyph.
807
+ - `rightIcon` — `React.ReactNode` — Renders a trailing icon node inside the field, overriding the built-in password reveal toggle when supplied.
808
+ - `onChange` — `(e: React.ChangeEvent) => void` — Native change handler on the underlying form control.
809
+ - `name` — `string` — Form field name submitted with the value.
810
+ - `placeholder` — `string` — Placeholder text shown while the field is empty.
811
+ - `disabled` — `boolean` · default `false` — Disables the control — no interaction and a dimmed appearance.
812
+ - `readOnly` — `boolean` · default `false` — Makes the field non-editable while still focusable/selectable.
813
+ - `autoComplete` — `string` — Browser autocomplete hint (e.g. "email", "off").
814
+
815
+ ```jsx
816
+ import { Input } from "twico-ui";
817
+
818
+ const SearchIcon = () => (<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><circle cx="11" cy="11" r="8" /><path d="m21 21-4.3-4.3" /></svg>);
819
+
820
+ <Input label="Email" type="email" placeholder="you@twico.dev" required />
821
+ <Input label="Search" leftIcon={<SearchIcon />} placeholder="Search…" />
822
+ <Input label="Amount" rightIcon={<span>USD</span>} placeholder="0.00" />
823
+ <Input label="Password" type="password" placeholder="••••••••" />
824
+ <Input label="Username" hint="Choose a unique handle" placeholder="twico" />
825
+ <Input label="Email" type="email" error="Invalid email address" defaultValue="nope" />
826
+ ```
827
+
828
+ #### MultiSelect
829
+
830
+ MultiSelect is a MUI-Autocomplete-style multi-select where chosen values appear as removable chips and you type inline to filter a checkable options overlay. Use it when users need to pick several values from a list that benefits from search, grouped headings, or two-line (label + description) options.
831
+
832
+ `import { MultiSelect } from "twico-ui"`
833
+
834
+ Props:
835
+ - `options` — `Array<string | MultiSelectOption | MultiSelectGroup>` · **required** — Supplies the selectable options as plain strings, {value,label,description} objects, or {group,options} grouped sections.
836
+ - `label` — `React.ReactNode` — Renders a label node above the control so the multi-select has an accessible, visible name for the user.
837
+ - `hint` — `React.ReactNode` — Shows helper text below the control to guide selection; it is hidden once an error message is present.
838
+ - `error` — `React.ReactNode` — Displays a validation error below the control and styles it as invalid to flag a selection problem.
839
+ - `required` — `boolean` · default `false` — Marks the field as required and adds an asterisk to the label to signal a mandatory selection; defaults to false.
840
+ - `placeholder` — `string` · default `"Select…"` — Sets the prompt text shown when no chips are selected, defaulting to "Select…" to invite a choice.
841
+ - `value` — `string[]` — Controls the selected values as a string array, making the component fully controlled when paired with onChange.
842
+ - `defaultValue` — `string[]` · default `[]` — Seeds the initially selected values for uncontrolled usage, defaulting to an empty array of no selections.
843
+ - `onChange` — `(values: string[]) => void` — Fires with the new array of selected values whenever a chip is added or removed from the selection.
844
+ - `disabled` — `boolean` · default `false` — Disables the control and blocks all interaction when set to true, greying out the field; defaults to false.
845
+ - `size` — `"sm" | "md" | "lg"` · default `"md"` — Control height preset, matching Select/Combobox.
846
+ - `tone` — `"primary" | "success" | "warning" | "danger" | "info" | "neutral"` · default `"primary"` — Recolors the focus/open accent (border + ring) using one of six semantic intents; defaults to primary.
847
+ - `clearable` — `boolean` · default `false` — Show a clear-all (×) affix when at least one value is selected.
848
+ - `placement` — `"bottom" | "top"` · default `"bottom"` — Open the menu upward instead of down (e.g. near a viewport bottom).
849
+ - `portal` — `boolean` · default `false` — Render the dropdown in a portal (position:fixed on document.body) so it is never clipped by a scrolling/overflow-hidden ancestor; auto-flips up near the viewport bottom.
850
+ - `minWidth` — `number` · default `0` — Minimum popover width in px when portaled (useful when the control is narrow).
851
+ - `name` — `string` — Form field name submitted with the value.
852
+ - `readOnly` — `boolean` · default `false` — Makes the field non-editable while still focusable/selectable.
853
+ - `autoComplete` — `string` — Browser autocomplete hint (e.g. "email", "off").
854
+
855
+ ```jsx
856
+ import { MultiSelect } from "twico-ui";
857
+
858
+ const [tags, setTags] = useState(["react", "tailwind"]);
859
+
860
+ <MultiSelect
861
+ label="Tech stack"
862
+ placeholder="Add technologies"
863
+ value={tags}
864
+ onChange={setTags}
865
+ options={[
866
+ { group: "Frameworks", options: [
867
+ { value: "react", label: "React", description: "UI library" },
868
+ { value: "vue", label: "Vue", description: "Progressive framework" },
869
+ ]},
870
+ { group: "Tooling", options: ["tailwind", "typescript"] },
871
+ ]}
872
+ />
873
+ ```
874
+
875
+ #### Radio
876
+
877
+ Radio is a single radio button rendered with an optional label and description. Use several Radio inputs sharing the same `name` to let users pick exactly one option from a group.
878
+
879
+ `import { Radio } from "twico-ui"`
880
+
881
+ Props:
882
+ - `label` — `React.ReactNode` — Text or node rendered beside the radio dot, naming the option the user selects when choosing it.
883
+ - `description` — `React.ReactNode` — Secondary helper text shown under the label to add context or clarify what selecting the option means.
884
+ - `size` — `"sm" | "md"` · default `"md"` — Visual size of the radio control, sm or md, to align with the density of nearby form fields.
885
+ - `tone` — `"primary" | "success" | "warning" | "danger" | "info" | "neutral"` · default `"primary"` — Sets the accent color of the checked/active state from six semantic intents like success, danger, or info.
886
+ - `error` — `React.ReactNode` — Error message shown below the control; tints the dot red and wires aria-invalid/aria-describedby.
887
+ - `onChange` — `(e: React.ChangeEvent) => void` — Native change handler on the underlying form control.
888
+ - `name` — `string` — Form field name submitted with the value.
889
+ - `placeholder` — `string` — Placeholder text shown while the field is empty.
890
+ - `disabled` — `boolean` · default `false` — Disables the control — no interaction and a dimmed appearance.
891
+ - `readOnly` — `boolean` · default `false` — Makes the field non-editable while still focusable/selectable.
892
+ - `required` — `boolean` · default `false` — Marks the field required for native form validation.
893
+ - `autoComplete` — `string` — Browser autocomplete hint (e.g. "email", "off").
894
+
895
+ ```jsx
896
+ import { Radio } from "twico-ui";
897
+
898
+ <Radio name="plan" value="free" label="Free" defaultChecked />
899
+ <Radio
900
+ name="plan"
901
+ value="pro"
902
+ label="Pro"
903
+ description="$12 / month"
904
+ />
905
+ ```
906
+
907
+ #### Rating
908
+
909
+ Rating is a star-rating input that works either as an interactive control (click or hover to set a score) or as a read-only display. Use it to capture user feedback like reviews or to show an existing score; clicking the current star again clears the rating to 0.
910
+
911
+ `import { Rating } from "twico-ui"`
912
+
913
+ Props:
914
+ - `value` — `number` — Controlled rating from 0 to count, letting the parent own the score and re-render when it changes.
915
+ - `defaultValue` — `number` · default `0` — Initial score for uncontrolled usage, defaulting to 0 so no stars start filled until the user interacts.
916
+ - `count` — `number` · default `5` — Total number of stars rendered, defaulting to 5, which sets the maximum selectable score.
917
+ - `size` — `"sm" | "md" | "lg"` · default `"md"` — Sets the star size (sm, md, or lg) to match the surrounding text density, defaulting to md.
918
+ - `tone` — `"primary" | "success" | "warning" | "danger" | "info" | "neutral"` · default `"warning"` — Sets the filled-star color from six semantic intents; defaults to warning (gold). The explicit color prop overrides it.
919
+ - `color` — `string` — Overrides the filled-star color with any CSS color, falling back to the warning/amber token when omitted.
920
+ - `readOnly` — `boolean` · default `false` — Renders the stars as a non-interactive display only, useful for showing an existing score without accepting input.
921
+ - `showValue` — `boolean` · default `false` — Displays the numeric score beside the stars when enabled, giving an exact figure alongside the visual rating.
922
+ - `onChange` — `(value: number) => void` — Fires with the new rating when the user clicks a star, or 0 when they clear the current selection.
923
+ - `disabled` — `boolean` · default `false` — Disable all interaction and dim the control.
924
+
925
+ ```jsx
926
+ import { Rating } from "twico-ui";
927
+
928
+ // Interactive
929
+ const [r, setR] = React.useState(0);
930
+ <Rating value={r} onChange={setR} />
931
+
932
+ // Read-only display with numeric value
933
+ <Rating value={4} readOnly showValue />
934
+
935
+ // Uncontrolled, larger stars
936
+ <Rating defaultValue={3} size="lg" count={5} />
937
+ ```
938
+
939
+ #### Select
940
+
941
+ A custom select that replaces the native browser dropdown with a rounded, keyboard-navigable popover, supporting grouped options and two-line (title + subtitle) options. Use it when you need a styled single-select with optional in-popover search, grouping, or descriptions.
942
+
943
+ `import { Select } from "twico-ui"`
944
+
945
+ Props:
946
+ - `label` — `React.ReactNode` — Renders a field label above the trigger to identify what the select controls within a form.
947
+ - `hint` — `React.ReactNode` — Shows helper text below the trigger to guide selection, automatically hidden once an error message is set.
948
+ - `error` — `React.ReactNode` — Displays an error message and marks the field invalid, replacing the hint to flag failed validation.
949
+ - `required` — `boolean` · default `false` — Appends a required asterisk to the label, signaling that a selection must be made before submitting.
950
+ - `size` — `"sm" | "md" | "lg"` · default `"md"` — Controls the trigger height (sm, md, or lg) to match the density of nearby form controls, defaulting to md.
951
+ - `tone` — `"primary" | "success" | "warning" | "danger" | "info" | "neutral"` · default `"primary"` — Recolors the focus/open accent (border + ring) using one of six semantic intents; defaults to primary.
952
+ - `placeholder` — `string` · default `"Select…"` — Text shown in the trigger when no value is selected, defaulting to "Select…" as a prompt to choose.
953
+ - `searchPlaceholder` — `string` · default `"Search…"` — Placeholder text for the in-popover search box, defaulting to "Search…" to invite filtering of options.
954
+ - `searchable` — `boolean` — Shows the filter search box inside the popover, auto-enabling when more than five options exist if left unset.
955
+ - `options` — `Array<string | SelectOption | SelectGroup>` · **required** — The selectable items as plain strings, {value,label,description} objects, or {group,options} groups for two-line or grouped lists.
956
+ - `value` — `string | null` — Controlled selected value as a string or null, letting the parent own which option is currently chosen.
957
+ - `defaultValue` — `string | null` · default `null` — Initial selected value for uncontrolled usage, defaulting to null so nothing is selected at first.
958
+ - `onChange` — `(value: string) => void` — Fires with the chosen value string when the user selects an option from the popover.
959
+ - `disabled` — `boolean` · default `false` — Disables the trigger so the popover cannot open and no selection can be made.
960
+ - `placement` — `"bottom" | "top"` · default `"bottom"` — Opens the menu downward or upward from the trigger, defaulting to "bottom" for the typical dropdown direction.
961
+ - `portal` — `boolean` · default `false` — Renders the dropdown in a body portal so it is never clipped, auto-flipping when near the viewport bottom.
962
+ - `minWidth` — `number` · default `0` — Sets the minimum popover width in pixels when portaled, ensuring the menu stays at least this wide.
963
+ - `clearable` — `boolean` · default `false` — Show a clear (×) affix when a value is selected; Delete/Backspace on the closed trigger also clears. Emits onChange(null).
964
+
965
+ ```jsx
966
+ import { Select } from "twico-ui";
967
+
968
+ const [v, setV] = useState(null);
969
+
970
+ <Select
971
+ label="Assignee"
972
+ placeholder="Pick a teammate"
973
+ value={v}
974
+ onChange={setV}
975
+ options={[
976
+ { group: "Design", options: [
977
+ { value: "ada", label: "Ada Park", description: "Product designer" },
978
+ { value: "sam", label: "Sam Lee", description: "Brand designer" },
979
+ ]},
980
+ { group: "Engineering", options: [
981
+ { value: "jo", label: "Jo Kim", description: "Frontend" },
982
+ ]},
983
+ ]}
984
+ />
985
+ ```
986
+
987
+ #### Slider
988
+
989
+ Slider is a range input with a filled track, draggable thumb, a value bubble that appears while dragging, optional step ticks, and full keyboard support (arrows, Home, End). Use it when users need to pick a number within a range, such as volume, price, or other continuous settings.
990
+
991
+ `import { Slider } from "twico-ui"`
992
+
993
+ Props:
994
+ - `label` — `React.ReactNode` — Renders a label above the track to describe what the slider adjusts, such as volume or price.
995
+ - `tone` — `"primary" | "success" | "warning" | "danger" | "info" | "neutral"` · default `"primary"` — Sets the filled-track and thumb accent color from six semantic intents; defaults to primary.
996
+ - `value` — `number` — Controlled current value, letting the parent own the slider position and re-render as the number changes.
997
+ - `defaultValue` — `number` · default `0` — Initial value for uncontrolled usage, defaulting to 0 so the thumb starts at the low end.
998
+ - `min` — `number` · default `0` — Lowest value the slider can reach, defaulting to 0, defining the start of the selectable range.
999
+ - `max` — `number` · default `100` — Highest value the slider can reach, defaulting to 100, defining the end of the selectable range.
1000
+ - `step` — `number` · default `1` — Increment between selectable values, defaulting to 1, which controls how finely the thumb can be positioned.
1001
+ - `disabled` — `boolean` · default `false` — Disables dragging and keyboard input while dimming the slider to show it is currently inactive.
1002
+ - `showValue` — `boolean` · default `true` — Displays the current value at the top-right above the track when true (the default), for an always-visible readout.
1003
+ - `showTicks` — `boolean` · default `false` — Renders step ticks along the rail when enabled, helping users see and snap to discrete increments.
1004
+ - `formatValue` — `(value: number) => React.ReactNode` — Formats the displayed value in the label and drag bubble, useful for adding units, currency, or custom text.
1005
+ - `onChange` — `(value: number) => void` — Fires with the new number whenever the value changes via drag or keyboard, keeping state in sync.
1006
+ - `hint` — `React.ReactNode` — Helper text shown below the track when there is no error.
1007
+ - `error` — `React.ReactNode` — Error message shown below the track; turns it red (sets aria-invalid) and replaces the hint.
1008
+
1009
+ ```jsx
1010
+ import { Slider } from "twico-ui";
1011
+
1012
+ const [vol, setVol] = React.useState(40);
1013
+
1014
+ <Slider label="Volume" value={vol} onChange={setVol} />
1015
+
1016
+ <Slider
1017
+ label="Price"
1018
+ min={0}
1019
+ max={1000}
1020
+ step={50}
1021
+ showTicks
1022
+ defaultValue={250}
1023
+ formatValue={(v) => `$${v}`}
1024
+ />
1025
+ ```
1026
+
1027
+ #### Switch
1028
+
1029
+ Switch is a toggle control with a springy thumb for instant on/off settings, optionally labeled with a title and supporting description. Use it for binary settings that apply immediately, like enabling dark mode or notifications.
1030
+
1031
+ `import { Switch } from "twico-ui"`
1032
+
1033
+ Props:
1034
+ - `label` — `React.ReactNode` — Renders the title text shown beside the switch, naming the setting it toggles like "Enable notifications".
1035
+ - `description` — `React.ReactNode` — Renders secondary helper text below the label to explain what the setting does or its consequences.
1036
+ - `size` — `"sm" | "md"` · default `"md"` — Sets the visual size of the track and thumb (sm or md) to match the density of the surrounding form.
1037
+ - `tone` — `"primary" | "success" | "warning" | "danger" | "info" | "neutral"` · default `"primary"` — Sets the ON-track accent color from six semantic intents; defaults to primary.
1038
+ - `error` — `React.ReactNode` — Error message shown below the control; tints the track red and wires aria-invalid/aria-describedby.
1039
+ - `onChange` — `(e: React.ChangeEvent) => void` — Native change handler on the underlying form control.
1040
+ - `name` — `string` — Form field name submitted with the value.
1041
+ - `placeholder` — `string` — Placeholder text shown while the field is empty.
1042
+ - `disabled` — `boolean` · default `false` — Disables the control — no interaction and a dimmed appearance.
1043
+ - `readOnly` — `boolean` · default `false` — Makes the field non-editable while still focusable/selectable.
1044
+ - `required` — `boolean` · default `false` — Marks the field required for native form validation.
1045
+ - `autoComplete` — `string` — Browser autocomplete hint (e.g. "email", "off").
1046
+
1047
+ ```jsx
1048
+ import { Switch } from "twico-ui";
1049
+
1050
+ <Switch label="Dark mode" defaultChecked />
1051
+
1052
+ <Switch
1053
+ label="Email notifications"
1054
+ description="Send me product updates"
1055
+ />
1056
+
1057
+ <Switch label="Compact view" size="sm" />
1058
+ ```
1059
+
1060
+ #### Textarea
1061
+
1062
+ Textarea is a multi-line text input with an optional label, hint text, and error state for longer-form content. Use it when collecting paragraph-length input like bios, descriptions, or comments.
1063
+
1064
+ `import { Textarea } from "twico-ui"`
1065
+
1066
+ Props:
1067
+ - `label` — `React.ReactNode` — Renders a field label above the textarea, identifying what longer-form input the control collects.
1068
+ - `hint` — `React.ReactNode` — Shows helper text below the textarea to guide input, hidden whenever an error message is present.
1069
+ - `error` — `React.ReactNode` — Displays an error message and marks the field invalid when set, replacing the hint text below.
1070
+ - `required` — `boolean` · default `false` — Adds a required asterisk beside the label when true, signaling that the field must be filled in.
1071
+ - `size` — `"sm" | "md" | "lg"` · default `"md"` — Control size — same padding steps as Input.
1072
+ - `tone` — `"primary" | "success" | "warning" | "danger" | "info" | "neutral"` · default `"primary"` — Recolors the focus/open accent (border + ring) using one of six semantic intents; defaults to primary.
1073
+ - `onChange` — `(e: React.ChangeEvent) => void` — Native change handler on the underlying form control.
1074
+ - `name` — `string` — Form field name submitted with the value.
1075
+ - `placeholder` — `string` — Placeholder text shown while the field is empty.
1076
+ - `rows` — `number` — Visible number of text rows.
1077
+ - `disabled` — `boolean` · default `false` — Disables the control — no interaction and a dimmed appearance.
1078
+ - `readOnly` — `boolean` · default `false` — Makes the field non-editable while still focusable/selectable.
1079
+
1080
+ ```jsx
1081
+ import { Textarea } from "twico-ui";
1082
+
1083
+ <Textarea
1084
+ label="Bio"
1085
+ rows={4}
1086
+ placeholder="Tell us about yourself"
1087
+ hint="Max 280 characters"
1088
+ />
1089
+ ```
1090
+
1091
+ ### Data display
1092
+
1093
+ #### Avatar
1094
+
1095
+ Avatar displays a user's image with a graceful fallback to initials and an optional presence dot. Use it to represent people or entities in lists, headers, comments, and profile menus.
1096
+
1097
+ `import { Avatar } from "twico-ui"`
1098
+
1099
+ Props:
1100
+ - `src` — `string` — The image URL to display, gracefully falling back to derived initials when it is missing or fails to load.
1101
+ - `name` — `string` — The person's full name, used to derive the displayed initials and the avatar's accessible label.
1102
+ - `size` — `"xs" | "sm" | "md" | "lg" | "xl"` · default `"md"` — Sets the overall avatar dimensions from xs to xl to suit lists, headers, comments, or profile menus.
1103
+ - `square` — `boolean` · default `false` — When true, renders the avatar as a rounded square instead of the default circle for a different visual style.
1104
+ - `status` — `"online" | "busy" | "away" | "offline"` — Shows a small presence dot indicating online, busy, away, or offline state on the avatar.
1105
+ - `ring` — `boolean` · default `false` — When true, adds a brand-colored ring around the avatar to emphasize or highlight the user.
1106
+
1107
+ ```jsx
1108
+ import { Avatar } from "twico-ui";
1109
+
1110
+ <Avatar src="/u/jane.jpg" name="Jane Doe" />
1111
+ <Avatar name="Sam Lee" status="online" />
1112
+ <Avatar name="Twico" size="lg" square ring />
1113
+ ```
1114
+
1115
+ #### AvatarMenu
1116
+
1117
+ AvatarMenu is a user avatar that opens a portaled account dropdown with a rich header (avatar, name, email) above the menu items. Use it for the classic account menu — avatar-only in a navbar, or with name/email/chevron in a sidebar.
1118
+
1119
+ `import { AvatarMenu } from "twico-ui"`
1120
+
1121
+ Props:
1122
+ - `name` — `string` — The user's full name, driving the derived initials and the rich header shown atop the dropdown.
1123
+ - `src` — `string` — The avatar image URL shown in both the trigger and the dropdown header, falling back to initials when absent.
1124
+ - `email` — `string` — The email address shown in the dropdown header, and in the trigger when showName is enabled.
1125
+ - `subtitle` — `React.ReactNode` — Overrides the secondary line in the header and trigger, defaulting to the email when not provided.
1126
+ - `status` — `"online" | "busy" | "away" | "offline"` — Shows a presence dot indicating online, busy, away, or offline state on the trigger avatar.
1127
+ - `size` — `"xs" | "sm" | "md" | "lg" | "xl"` · default `"sm"` — Sets the trigger avatar dimensions from xs to xl to fit a compact navbar or a roomier sidebar.
1128
+ - `items` — `MenuItemDef[]` · **required** — The account menu entries to render in the dropdown, using the same MenuItemDef shape as Menu's items.
1129
+ - `showName` — `boolean` · default `false` — When true, displays the name and email beside the avatar in the trigger instead of showing the avatar alone.
1130
+ - `showChevron` — `boolean` · default `showName` — When true, shows a chevron in the trigger to hint at the dropdown, defaulting to the showName value.
1131
+ - `align` — `"start" | "end"` · default `"end"` — Aligns the dropdown's start or end edge to the trigger, defaulting to end for right-anchored account menus.
1132
+
1133
+ ```jsx
1134
+ import { AvatarMenu } from "twico-ui";
1135
+
1136
+ <AvatarMenu
1137
+ name="Ada Park"
1138
+ email="ada@twico.dev"
1139
+ status="online"
1140
+ showName
1141
+ items={[
1142
+ { label: "Profile", onClick: () => {} },
1143
+ { label: "Settings", shortcut: "⌘,", onClick: () => {} },
1144
+ { separator: true },
1145
+ { label: "Sign out", danger: true, onClick: () => {} },
1146
+ ]}
1147
+ />
1148
+ ```
1149
+
1150
+ #### Badge
1151
+
1152
+ Badge is a compact status label or count chip available in six semantic tones and three fill styles. Use it to flag state (active, pending, draft), categories, or small counts inline next to text, list items, or table rows.
1153
+
1154
+ `import { Badge } from "twico-ui"`
1155
+
1156
+ Props:
1157
+ - `tone` — `"primary" | "success" | "warning" | "danger" | "info" | "neutral"` · default `"primary"` — Sets the semantic color from six options like success or danger to convey the badge's state or category.
1158
+ - `variant` — `"soft" | "solid" | "outline"` · default `"soft"` — Chooses the fill style between soft, solid, and outline to control how prominently the badge reads.
1159
+ - `size` — `"sm" | "md" | "lg"` · default `"md"` — Adds a new "sm" option (smaller font padding/height than md) to the existing size scale; md/lg unchanged and md remains the default.
1160
+ - `dot` — `boolean` · default `false` — When true, shows a small leading status dot before the label to reinforce the badge's state.
1161
+ - `children` — `React.ReactNode` — The badge content, typically a short status label or a small count shown inline next to text.
1162
+
1163
+ ```jsx
1164
+ import { Badge } from "twico-ui";
1165
+
1166
+ <Badge tone="success" dot>Active</Badge>
1167
+ <Badge tone="warning" variant="solid">Pending</Badge>
1168
+ <Badge tone="neutral" variant="outline">Draft</Badge>
1169
+ <Badge tone="info" size="lg">12 new</Badge>
1170
+ ```
1171
+
1172
+ #### Card
1173
+
1174
+ Card is a surface container for grouping related content, with an optional header (title/subtitle), body, and footer. Use it to present grouped information such as stats, summaries, or actions, optionally making it interactive with a hover-lift effect.
1175
+
1176
+ `import { Card } from "twico-ui"`
1177
+
1178
+ Props:
1179
+ - `title` — `React.ReactNode` — Renders a bold title in the card header, giving the grouped content a clear heading at the top.
1180
+ - `subtitle` — `React.ReactNode` — Renders a muted subtitle beneath the title to add supporting context or a brief secondary description.
1181
+ - `footer` — `React.ReactNode` — Renders pinned footer content such as action buttons below the card body, separated from the main content.
1182
+ - `variant` — `"elevated" | "outline" | "soft"` · default `"elevated"` — Selects the surface treatment from elevated, outline, or soft to control how the card stands out from the page.
1183
+ - `padding` — `"none" | "md" | "lg"` · default `"md"` — Sets the card's inner padding to none, md, or lg, with none useful for edge-to-edge media or custom layouts.
1184
+ - `interactive` — `boolean` · default `false` — When true, adds a hover-lift effect and pointer cursor to signal the whole card is clickable.
1185
+ - `fullHeight` — `boolean` · default `false` — Stretches the card to fill its parent cell (height: 100%) so cards in a grid or flex row align to equal height.
1186
+ - `children` — `React.ReactNode` — The main body content of the card, rendered between the optional header and footer regions.
1187
+
1188
+ ```jsx
1189
+ import { Card } from "twico-ui";
1190
+
1191
+ <Card
1192
+ title="Monthly revenue"
1193
+ subtitle="June 2026"
1194
+ footer={<Button size="sm">View report</Button>}
1195
+ >
1196
+ Revenue grew 18% month over month.
1197
+ </Card>
1198
+
1199
+ <Card variant="outline" interactive>
1200
+ Hover me — I lift.
1201
+ </Card>
1202
+ ```
1203
+
1204
+ #### Carousel
1205
+
1206
+ Carousel is a sliding one-slide-per-view component with prev/next arrows, dot indicators, optional looping, and optional autoplay that pauses on hover. Use it to showcase a sequence of images or content panels where each child is a single slide.
1207
+
1208
+ `import { Carousel } from "twico-ui"`
1209
+
1210
+ Props:
1211
+ - `showArrows` — `boolean` · default `true` — When true, displays prev/next arrow controls so users can step manually between slides in either direction.
1212
+ - `showDots` — `boolean` · default `true` — When true, displays dot indicators beneath the slides showing position and letting users jump to a specific slide.
1213
+ - `loop` — `boolean` · default `true` — When true, wraps navigation around the ends so advancing past the last slide returns to the first and vice versa.
1214
+ - `autoPlay` — `boolean` · default `false` — When true, automatically advances slides on a timer, pausing while the pointer hovers over the carousel.
1215
+ - `interval` — `number` · default `4000` — Sets the delay in milliseconds between automatic slide advances when autoPlay is enabled, defaulting to 4000.
1216
+ - `children` — `React.ReactNode` — The slide elements, where each direct child is rendered as a single full-width slide in the carousel.
1217
+ - `index` — `number` — Controlled active slide index — pair with onIndexChange.
1218
+ - `defaultIndex` — `number` · default `0` — Initial slide index when uncontrolled.
1219
+ - `onIndexChange` — `(index: number) => void` — Fired when the active slide changes (arrows, dots, or autoplay).
1220
+
1221
+ ```jsx
1222
+ import { Carousel } from "twico-ui";
1223
+
1224
+ <Carousel autoPlay interval={5000}>
1225
+ <img src="/a.jpg" alt="" />
1226
+ <img src="/b.jpg" alt="" />
1227
+ <div>Any slide content</div>
1228
+ </Carousel>
1229
+ ```
1230
+
1231
+ #### Chart
1232
+
1233
+ A lightweight, dependency-free SVG chart that renders bar or line graphs (single or multi-series) with grid, axes, animated entrance, tooltips, and an optional legend. Use it to visualize small datasets like daily metrics or trends without pulling in a charting library.
1234
+
1235
+ `import { Chart } from "twico-ui"`
1236
+
1237
+ Props:
1238
+ - `type` — `"bar" | "line"` · default `"bar"` — Selects the rendering style, drawing the data as grouped bars or as connected lines for trends over time.
1239
+ - `data` — `ChartDatum[]` · **required** — Supplies the array of data points, each carrying a label plus one numeric field for every named series.
1240
+ - `series` — `string[]` · default `["value"]` — Names the numeric fields to plot, where multiple keys yield grouped bars or multiple lines per data point.
1241
+ - `height` — `number` · default `220` — Sets the chart's pixel height, controlling its vertical footprint while it scales horizontally to its container.
1242
+ - `showGrid` — `boolean` · default `true` — Draws horizontal grid lines behind the data, on by default, to make values easier to read against the scale.
1243
+ - `showAxis` — `boolean` · default `true` — Renders axis tick marks and label text, enabled by default, so viewers can map plotted values to categories.
1244
+ - `showLegend` — `boolean` · default `false` — Displays a legend keying each series to its color, off by default and most useful when plotting multiple series.
1245
+ - `valueFormat` — `(value: number) => string` — Formats raw numeric values into display strings for tooltips, letting you add units, separators, or currency symbols.
1246
+ - `colors` — `string[]` · default `built-in token palette` — Per-series colors (any CSS color), cycled when shorter than series.
1247
+ - `ariaLabel` — `string` — Accessible name for the chart's <svg role="img">; defaults to a description of the chart type.
1248
+
1249
+ ```jsx
1250
+ import { Chart } from "twico-ui";
1251
+
1252
+ <Chart
1253
+ type="bar"
1254
+ data={[
1255
+ { label: "Mon", value: 240 },
1256
+ { label: "Tue", value: 310 },
1257
+ { label: "Wed", value: 280 },
1258
+ ]}
1259
+ valueFormat={(v) => `$${v}`}
1260
+ />
1261
+
1262
+ <Chart
1263
+ type="line"
1264
+ series={["signups", "active"]}
1265
+ showLegend
1266
+ data={[
1267
+ { label: "Jan", signups: 120, active: 80 },
1268
+ { label: "Feb", signups: 180, active: 140 },
1269
+ ]}
1270
+ />
1271
+ ```
1272
+
1273
+ #### Datatable
1274
+
1275
+ Datatable is an advanced, MUI Data Grid-style data table with sortable, filterable, hideable, pinnable, reorderable, and resizable columns, plus a toolbar, density control, multi-format export, optional checkbox selection with batch actions, an actions column, pagination, and a skeleton loading state. Use it to display large tabular datasets that need interactive sorting, filtering, grouping, aggregation, or a server-side mode where only the current page is loaded.
1276
+
1277
+ `import { Datatable } from "twico-ui"`
1278
+
1279
+ Props:
1280
+ - `columns` — `DatatableColumn array` · **required** — Defines each column's field, header, type, and behavior, controlling sorting, filtering, formatting, and pinning across the grid.
1281
+ - `rows` — `any array` · **required** — Supplies the row objects to render, holding only the current page's data when serverMode is enabled.
1282
+ - `loading` — `boolean` · default `false` — Replaces the grid body with skeleton rows while data is being fetched; off by default.
1283
+ - `rowKey` — `(row,index)=>key` · default `(r,i)=>r.id??i` — Returns a stable unique key per row, defaulting to the row's id or its index, to keep selection and renders consistent.
1284
+ - `checkboxSelection` — `boolean` · default `false` — Adds a leading checkbox column for selecting rows, enabling batch actions; off by default.
1285
+ - `batchActions` — `DatatableBatchAction array` · default `[]` — Defines actions shown in the selection toolbar that operate on the currently selected rows; empty by default.
1286
+ - `density` — `compact|standard|comfortable` · default `standard` — Sets the row height preset (compact, standard, or comfortable) to trade vertical space for readability, defaulting to standard.
1287
+ - `pageSize` — `number` · default `10` — Sets the initial number of rows shown per page, defaulting to 10; pass 0 to disable pagination.
1288
+ - `pageSizeOptions` — `number array` · default `[5,10,25,50]` — Lists the selectable rows-per-page values offered in the pagination control, defaulting to [5,10,25,50].
1289
+ - `height` — `number` · default `440` — Sets the maximum scroll height of the grid body in pixels before rows scroll internally, defaulting to 440.
1290
+ - `serverMode` — `boolean` · default `false` — Switches to server-side data handling so sorting, filtering, and paging are delegated to your backend; off by default.
1291
+ - `rowCount` — `number` · default `-` — Tells the grid the total number of server-side rows so pagination can render correctly in serverMode.
1292
+ - `onServerChange` — `(state)=>void` · default `-` — Fires with the current query state (sort, filter, page) in serverMode so you can fetch the matching rows.
1293
+ - `showExport` — `boolean` · default `true` — Shows the toolbar Export button for downloading data in supported formats; enabled by default.
1294
+ - `exportFilename` — `string` · default `export` — Sets the base filename used for exported files, defaulting to "export".
1295
+ - `aggregationValues` — `object|null` · default `null` — Supplies precomputed footer aggregation values for serverMode, where totals cannot be calculated client-side; null by default.
1296
+ - `disableColumnReorder` — `boolean` · default `false` — Prevents users from dragging columns into a new order, locking the column arrangement; off by default.
1297
+ - `disableColumnResize` — `boolean` · default `false` — Prevents resizing columns, locking their widths; off by default. When enabled, drag a header's right edge to resize, or double-click it to auto-fit the column to its content (Excel-style).
1298
+ - `editMode` — `boolean` · default `false` — Makes all columns editable inline so users can change cell values directly; off by default.
1299
+ - `onRowUpdate` — `(updated,original,field)=>void` · default `-` — Fires when an inline cell edit is committed, receiving the updated row, the original row, and the edited field.
1300
+ - `onRowsChange` — `(rows)=>void` · default `-` — Fires with the full next array of rows after an edit, letting you persist the new dataset.
1301
+ - `onBatchUpdate` — `(rows,patch,keys)=>void` · default `-` — Fires when a batch edit is applied, receiving the affected rows, the patch object, and the selected keys.
1302
+ - `showPageJumper` — `boolean` · default `true` — Shows a go-to-page input in the pagination footer for jumping directly to a page number; enabled by default.
1303
+ - `selectionMode` — `none|row|cell` · default `none` — Sets click-to-select granularity (none, row, or cell), controlling what gets highlighted on click, defaulting to none.
1304
+ - `onRowClick` — `(row,key)=>void` · default `-` — Fires with the clicked row and its key, useful for navigation or opening a detail view.
1305
+ - `onCellClick` — `(value,row,field)=>void` · default `-` — Fires with the clicked cell's value, its row, and the field name when a cell is clicked.
1306
+ - `onActiveCellChange` — `(cell|null)=>void` · default `-` — Fires when the active cell changes via keyboard or click, receiving the new cell descriptor or null.
1307
+ - `showAggregation` — `boolean` · default `false` — Shows a totals row with column aggregations beneath the data on initial render; off by default.
1308
+ - `ariaLabel` — `string` · default `Data table` — Sets the grid's accessible aria-label for screen readers, defaulting to "Data table".
1309
+ - `rowGrouping` — `string array` · default `[]` — Lists the field names to group rows by on initial render, collapsing rows into expandable groups; empty by default.
1310
+ - `rowPinning` — `boolean` · default `false` — Allows users to pin rows to the top or bottom so they stay visible while scrolling; off by default.
1311
+ - `rowReorder` — `boolean` · default `false` — Enables drag-and-drop reordering of rows so users can change their sequence; off by default.
1312
+ - `rowResize` — `boolean` · default `false` — Allows users to drag row edges to adjust individual row heights; off by default.
1313
+ - `onRowOrderChange` — `(keys)=>void` · default `-` — Fires with the new array of row keys after a drag reorder, letting you persist the updated order.
1314
+ - `pivot` — `object|null` · default `null` — Supplies the initial pivot model defining row, column, and value fields for cross-tabulated analysis; null by default.
1315
+ - `pivotMode` — `boolean` · default `false` — Renders the grid in pivot view from the start instead of the flat table layout; off by default.
1316
+ - `align` — `"left" | "right"` · default `"right" for number/actions columns, else "left"` — Cell alignment; currently affects the actions column's button justification. Already supported at runtime — newly exposed in the .d.ts.
1317
+ - `virtualized` — `boolean` · default `false` — Opt into row virtualization (windowing). Active only when pagination is off (pageSize={0}) and row grouping is not active; otherwise ignored and the table renders normally. Pair with a fixed height. Selection/inline-edit keep working because they key off the row id, not the rendered index.
1318
+ - `overscan` — `number` · default `8` — Extra rows rendered above and below the visible window for smoother fast scrolling when virtualized.
1319
+ - `rowHeight` — `number` — Estimated/fixed row height in px used to compute the virtualization window. Set if rows have a custom height so the scrollbar stays accurate.
1320
+
1321
+ ```jsx
1322
+ import { useState } from "react";
1323
+ import { Datatable, Avatar, Badge } from "twico-ui";
1324
+
1325
+ // Sample data — swap for your own array or API rows. Each row needs a stable `id`.
1326
+ const seed = [
1327
+ { id: 1, name: "Ava Stone", email: "ava.stone@twico.io", role: "Admin", department: "Engineering", status: "active", country: "US", seats: 12, mrr: 480, salary: 128000, startDate: "2023-01-14" },
1328
+ { id: 2, name: "Liam Reed", email: "liam.reed@twico.io", role: "Editor", department: "Design", status: "invited", country: "GB", seats: 4, mrr: 120, salary: 92000, startDate: "2023-05-02" },
1329
+ { id: 3, name: "Noah Park", email: "noah.park@twico.io", role: "Viewer", department: "Sales", status: "active", country: "DE", seats: 2, mrr: 60, salary: 74000, startDate: "2024-02-20" },
1330
+ { id: 4, name: "Mia Cruz", email: "mia.cruz@twico.io", role: "Editor", department: "Support", status: "suspended", country: "ID", seats: 7, mrr: 240, salary: 105000, startDate: "2022-11-09" },
1331
+ ];
1332
+
1333
+ const ROLES = ["Admin", "Editor", "Viewer"];
1334
+ const DEPARTMENTS = ["Engineering", "Design", "Sales", "Marketing", "Support", "Finance"];
1335
+ const STATUSES = ["active", "invited", "suspended"];
1336
+ const STATUS_TONE = { active: "success", invited: "info", suspended: "danger" };
1337
+ const usd = (n) => "$" + Number(n).toLocaleString("en-US");
1338
+
1339
+ // Inline SVG icons (no CDN) for the actions column + batch toolbar.
1340
+ const Eye = () => (<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" /><circle cx="12" cy="12" r="3" /></svg>);
1341
+ const Pencil = () => (<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M12 20h9" /><path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4z" /></svg>);
1342
+ const Trash = () => (<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M3 6h18M8 6V4h8v2M19 6l-1 14H6L5 6" /></svg>);
1343
+ const Download = () => (<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M12 3v12M7 10l5 5 5-5M5 21h14" /></svg>);
1344
+ const Mail = () => (<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M3 6h18v12H3z" /><path d="M3 7l9 6 9-6" /></svg>);
1345
+
1346
+ export default function Example() {
1347
+ const [rows, setRows] = useState(seed);
1348
+
1349
+ const columns = [
1350
+ {
1351
+ field: "name", headerName: "Name", width: 220, pinned: "left",
1352
+ renderCell: (v) => (
1353
+ <span style={{ display: "flex", alignItems: "center", gap: 10 }}>
1354
+ <Avatar name={v} size="sm" /> <strong>{v}</strong>
1355
+ </span>
1356
+ ),
1357
+ },
1358
+ { field: "email", headerName: "Email", width: 220 },
1359
+
1360
+ // Editable columns: double-click a cell to edit it, AND when rows are ticked an
1361
+ // "Edit" button appears in the selection toolbar to set one value across every
1362
+ // selected row at once (fires onBatchUpdate).
1363
+ {
1364
+ field: "role", headerName: "Role", width: 130,
1365
+ editable: true, editType: "select", valueOptions: ROLES,
1366
+ renderCell: (v) => <Badge variant="soft" size="sm" tone="neutral">{v}</Badge>,
1367
+ },
1368
+ { field: "department", headerName: "Department", width: 150, editable: true, editType: "select", valueOptions: DEPARTMENTS },
1369
+ {
1370
+ field: "status", headerName: "Status", width: 130,
1371
+ editable: true, editType: "select", valueOptions: STATUSES,
1372
+ renderCell: (v) => <Badge variant="soft" size="sm" tone={STATUS_TONE[v]}>{v}</Badge>,
1373
+ },
1374
+ { field: "country", headerName: "Country", width: 120, valueOptions: ["US", "GB", "DE", "ID", "JP", "BR", "CA", "AU"] },
1375
+ { field: "seats", headerName: "Seats", type: "number", width: 110, editable: true, aggregation: "sum" },
1376
+ { field: "mrr", headerName: "MRR", type: "number", width: 120, aggregation: "sum", valueFormatter: usd, aggregationFormatter: usd },
1377
+ { field: "salary", headerName: "Salary", type: "number", width: 130, editable: true, aggregation: "avg", valueFormatter: usd },
1378
+ { field: "startDate", headerName: "Start date", width: 130 },
1379
+
1380
+ // Per-row actions: inline icon buttons + an overflow (⋮) menu (showInMenu).
1381
+ {
1382
+ field: "actions", headerName: "Actions", type: "actions", width: 110,
1383
+ getActions: (row) => [
1384
+ { icon: <Eye />, label: "View", onClick: () => console.log("view", row.id) },
1385
+ { icon: <Pencil />, label: "Edit", showInMenu: true, onClick: () => console.log("edit", row.id) },
1386
+ { icon: <Trash />, label: "Delete", showInMenu: true, danger: true, onClick: () => setRows((d) => d.filter((r) => r.id !== row.id)) },
1387
+ ],
1388
+ },
1389
+ ];
1390
+
1391
+ // Actions for the toolbar that replaces the header when one or more rows are ticked.
1392
+ // (A built-in "Edit" button is added automatically because some columns are editable.)
1393
+ const batchActions = [
1394
+ { label: "Export", icon: <Download />, onClick: (keys, selected, clear) => clear() },
1395
+ { label: "Email", icon: <Mail />, onClick: (keys, selected, clear) => { console.log(selected); clear(); } },
1396
+ { label: "Delete", icon: <Trash />, danger: true, onClick: (keys, selected, clear) => { setRows((d) => d.filter((r) => !keys.includes(r.id))); clear(); } },
1397
+ ];
1398
+
1399
+ return (
1400
+ <Datatable
1401
+ columns={columns}
1402
+ rows={rows}
1403
+ checkboxSelection
1404
+ rowNumbers
1405
+ batchActions={batchActions}
1406
+ onRowUpdate={(updated) => setRows((d) => d.map((r) => (r.id === updated.id ? updated : r)))}
1407
+ onBatchUpdate={(changed) => setRows((d) => d.map((r) => changed.find((c) => c.id === r.id) || r))}
1408
+ showAggregation
1409
+ rowPinning
1410
+ pageSize={10}
1411
+ />
1412
+ );
1413
+ }
1414
+ ```
1415
+
1416
+ #### Kanban
1417
+
1418
+ Kanban is a drag-and-drop board that arranges cards into columns you can drag between. Use it for visualizing and reorganizing work items (tasks, tickets, stages) by status, either controlled via cards + onCardMove or uncontrolled.
1419
+
1420
+ `import { Kanban } from "twico-ui"`
1421
+
1422
+ Props:
1423
+ - `columns` — `KanbanColumn[] ({ id: string; title: React.ReactNode; color?: string })` · **required** — Defines the board columns, each with an id matching cards' column field, a title, and an optional heading dot color.
1424
+ - `cards` — `KanbanCard[] ({ id; column; title?; description?; tags?; footer?; ... })` · **required** — Supplies the cards to render, each assigned to a column via its column field and carrying title, description, and tags.
1425
+ - `onCardMove` — `(cardId: string, toColumn: string, nextCards: KanbanCard[]) => void` — Fires when a card is dropped into a different column, passing the card id, target column, and updated card list.
1426
+ - `renderCard` — `(card: KanbanCard) => React.ReactNode` — Overrides the default card contents with a custom renderer, falling back to title, description, tags, and footer.
1427
+ - `defaultCards` — `KanbanCard[]` · default `[]` — Initial cards when uncontrolled (the board applies moves internally).
1428
+
1429
+ ```jsx
1430
+ import { Kanban } from "twico-ui";
1431
+
1432
+ <Kanban
1433
+ columns={[
1434
+ { id: "todo", title: "To do", color: "var(--slate-400)" },
1435
+ { id: "doing", title: "In progress", color: "var(--amber-500)" },
1436
+ { id: "done", title: "Done", color: "var(--emerald-500)" },
1437
+ ]}
1438
+ cards={[
1439
+ { id: "1", column: "todo", title: "Design tokens", tags: ["Design"] },
1440
+ { id: "2", column: "doing", title: "Datatable filters" },
1441
+ { id: "3", column: "done", title: "Ship release" },
1442
+ ]}
1443
+ onCardMove={(id, to) => {}}
1444
+ />
1445
+ ```
1446
+
1447
+ #### List
1448
+
1449
+ List renders a vertical stack of rows with optional leading/trailing slots, a title, and a description. Use it for compact data displays like people, files, or settings, where rows can be static, links, or buttons.
1450
+
1451
+ `import { List } from "twico-ui"`
1452
+
1453
+ Props:
1454
+ - `items` — `ListItemData[]` · **required** — Provides the rows to render, each with a title plus optional description, leading, trailing, onClick, href, and active fields.
1455
+ - `plain` — `boolean` · default `false` — Drops the card chrome to render borderless, transparent rows when set to true; defaults to false for the boxed style.
1456
+
1457
+ ```jsx
1458
+ import { List } from "twico-ui";
1459
+
1460
+ <List
1461
+ items={[
1462
+ { title: "Ada Park", description: "ada@twico.dev", trailing: "Active", onClick: () => {} },
1463
+ { title: "report.pdf", description: "2.4 MB", trailing: "Today" },
1464
+ ]}
1465
+ />
1466
+ ```
1467
+
1468
+ #### Pagination
1469
+
1470
+ Pagination renders page navigation with prev/next buttons and smart ellipsis truncation, keeping boundary and sibling pages visible. Use it to let users move through paged data (tables, lists, search results), optionally with a "Go to" page jumper.
1471
+
1472
+ `import { Pagination } from "twico-ui"`
1473
+
1474
+ Props:
1475
+ - `page` — `number` · default `1` — The currently active 1-based page number, used to highlight the active button and compute which pages to show.
1476
+ - `total` — `number` · **required** — Total number of pages available, determining the range of page buttons and when ellipsis truncation appears.
1477
+ - `onChange` — `(page: number) => void` — Fires with the destination page number whenever the user clicks a page, prev, next, or jumper control.
1478
+ - `siblings` — `number` · default `1` — Number of adjacent page buttons rendered on each side of the current page before ellipsis truncation kicks in.
1479
+ - `boundaries` — `number` · default `1` — Number of page buttons always kept visible at each end so the first and last pages stay reachable.
1480
+ - `size` — `"sm" | "md"` · default `"md"` — Sets the control density, sm or md, scaling button height and spacing to match the surrounding layout.
1481
+ - `showJumper` — `boolean` · default `false` — Adds a "Go to" number input so users can jump directly to a specific page instead of clicking through.
1482
+ - `jumperLabel` — `React.ReactNode` · default `"Go to"` — Text or node rendered before the jumper input, defaulting to "Go to", to clarify what the field does.
1483
+ - `defaultPage` — `number` · default `1` — Initial 1-based page when uncontrolled (no `page` prop).
1484
+ - `showPageJumper` — `boolean` · default `false` — Show a "Go to" page input for jumping directly to a page; preferred alias for showJumper.
1485
+
1486
+ ```jsx
1487
+ import { Pagination } from "twico-ui";
1488
+
1489
+ const [page, setPage] = useState(1);
1490
+
1491
+ <Pagination
1492
+ page={page}
1493
+ total={12}
1494
+ onChange={setPage}
1495
+ showJumper
1496
+ />
1497
+ ```
1498
+
1499
+ #### Stat
1500
+
1501
+ Stat is a KPI / metric card that displays a label, a large value, and optional icon, trend delta, and help text. Use it on dashboards or summary panels to surface a single key metric with its trend direction.
1502
+
1503
+ `import { Stat } from "twico-ui"`
1504
+
1505
+ Props:
1506
+ - `label` — `React.ReactNode` · **required** — Renders the metric's caption shown above the value, naming what the number represents such as "Revenue".
1507
+ - `value` — `React.ReactNode` · **required** — Renders the large primary metric value that anchors the card, like a count, currency amount, or percentage.
1508
+ - `icon` — `React.ReactNode` — Renders an optional icon in a tinted tile at the top-right corner to visually reinforce the metric's meaning.
1509
+ - `delta` — `React.ReactNode` — Shows the trend change beside the value, such as "+12.5%" or "-3%", to convey movement over a period.
1510
+ - `deltaDirection` — `"up" | "down" | "flat"` — Forces the delta's direction and color (up, down, or flat); otherwise it is inferred from a leading minus sign.
1511
+ - `helpText` — `React.ReactNode` — Renders muted context after the delta, such as "vs last month", to clarify the comparison period.
1512
+ - `plain` — `boolean` · default `false` — Strips the card chrome (border, background, and padding) so the stat blends into an existing surface.
1513
+
1514
+ ```jsx
1515
+ import { Stat } from "twico-ui";
1516
+
1517
+ <Stat
1518
+ label="Revenue"
1519
+ value="$48,200"
1520
+ delta="+12.5%"
1521
+ helpText="vs last month"
1522
+ />
1523
+ <Stat
1524
+ label="Churn"
1525
+ value="2.1%"
1526
+ delta="-0.4%"
1527
+ deltaDirection="up"
1528
+ helpText="improved"
1529
+ />
1530
+ ```
1531
+
1532
+ #### Table
1533
+
1534
+ A data table with a styled header, hover and zebra-striped rows, client-side click-to-sort headers, custom cell renderers, and selected-row highlighting. Use it to display structured rows of data when you need lightweight sorting and per-column rendering or alignment.
1535
+
1536
+ `import { Table } from "twico-ui"`
1537
+
1538
+ Props:
1539
+ - `columns` — `TableColumn<T>[]` · **required** — Defines each column with key, header, optional align, width, sortable flag, and a custom render function for cell content.
1540
+ - `hover` — `boolean` · default `true` — Highlights the row under the cursor on mouse-over, on by default to aid scanning and pointing at dense rows.
1541
+ - `striped` — `boolean` · default `false` — Applies zebra striping to alternating rows when enabled, improving readability across wide tables of similar-looking data.
1542
+ - `size` — `"sm" | "md"` · default `"md"` — Controls cell padding density, choosing sm for compact rows or md for the default roomier spacing.
1543
+ - `sortable` — `boolean` · default `false` — Enables client-side click-to-sort headers when true, letting users reorder rows by any column marked sortable.
1544
+ - `rowKey` — `(row: T, index: number) => string | number` — Returns a stable unique key for each row from its data and index, defaulting to the row index.
1545
+ - `selectedKeys` — `Array<string | number>` — Lists the keys of rows to render with selected-row highlighting, useful for marking chosen or active records.
1546
+ - `sort` — `TableSort | null` — Controlled sort state — pair with onSortChange (null = unsorted).
1547
+ - `defaultSort` — `TableSort` · default `{ key: null, dir: "asc" }` — Initial sort when uncontrolled.
1548
+ - `onSortChange` — `(sort: TableSort) => void` — Fired when a sortable header is clicked with the next sort state.
1549
+ - `stickyHeader` — `boolean` · default `false` — Pin the header to the top while the body scrolls (use with maxHeight or an enclosing scroll container).
1550
+ - `maxHeight` — `number | string` — Cap the table height and give it its own vertical scroll area (CSS length or number of px); enables stickyHeader to shine.
1551
+ - `rows` — `T[]` — Row objects.
1552
+ - `field (column)` — `string` — Alias for key (Datatable vocabulary); key wins when both are set.
1553
+ - `headerName (column)` — `React.ReactNode` — Alias for header (Datatable vocabulary); header wins when both are set.
1554
+ - `renderCell (column)` — `(value: any, row: T, index: number) => React.ReactNode` — Alias for render (Datatable vocabulary); render wins when both are set.
1555
+
1556
+ ```jsx
1557
+ import { Table } from "twico-ui";
1558
+
1559
+ const users = [
1560
+ { id: 1, name: "Ava Stone", role: "Admin", status: "Active", mrr: 480 },
1561
+ { id: 2, name: "Liam Reed", role: "Editor", status: "Invited", mrr: 120 },
1562
+ { id: 3, name: "Noah Park", role: "Viewer", status: "Active", mrr: 60 },
1563
+ ];
1564
+
1565
+ <Table
1566
+ sortable
1567
+ striped
1568
+ rowKey={(r) => r.id}
1569
+ columns={[
1570
+ { field: "name", headerName: "Name" },
1571
+ { field: "role", headerName: "Role" },
1572
+ { field: "status", headerName: "Status" },
1573
+ { field: "mrr", headerName: "MRR", align: "right" },
1574
+ ]}
1575
+ rows={users}
1576
+ />
1577
+ ```
1578
+
1579
+ #### Tag
1580
+
1581
+ Tag is a removable chip for displaying filters, selected items, or keywords. Use it when you need compact, dismissible labels with an optional leading icon and an optional remove (×) button.
1582
+
1583
+ `import { Tag } from "twico-ui"`
1584
+
1585
+ Props:
1586
+ - `onRemove` — `(e: React.MouseEvent<HTMLButtonElement>) => void` — Renders the remove (×) button and handles its click, letting users dismiss the tag as a filter or selection.
1587
+ - `variant` — `"soft" | "solid" | "outline"` · default `"soft"` — Chooses the fill style between soft, solid, and outline to control how prominently the tag reads.
1588
+ - `tone` — `"neutral" | "primary" | "success" | "warning" | "danger" | "info"` · default `"neutral"` — Sets the semantic color from six options like success or danger; defaults to neutral (the classic chip).
1589
+ - `leftIcon` — `React.ReactNode` — Renders an optional leading icon before the label, useful for signaling the tag's category or status.
1590
+ - `children` — `React.ReactNode` — Provides the tag's label content, typically the filter name, selected item, or keyword being displayed.
1591
+
1592
+ ```jsx
1593
+ import { Tag } from "twico-ui";
1594
+
1595
+ <Tag onRemove={() => {}}>React</Tag>
1596
+ <Tag leftIcon={<span>#</span>}>design-system</Tag>
1597
+ <Tag>read-only</Tag>
1598
+ ```
1599
+
1600
+ #### Timeline
1601
+
1602
+ Timeline renders a vertical list of events as node dots connected by a rail, each showing a title, optional timestamp, and description. Use it for activity feeds, order tracking, or history views.
1603
+
1604
+ `import { Timeline } from "twico-ui"`
1605
+
1606
+ Props:
1607
+ - `items` — `TimelineItem[] — { title, time?, description?, icon?, tone? }` · **required** — Array of events to render as connected node dots, each with a title, optional time, description, icon, and primary/success/warning/danger tone.
1608
+ - `className` — `string` · default `""` — Extra CSS class appended to the root <ul> so you can layer custom spacing or styling onto the timeline.
1609
+ - `tone (TimelineItem)` — `"primary" | "info" | "success" | "warning" | "danger"` — Added `info` value to the per-item node tone union; maps the dot to var(--color-info) with var(--color-info-fg) icon color.
1610
+
1611
+ ```jsx
1612
+ import { Timeline } from "twico-ui";
1613
+
1614
+ <Timeline
1615
+ items={[
1616
+ { title: "Order placed", time: "9:41 AM", description: "We received your order.", tone: "primary" },
1617
+ { title: "Shipped", time: "2:10 PM", description: "Left the warehouse.", tone: "success" },
1618
+ { title: "Out for delivery", time: "Tomorrow" },
1619
+ ]}
1620
+ />
1621
+ ```
1622
+
1623
+ ### Navigation
1624
+
1625
+ #### Accordion
1626
+
1627
+ Accordion renders vertically stacked, expandable disclosure panels with a smooth open/close height animation. Use it to organize content like FAQs or settings into collapsible sections, optionally allowing several panels open at once.
1628
+
1629
+ `import { Accordion } from "twico-ui"`
1630
+
1631
+ Props:
1632
+ - `items` — `AccordionItem[]` · **required** — The array of disclosure panels to render, each an { value, label, content, icon? } object keyed by its unique value.
1633
+ - `multiple` — `boolean` · default `false` — When true, lets several panels stay open simultaneously instead of collapsing others as each new panel expands.
1634
+ - `defaultOpen` — `string[]` · default `[]` — The list of item values that should start expanded on first render, useful for surfacing key sections by default.
1635
+ - `open` — `string[]` — Controlled open panel values; pair with onOpenChange. When set, internal state is bypassed and defaultOpen is ignored.
1636
+ - `onOpenChange` — `(values: string[]) => void` — Called with the next open values whenever a panel trigger is toggled (fires in both controlled and uncontrolled modes).
1637
+
1638
+ ```jsx
1639
+ import { Accordion } from "twico-ui";
1640
+
1641
+ <Accordion
1642
+ multiple
1643
+ defaultOpen={["a"]}
1644
+ items={[
1645
+ { value: "a", label: "Is Twico UI free?", content: "Yes — MIT licensed, forever." },
1646
+ { value: "b", label: "Does it support dark mode?", content: "Out of the box." },
1647
+ ]}
1648
+ />
1649
+ ```
1650
+
1651
+ #### Breadcrumb
1652
+
1653
+ Breadcrumb renders a navigation trail to the current page, with optional per-item icons and a custom separator. Use it to show hierarchy and let users jump back up the path; it can collapse the middle of long trails into a "…" that expands on click.
1654
+
1655
+ `import { Breadcrumb } from "twico-ui"`
1656
+
1657
+ Props:
1658
+ - `items` — `BreadcrumbItem[]` · **required** — The ordered array of trail entries ({ label, href?, icon?, onClick? }), where the last item renders as the current, non-linked page.
1659
+ - `separator` — `React.ReactNode` · default `<ChevronSep />` — The node drawn between adjacent items, defaulting to a chevron but replaceable with any custom divider such as a slash.
1660
+ - `maxItems` — `number` · default `0` — Collapses the middle of the trail into an expandable ellipsis once the item count exceeds this number, with 0 disabling collapsing.
1661
+
1662
+ ```jsx
1663
+ import { Breadcrumb } from "twico-ui";
1664
+
1665
+ <Breadcrumb
1666
+ items={[
1667
+ { label: "Home", href: "/" },
1668
+ { label: "Projects", href: "/projects" },
1669
+ { label: "Twico UI" },
1670
+ ]}
1671
+ />
1672
+
1673
+ <Breadcrumb
1674
+ maxItems={3}
1675
+ items={[
1676
+ { label: "Home", href: "/" },
1677
+ { label: "Workspace", href: "/workspace" },
1678
+ { label: "Projects", href: "/workspace/projects" },
1679
+ { label: "Twico UI", href: "/workspace/projects/twico" },
1680
+ { label: "Settings" },
1681
+ ]}
1682
+ />
1683
+ ```
1684
+
1685
+ #### Navbar
1686
+
1687
+ Navbar is a top application bar with a left brand/logo slot, center inline navigation links (hidden on small screens), and a right-aligned actions slot. Use it as the primary site or app header; it is sticky and translucent by default.
1688
+
1689
+ `import { Navbar } from "twico-ui"`
1690
+
1691
+ Props:
1692
+ - `brand` — `React.ReactNode` — Renders the brand or logo node in the navbar's left slot, typically a wordmark or icon linking home.
1693
+ - `links` — `NavbarLink[]` · default `[]` — Inline navigation items shown centered, each with a label and optional href, icon, active state, or onClick.
1694
+ - `actions` — `React.ReactNode` — Right-aligned content such as buttons, a search field, or an avatar for account and primary actions.
1695
+ - `sticky` — `boolean` · default `true` — Pins the navbar to the top of the viewport with a translucent blur as the page scrolls; on by default.
1696
+ - `onMenuClick` — `() => void` — Fires when the hamburger button (shown only on small screens) is clicked, typically to open a mobile drawer.
1697
+
1698
+ ```jsx
1699
+ import { Navbar } from "twico-ui";
1700
+
1701
+ <Navbar
1702
+ brand={<>twico<span style={{ color: "var(--color-primary)" }}>UI</span></>}
1703
+ links={[
1704
+ { label: "Dashboard", active: true },
1705
+ { label: "Projects" },
1706
+ { label: "Team" },
1707
+ ]}
1708
+ actions={<button>Sign in</button>}
1709
+ />
1710
+ ```
1711
+
1712
+ #### Sidebar
1713
+
1714
+ Sidebar is a collapsible side-navigation panel with a brand header, grouped nav items (icons, labels, badges, or uppercase section headings), an optional footer, and a collapse toggle that switches to an icon-only rail. Use it as the primary app navigation in a fixed-height layout.
1715
+
1716
+ `import { Sidebar } from "twico-ui"`
1717
+
1718
+ Props:
1719
+ - `items` — `SidebarItem[]` · **required** — The navigation entries, each a link with label, icon, href, active, onClick, and badge, or a { section } heading.
1720
+ - `brand` — `React.ReactNode` — Brand or logo content shown in the header, with its text hidden when the sidebar collapses to the icon rail.
1721
+ - `footer` — `React.ReactNode` — Content pinned above the collapse toggle, such as a user row, anchored to the bottom of the panel.
1722
+ - `collapsed` — `boolean` — Controlled collapsed state, letting the parent toggle the sidebar between full width and the icon-only rail.
1723
+ - `defaultCollapsed` — `boolean` · default `false` — Initial collapsed state for uncontrolled usage, defaulting to false so the sidebar starts fully expanded.
1724
+ - `collapsible` — `boolean` · default `true` — Shows the collapse toggle button when true (the default), allowing users to switch to the icon-only rail.
1725
+ - `onCollapsedChange` — `(collapsed: boolean) => void` — Fires with the new boolean state whenever the collapse toggle is activated, useful for persisting the preference.
1726
+
1727
+ ```jsx
1728
+ import { Sidebar } from "twico-ui";
1729
+
1730
+ const HomeIcon = () => (<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M3 11l9-8 9 8M5 10v10h14V10" /></svg>);
1731
+ const MailIcon = () => (<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><rect x="3" y="5" width="18" height="14" rx="2" /><path d="m3 7 9 6 9-6" /></svg>);
1732
+ const CogIcon = () => (<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><circle cx="12" cy="12" r="3" /><path d="M12 3v3M12 18v3M3 12h3M18 12h3" /></svg>);
1733
+
1734
+ <Sidebar
1735
+ brand={<>twico<span style={{ color: "var(--color-primary)" }}>UI</span></>}
1736
+ items={[
1737
+ { section: "Main" },
1738
+ { label: "Dashboard", icon: <HomeIcon />, active: true },
1739
+ { label: "Inbox", icon: <MailIcon />, badge: 4 },
1740
+ { section: "Account" },
1741
+ { label: "Settings", icon: <CogIcon /> },
1742
+ ]}
1743
+ footer={<span style={{ fontSize: "var(--text-xs)", color: "var(--color-text-muted)" }}>v1.0.0</span>}
1744
+ defaultCollapsed={false}
1745
+ />
1746
+ ```
1747
+
1748
+ #### Stepper
1749
+
1750
+ Stepper is a multi-step progress indicator for wizards and checkout flows: steps before the active index show a check, the active step is ringed, and later steps are muted. Use it to show users where they are in a sequential, multi-step process, either horizontally or vertically.
1751
+
1752
+ `import { Stepper } from "twico-ui"`
1753
+
1754
+ Props:
1755
+ - `steps` — `Step[] ({ title, description?, icon?, error? })` · **required** — The ordered array of step objects to render, each with a title and optional description, icon, and error flag.
1756
+ - `tone` — `"primary" | "success" | "warning" | "danger" | "info" | "neutral"` · default `"primary"` — Sets the active/completed step marker and connector accent color from six semantic intents; defaults to primary.
1757
+ - `active` — `number` — Controlled zero-based index of the current step (pair with onStepClick); earlier steps render complete, later steps upcoming.
1758
+ - `defaultActive` — `number` · default `0` — Uncontrolled initial step index; the Stepper then advances itself when a clickable step is clicked.
1759
+ - `orientation` — `"horizontal" | "vertical"` · default `"horizontal"` — Lays the stepper out horizontally (default) or vertically to fit wide toolbars or narrow sidebars.
1760
+ - `clickable` — `boolean` · default `false` — Lets users click completed or active steps to navigate back, enabling jump-around movement through the flow.
1761
+ - `onStepClick` — `(index: number) => void` — Callback fired with the clicked step's index when steps are clickable, letting you drive navigation.
1762
+
1763
+ ```jsx
1764
+ import { Stepper } from "twico-ui";
1765
+
1766
+ const [active, setActive] = useState(2);
1767
+
1768
+ <Stepper
1769
+ active={1}
1770
+ steps={[
1771
+ { title: "Account", description: "Your details" },
1772
+ { title: "Payment", description: "Billing info" },
1773
+ { title: "Confirm" },
1774
+ ]}
1775
+ />
1776
+
1777
+ <Stepper
1778
+ orientation="vertical"
1779
+ active={active}
1780
+ clickable
1781
+ onStepClick={setActive}
1782
+ steps={[
1783
+ { title: "Account", description: "Your details" },
1784
+ { title: "Payment", description: "Billing info" },
1785
+ { title: "Confirm" },
1786
+ ]}
1787
+ />
1788
+ ```
1789
+
1790
+ #### Tabs
1791
+
1792
+ Tabbed navigation with a sliding active indicator, available as a line or pill variant. Use it to switch between related views or sections, either controlled (value + onChange) or uncontrolled (defaultValue).
1793
+
1794
+ `import { Tabs } from "twico-ui"`
1795
+
1796
+ Props:
1797
+ - `items` — `TabItem[]` · **required** — Defines the tabs to render, each carrying a value, label, and optional icon, count badge, and content.
1798
+ - `value` — `string` — Sets the active tab value for controlled usage, kept in sync by handling onChange yourself.
1799
+ - `defaultValue` — `string` · default `items[0]?.value` — Sets the initially active tab for uncontrolled usage, defaulting to the first item's value when omitted.
1800
+ - `onChange` — `(value: string) => void` — Fires with the newly selected tab value whenever the user switches tabs, used to drive controlled state.
1801
+ - `variant` — `"line" | "pill"` · default `"line"` — Selects the visual style, rendering either a line underline indicator or a pill-shaped background behind the active tab.
1802
+ - `tone` — `"primary" | "success" | "warning" | "danger" | "info" | "neutral"` · default `"primary"` — Sets the active-indicator and active-label accent color from six semantic intents; defaults to primary.
1803
+ - `orientation` — `"horizontal" | "vertical"` · default `"horizontal"` — Layout axis. Vertical stacks the tablist with the active indicator on its trailing edge and the panel beside it; ArrowUp/ArrowDown navigate between tabs (Home/End still jump to ends).
1804
+
1805
+ ```jsx
1806
+ import { Tabs } from "twico-ui";
1807
+
1808
+ <Tabs
1809
+ variant="line"
1810
+ defaultValue="overview"
1811
+ items={[
1812
+ { value: "overview", label: "Overview", content: <p>Overview panel</p> },
1813
+ { value: "activity", label: "Activity", count: 12, content: <p>Activity panel</p> },
1814
+ { value: "settings", label: "Settings", content: <p>Settings panel</p> },
1815
+ ]}
1816
+ />
1817
+ ```
1818
+
1819
+ #### TreeView
1820
+
1821
+ TreeView renders a hierarchical, expand/collapse tree with indentation, icons, badges, and a selectable row. Use it for file explorers and nested navigation where items have parent/child relationships.
1822
+
1823
+ `import { TreeView } from "twico-ui"`
1824
+
1825
+ Props:
1826
+ - `defaultExpanded` — `string[]` · default `[]` — Node ids that start expanded on first render, letting you reveal specific branches of the tree by default.
1827
+ - `defaultSelectedId` — `string | null` · default `null` — Selected node id on first render in uncontrolled mode; the tree then manages selection itself.
1828
+ - `selectedId` — `string | null` — Controlled id of the currently selected node; omit it to let the component manage selection internally on its own.
1829
+ - `onSelect` — `(id: string, node: TreeNode) => void` — Fired when a row is clicked, id first (the full node is the second argument).
1830
+ - `expanded` — `string[]` — Controlled expanded node ids (pair with `onExpandedChange`).
1831
+ - `onExpandedChange` — `(ids: string[]) => void` — Fired with the next expanded ids whenever a row expands or collapses.
1832
+ - `items` — `TreeNode[]` · default `[]` — Tree nodes.
1833
+
1834
+ ```jsx
1835
+ import { TreeView } from "twico-ui";
1836
+
1837
+ <TreeView
1838
+ defaultExpanded={["src"]}
1839
+ onSelect={(id) => open(id)}
1840
+ items={[
1841
+ { id: "src", label: "src", children: [
1842
+ { id: "app", label: "App.tsx" },
1843
+ { id: "comp", label: "components", children: [
1844
+ { id: "btn", label: "Button.tsx", badge: 3 },
1845
+ ]},
1846
+ ]},
1847
+ ]}
1848
+ />
1849
+ ```
1850
+
1851
+ ### Overlay
1852
+
1853
+ #### CommandPalette
1854
+
1855
+ A ⌘K-style command palette overlay with searchable, grouped commands and full keyboard navigation (↑/↓, Enter, Esc). Use it to give users a fast, central way to run actions or jump around the app, typically opened on Cmd/Ctrl+K.
1856
+
1857
+ `import { CommandPalette } from "twico-ui"`
1858
+
1859
+ Props:
1860
+ - `open` — `boolean` · **required** — Controls whether the palette overlay is visible; you own this state and typically toggle it on Cmd/Ctrl+K.
1861
+ - `onClose` — `() => void` — Fires when the palette should dismiss, such as on Esc, an overlay click, or after a command is selected.
1862
+ - `commands` — `Command[]` · **required** — The list of Command objects to search, group, and render, each typically defining a label, action, and optional group.
1863
+ - `placeholder` — `string` · default `"Type a command or search…"` — Sets the search input's placeholder text, defaulting to "Type a command or search…" to hint at the expected query.
1864
+ - `emptyText` — `string` · default `"No results found"` — The message shown when no command matches the current query, defaulting to "No results found".
1865
+ - `onClick` — `() => void` — Alias for onSelect, matching the other item-array APIs (Menu, List, Sidebar, ...) — used when onSelect is absent; palette closes after it runs.
1866
+
1867
+ ```jsx
1868
+ import { CommandPalette } from "twico-ui";
1869
+
1870
+ const [open, setOpen] = React.useState(false);
1871
+ React.useEffect(() => {
1872
+ const h = (e) => {
1873
+ if ((e.metaKey || e.ctrlKey) && e.key === "k") { e.preventDefault(); setOpen(true); }
1874
+ };
1875
+ window.addEventListener("keydown", h);
1876
+ return () => window.removeEventListener("keydown", h);
1877
+ }, []);
1878
+
1879
+ <CommandPalette
1880
+ open={open}
1881
+ onClose={() => setOpen(false)}
1882
+ commands={[
1883
+ { group: "Navigation", label: "Go to Dashboard", shortcut: "G D", onSelect: () => {} },
1884
+ { group: "Actions", label: "New project", keywords: "create add", onSelect: () => {} },
1885
+ ]}
1886
+ />
1887
+ ```
1888
+
1889
+ #### Dialog
1890
+
1891
+ Dialog is a modal overlay with a scrim, pop-in animation, focus trapping, Esc-to-close, and backdrop-close. Use it to interrupt the user for confirmations, forms, or detail views that require attention before continuing.
1892
+
1893
+ `import { Dialog } from "twico-ui"`
1894
+
1895
+ Props:
1896
+ - `open` — `boolean` · **required** — Controls whether the modal is visible; the dialog renders null when false, so drive it from state.
1897
+ - `onClose` — `() => void` — Called when the dialog requests to close via Esc, a backdrop click, or the close button.
1898
+ - `title` — `React.ReactNode` — Heading content rendered in the dialog header to announce the modal's purpose to the user.
1899
+ - `description` — `React.ReactNode` — Secondary supporting text shown beneath the title to give context for the dialog's content or task.
1900
+ - `footer` — `React.ReactNode` — Footer region for pinned actions, typically right-aligned confirm and cancel buttons below the body.
1901
+ - `size` — `"sm" | "md" | "lg" | "full"` · default `"md"` — Adds a new "full" option: a near-fullscreen panel that fills the viewport minus a small margin (existing sm/md/lg unchanged).
1902
+ - `closeOnBackdrop` — `boolean` · default `true` — Closes the dialog when the surrounding scrim is clicked, enabled by default; disable for required interactions.
1903
+ - `children` — `React.ReactNode` — Main body content of the dialog, such as forms, messages, or detail views requiring the user's attention.
1904
+ - `scrollBody` — `boolean` · default `false` — Keep the header and footer fixed and scroll only the body, so long content does not overflow the viewport.
1905
+
1906
+ ```jsx
1907
+ import { Dialog, Button } from "twico-ui";
1908
+
1909
+ const [open, setOpen] = useState(false);
1910
+
1911
+ <Button tone="danger" onClick={() => setOpen(true)}>Delete project</Button>
1912
+
1913
+ <Dialog
1914
+ open={open}
1915
+ onClose={() => setOpen(false)}
1916
+ title="Delete project?"
1917
+ description="This action cannot be undone."
1918
+ footer={
1919
+ <>
1920
+ <Button variant="ghost" onClick={() => setOpen(false)}>Cancel</Button>
1921
+ <Button tone="danger" onClick={() => setOpen(false)}>Delete</Button>
1922
+ </>
1923
+ }
1924
+ >
1925
+ The project and all its data will be permanently removed.
1926
+ </Dialog>
1927
+ ```
1928
+
1929
+ #### Drawer
1930
+
1931
+ Drawer is a slide-in overlay panel that anchors to any screen edge with a scrim, Esc-to-close, focus trapping, and a header/body/footer layout. Use it for nav menus, filters, detail views, or carts that should appear over the current page; drive it with the open and onClose props.
1932
+
1933
+ `import { Drawer } from "twico-ui"`
1934
+
1935
+ Props:
1936
+ - `open` — `boolean` · **required** — Controls whether the panel is visible; the drawer renders nothing when false, so drive it from state.
1937
+ - `onClose` — `() => void` — Called when the drawer requests to close via Esc, a backdrop click, or the close button.
1938
+ - `side` — `"left" | "right" | "top" | "bottom"` · default `"right"` — Screen edge the panel anchors to and slides in from, defaulting to right (left, right, top, or bottom).
1939
+ - `title` — `React.ReactNode` — Heading content rendered in the drawer header to announce the panel's purpose to the user.
1940
+ - `description` — `React.ReactNode` — Secondary supporting text shown under the title to give context for the drawer's content or task.
1941
+ - `footer` — `React.ReactNode` — Footer region for pinned actions, typically right-aligned buttons displayed below the drawer body.
1942
+ - `closeOnBackdrop` — `boolean` · default `true` — Closes the drawer when the backdrop scrim is clicked, enabled by default; disable for required interactions.
1943
+ - `children` — `React.ReactNode` — Body content of the drawer, such as nav menus, filters, detail views, or a cart over the page.
1944
+ - `width` — `"sm" | "md" | "lg" | number | string` · default `"md"` — Panel width for side left/right — preset sm/md/lg, a number (px), or a CSS length.
1945
+ - `height` — `"sm" | "md" | "lg" | number | string` · default `"md"` — Panel height for side top/bottom — preset sm/md/lg, a number (px), or a CSS length.
1946
+
1947
+ ```jsx
1948
+ import { Drawer } from "twico-ui";
1949
+
1950
+ const [open, setOpen] = useState(false);
1951
+
1952
+ <Drawer
1953
+ open={open}
1954
+ onClose={() => setOpen(false)}
1955
+ side="right"
1956
+ title="Filters"
1957
+ description="Narrow down the results"
1958
+ footer={<button onClick={() => setOpen(false)}>Apply</button>}
1959
+ >
1960
+ ...filter controls...
1961
+ </Drawer>
1962
+ ```
1963
+
1964
+ #### Menu
1965
+
1966
+ Menu is a portaled dropdown menu that takes a clickable trigger and an array of items supporting icons, shortcuts, separators, section headings, and danger styling. Use it for contextual action lists (e.g. an account or "more options" menu) that need to stay keyboard-navigable and never get clipped by overflow.
1967
+
1968
+ `import { Menu } from "twico-ui"`
1969
+
1970
+ Props:
1971
+ - `trigger` — `React.ReactNode` · **required** — Defines the clickable element that opens the menu, typically a Button or IconButton the dropdown anchors to.
1972
+ - `items` — `MenuItemDef[]` · **required** — Lists the menu entries, where each can be an action item, a separator, or a section heading.
1973
+ - `align` — `"start" | "end"` · default `"start"` — Aligns the menu horizontally to the trigger's start or end edge so it sits flush in tight layouts; defaults to start.
1974
+ - `header` — `React.ReactNode` — Renders an optional rich node above the items, handy for surfacing user info or context at the top of the menu.
1975
+ - `width` — `number` — Pins the menu to a fixed pixel width, otherwise it sizes to the larger of 200px and the trigger's width.
1976
+ - `defaultOpen` — `boolean` · default `false` — Open on first render in uncontrolled mode; the Menu then tracks its own open state.
1977
+ - `open` — `boolean` — Controlled open state - pair with onOpenChange. Omit for internal (uncontrolled) state.
1978
+ - `onOpenChange` — `(open: boolean) => void` — Called with the requested open state on trigger click, item select, Esc, or outside click.
1979
+
1980
+ ```jsx
1981
+ import { Menu, Button } from "twico-ui";
1982
+
1983
+ <Menu
1984
+ align="end"
1985
+ trigger={<Button>Options</Button>}
1986
+ items={[
1987
+ { label: "Account", heading: true },
1988
+ { label: "Profile", onClick: () => {}, shortcut: "⌘P" },
1989
+ { label: "Settings", onClick: () => {} },
1990
+ { separator: true },
1991
+ { label: "Sign out", danger: true, onClick: () => {} },
1992
+ ]}
1993
+ />
1994
+ ```
1995
+
1996
+ #### Popover
1997
+
1998
+ Popover is a click-triggered floating panel anchored to a trigger element, rendered in a portal so it is never clipped, auto-flipping near viewport edges and closing on outside-click or Esc. Use it for rich interactive content (inputs, buttons, links); for plain text hints use Tooltip instead.
1999
+
2000
+ `import { Popover } from "twico-ui"`
2001
+
2002
+ Props:
2003
+ - `trigger` — `React.ReactNode` · **required** — The clickable element the panel anchors to; clicking it toggles the popover open and closed.
2004
+ - `title` — `React.ReactNode` — Optional bold heading rendered at the top of the panel to introduce its interactive content.
2005
+ - `placement` — `"top" | "bottom" | "left" | "right"` · default `"bottom"` — Which side of the trigger the panel opens toward (top, bottom, left, or right), auto-flipping near viewport edges.
2006
+ - `align` — `"start" | "center" | "end"` · default `"center"` — Cross-axis alignment (start, center, or end) of the panel relative to the trigger for top and bottom placements.
2007
+ - `width` — `number` · default `240` — Fixed width of the floating panel in pixels, controlling how much interactive content fits on each line.
2008
+ - `children` — `React.ReactNode` — The rich interactive content rendered inside the panel, such as inputs, buttons, or links.
2009
+ - `defaultOpen` — `boolean` · default `false` — Open on first render in uncontrolled mode; the Popover then tracks its own open state.
2010
+ - `open` — `boolean` — Controlled open state - pair with onOpenChange. Omit for internal (uncontrolled) state.
2011
+ - `onOpenChange` — `(open: boolean) => void` — Called with the requested open state on trigger click, Esc, or outside click.
2012
+
2013
+ ```jsx
2014
+ import { Popover, Button, Input } from "twico-ui";
2015
+
2016
+ <Popover
2017
+ trigger={<Button variant="outline">Share</Button>}
2018
+ title="Share link"
2019
+ placement="bottom"
2020
+ >
2021
+ <Input defaultValue="https://twico.dev/x" />
2022
+ <Button fullWidth>Copy link</Button>
2023
+ </Popover>
2024
+ ```
2025
+
2026
+ #### Tooltip
2027
+
2028
+ Tooltip is a small hover/focus overlay that wraps a single trigger element and shows a label on interaction. Use it to surface short, supplementary hints (like icon-button descriptions) without cluttering the UI.
2029
+
2030
+ `import { Tooltip } from "twico-ui"`
2031
+
2032
+ Props:
2033
+ - `label` — `React.ReactNode` · **required** — The short hint text or content revealed on hover or focus, ideal for describing icon buttons without cluttering the layout.
2034
+ - `placement` — `"top" | "bottom" | "left" | "right"` · default `"top"` — Chooses which side of the trigger the tooltip appears on, one of top, bottom, left, or right (default top).
2035
+ - `delay` — `number` · default `120` — Open delay in milliseconds before the tooltip appears (default 120), preventing flicker as the pointer passes over triggers.
2036
+ - `children` — `React.ReactNode` · **required** — The single trigger element the tooltip wraps and attaches its hover and focus listeners to.
2037
+
2038
+ ```jsx
2039
+ import { Tooltip } from "twico-ui";
2040
+
2041
+ <Tooltip label="Copy to clipboard" placement="top">
2042
+ <button aria-label="Copy">Copy</button>
2043
+ </Tooltip>
2044
+ ```
2045
+
2046
+ ### Feedback
2047
+
2048
+ #### Alert
2049
+
2050
+ Alert is an inline message banner with four semantic tones (info, success, warning, danger), an optional bold title, a default or custom icon, and an optional dismiss button. Use it to surface contextual feedback inline within a page, such as success confirmations, warnings, or errors.
2051
+
2052
+ `import { Alert } from "twico-ui"`
2053
+
2054
+ Props:
2055
+ - `tone` — `"info" | "success" | "warning" | "danger" | "primary" | "neutral"` · default `"info"` — Sets the semantic color from six intents (info, success, warning, danger, primary, neutral); defaults to info.
2056
+ - `variant` — `"soft" | "solid" | "outline"` · default `"soft"` — Chooses the fill style between soft (tinted), solid (filled), and outline; defaults to soft.
2057
+ - `title` — `React.ReactNode` — Renders a bold heading line above the body, useful for summarizing the alert before its supporting detail.
2058
+ - `icon` — `React.ReactNode` — Overrides the default tone icon with your own node when you need a more specific or branded visual cue.
2059
+ - `onClose` — `() => void` — When provided, shows a dismiss button and runs this callback so the alert can be closed by the user.
2060
+ - `children` — `React.ReactNode` — The main body content of the alert, typically the descriptive message shown beneath the optional title.
2061
+
2062
+ ```jsx
2063
+ import { Alert } from "twico-ui";
2064
+
2065
+ <Alert tone="success" title="Saved" onClose={() => {}}>
2066
+ Your changes are live.
2067
+ </Alert>
2068
+
2069
+ <Alert tone="warning">
2070
+ Your trial ends in 3 days.
2071
+ </Alert>
2072
+ ```
2073
+
2074
+ #### EmptyState
2075
+
2076
+ EmptyState is a zero-data placeholder that centers an optional icon tile, title, description, and an actions row for a call-to-action. Use it when a list, table, inbox, or panel has no content yet, or as a dashed drop-target placeholder.
2077
+
2078
+ `import { EmptyState } from "twico-ui"`
2079
+
2080
+ Props:
2081
+ - `icon` — `React.ReactNode` — Renders an illustrative icon inside a tinted tile above the title, signalling visually why the area has no content yet.
2082
+ - `title` — `React.ReactNode` — Sets the centered headline summarising the empty condition, such as "No results" or "Your inbox is empty".
2083
+ - `description` — `React.ReactNode` — Provides supporting copy beneath the title that explains the empty state and hints at how to fill it.
2084
+ - `actions` — `React.ReactNode` — Renders a row of call-to-action buttons below the text so users can immediately add content or take the next step.
2085
+ - `bordered` — `boolean` · default `false` — Adds a dashed border around the placeholder when true, ideal for drop targets or visually distinct empty regions.
2086
+ - `border` — `boolean` · default `false` — Add a dashed border (good for drop targets / placeholders); preferred alias for bordered.
2087
+
2088
+ ```jsx
2089
+ import { EmptyState, Button } from "twico-ui";
2090
+
2091
+ <EmptyState
2092
+ icon={<span style={{ fontSize: 28 }}>📭</span>}
2093
+ title="No messages yet"
2094
+ description="When someone sends you a message, it'll show up here."
2095
+ actions={<Button>Compose</Button>}
2096
+ />
2097
+ ```
2098
+
2099
+ #### Progress
2100
+
2101
+ Progress is a linear progress bar that can be determinate (driven by a value) or indeterminate, with semantic tones. Use it to show task completion, upload/download progress, or ongoing background work.
2102
+
2103
+ `import { Progress } from "twico-ui"`
2104
+
2105
+ Props:
2106
+ - `value` — `number` · default `0` — Current progress amount that drives how much of the bar is filled, interpreted relative to max.
2107
+ - `max` — `number` · default `100` — Upper bound the bar represents, so value divided by max determines the filled percentage.
2108
+ - `tone` — `"primary" | "info" | "success" | "warning" | "danger"` · default `"primary"` — Added `info` value to the existing tone union; maps the bar fill to var(--color-info).
2109
+ - `size` — `"sm" | "md" | "lg"` · default `"md"` — Thickness of the bar, sm, md, or lg, to match the visual weight of the surrounding content.
2110
+ - `indeterminate` — `boolean` · default `false` — Switches to a continuously animated bar that ignores value, signaling work of unknown duration.
2111
+ - `showLabel` — `boolean` · default `false` — Displays a percentage label above the bar so users can read the exact completion at a glance.
2112
+
2113
+ ```jsx
2114
+ import { Progress } from "twico-ui";
2115
+
2116
+ <Progress value={64} showLabel />
2117
+ <Progress value={90} tone="success" />
2118
+ <Progress value={45} tone="warning" size="lg" />
2119
+ <Progress indeterminate />
2120
+ ```
2121
+
2122
+ #### Skeleton
2123
+
2124
+ Skeleton is a shimmering placeholder shown while content loads. Use it to reserve layout space and signal loading for text, avatars, or image/card blocks before real data arrives.
2125
+
2126
+ `import { Skeleton } from "twico-ui"`
2127
+
2128
+ Props:
2129
+ - `variant` — `"text" | "circle" | "rect"` · default `"text"` — Chooses the placeholder shape: text for lines, circle for avatars, or rect for image and card blocks, defaulting to text.
2130
+ - `width` — `string | number` — Sets the placeholder width as a CSS string or number, such as "120px" or 60, to match expected content.
2131
+ - `height` — `string | number` — Sets the placeholder height as a CSS string or number to reserve the vertical space the real content will occupy.
2132
+ - `lines` — `number` · default `1` — For the text variant, the number of lines to render, defaulting to 1, with the last line shortened for realism.
2133
+
2134
+ ```jsx
2135
+ import { Skeleton } from "twico-ui";
2136
+
2137
+ <Skeleton variant="circle" width={40} height={40} />
2138
+ <Skeleton variant="text" lines={3} />
2139
+ <Skeleton variant="rect" height={120} />
2140
+ ```
2141
+
2142
+ #### Spinner
2143
+
2144
+ An indeterminate loading spinner that signals an ongoing background operation. Use it when content or an action is loading and there's no measurable progress to show, such as inside buttons or over panels.
2145
+
2146
+ `import { Spinner } from "twico-ui"`
2147
+
2148
+ Props:
2149
+ - `size` — `"sm" | "md" | "lg" | "xl"` · default `"md"` — Sets the spinner diameter (sm, md, lg, or xl) so it fits its context, from inside a button to over a panel.
2150
+ - `tone` — `"current" | "primary" | "success" | "warning" | "danger" | "info" | "neutral" | "white"` · default `"current"` — Deprecated alias for color.
2151
+ - `label` — `string` · default `"Loading"` — Accessible name exposed via aria-label so screen readers announce the loading state, defaulting to "Loading".
2152
+ - `color` — `"current" | "primary" | "success" | "warning" | "danger" | "info" | "neutral" | "white"` · default `"current"` — Sets the spinner color; defaults to currentColor so it adapts inside buttons and themed contexts.
2153
+
2154
+ ```jsx
2155
+ import { Spinner } from "twico-ui";
2156
+
2157
+ <Spinner />
2158
+ <Spinner size="lg" />
2159
+ <Spinner tone="white" /> {/* on a colored button/background */}
2160
+ ```
2161
+
2162
+ #### Toast
2163
+
2164
+ Toast is a transient notification card with a tone accent, optional title/icon, and a dismiss button. Use it to surface brief feedback (success, warning, error, info) by managing a list of toasts in state and rendering them inside a fixed-position ToastViewport.
2165
+
2166
+ `import { Toast, ToastViewport } from "twico-ui"`
2167
+
2168
+ Props:
2169
+ - `tone` — `"default" | "neutral" | "success" | "warning" | "danger" | "info"` · default `"default"` — "neutral" is now accepted as an alias for "default" (aligns with Badge's tone vocabulary). No new standalone props were added.
2170
+ - `title` — `React.ReactNode` — Bold heading text shown above the body content to summarize what the toast is reporting at a glance.
2171
+ - `icon` — `React.ReactNode` — Custom leading icon that overrides the default tone icon when you want a more specific visual marker.
2172
+ - `onClose` — `() => void` — Callback invoked on dismissal; providing it also reveals the close button so users can manually remove the toast.
2173
+ - `duration` — `number` · default `4500` — Milliseconds before the toast auto-dismisses and calls onClose (default 4500); the countdown pauses on hover or focus, and 0 or Infinity keeps it open.
2174
+ - `children` — `React.ReactNode` — Body or description content rendered beneath the title to give users the detailed message of the notification.
2175
+ - `limit (ToastViewport)` — `number` — Caps how many toasts the viewport shows at once, keeping the most recent and dropping older ones so bursts never flood the screen.
2176
+
2177
+ ```jsx
2178
+ import { Toast, ToastViewport } from "twico-ui";
2179
+
2180
+ const [toasts, setToasts] = useState([]);
2181
+ const push = (t) => setToasts((s) => [...s, { id: Date.now(), ...t }]);
2182
+ const remove = (id) => setToasts((s) => s.filter((t) => t.id !== id));
2183
+
2184
+ {/* limit caps what's shown; each toast auto-dismisses after ~4.5s */}
2185
+ <ToastViewport limit={4}>
2186
+ {toasts.map((t) => (
2187
+ <Toast
2188
+ key={t.id}
2189
+ tone={t.tone}
2190
+ title={t.title}
2191
+ onClose={() => remove(t.id)}
2192
+ >
2193
+ {t.body}
2194
+ </Toast>
2195
+ ))}
2196
+ </ToastViewport>
2197
+ ```
2198
+
2199
+ #### ToastProvider
2200
+
2201
+ ToastProvider drops a ready-made toast manager into your tree: it owns the toast list and renders a single ToastViewport, so you no longer hand-manage an array in state. Anywhere beneath it, call const { toast } = useToast() and fire toast.success("Saved") or toast.error({ title, description }); each toast auto-dismisses on the shared timer.
2202
+
2203
+ `import { ToastProvider, useToast } from "twico-ui"`
2204
+
2205
+ Props:
2206
+ - `children` — `React.ReactNode` — Your app — anything under the provider can call useToast().
2207
+ - `limit` — `number` · default `4` — Maximum toasts shown at once (the most recent are kept).
2208
+ - `duration` — `number` · default `4500` — Default auto-dismiss duration (ms) for toasts that don't set their own.
2209
+ - `toast() (useToast)` — `(opts) => number` — Push a toast; has tone helpers toast.success/warning/danger/error/info(title, options?).
2210
+ - `dismiss / clear (useToast)` — `(id) => void / () => void` — Remove one toast by id, or clear them all.
2211
+
2212
+ ```jsx
2213
+ import { ToastProvider, useToast, Button } from "twico-ui";
2214
+
2215
+ function App() {
2216
+ return (
2217
+ <ToastProvider limit={4}>
2218
+ <Page />
2219
+ </ToastProvider>
2220
+ );
2221
+ }
2222
+
2223
+ function Page() {
2224
+ const { toast } = useToast();
2225
+ return (
2226
+ <Button onClick={() => toast.success("Saved")}>Save</Button>
2227
+ );
2228
+ }
2229
+ ```
2230
+
2231
+ ## Hooks (23)
2232
+
2233
+ ### useIsomorphicLayoutEffect
2234
+
2235
+ SSR-safe `useLayoutEffect` (falls back to `useEffect` on the server).
2236
+
2237
+ ```ts
2238
+ export const useIsomorphicLayoutEffect: typeof React.useLayoutEffect;
2239
+ ```
2240
+
2241
+ ### useMounted
2242
+
2243
+ `true` once the component has mounted on the client — for SSR-safe rendering.
2244
+
2245
+ ```ts
2246
+ export function useMounted(): boolean;
2247
+ ```
2248
+
2249
+ ### usePrevious
2250
+
2251
+ The value from the previous render.
2252
+
2253
+ ```ts
2254
+ export function usePrevious<T>(value: T): T | undefined;
2255
+ ```
2256
+
2257
+ ### useToggle
2258
+
2259
+ Boolean state with a stable toggle. `toggle()` flips; `toggle(true)` sets.
2260
+
2261
+ ```ts
2262
+ export function useToggle( initial?: boolean ): [boolean, (next?: boolean) => void, React.Dispatch<React.SetStateAction<boolean>>];
2263
+ ```
2264
+
2265
+ ### useDisclosure
2266
+
2267
+ Open/close state for dialogs, drawers, menus, popovers.
2268
+
2269
+ ```ts
2270
+ export function useDisclosure(initial?: boolean): { open: boolean; onOpen: () => void; onClose: () => void; onToggle: () => void; setOpen: React.Dispatch<React.SetStateAction<boolean>>; };
2271
+ ```
2272
+
2273
+ ### useControllableState
2274
+
2275
+ Controlled/uncontrolled state — the pattern every form component needs.
2276
+
2277
+ ```ts
2278
+ export function useControllableState<T>(options?: { value?: T; defaultValue?: T; onChange?: (value: T) => void; }): [T, (next: T | ((prev: T) => T)) => void];
2279
+ ```
2280
+
2281
+ ### useMediaQuery
2282
+
2283
+ Reactively match a media query (SSR-safe).
2284
+
2285
+ ```ts
2286
+ export function useMediaQuery(query: string): boolean;
2287
+ ```
2288
+
2289
+ ### usePrefersReducedMotion
2290
+
2291
+ `true` when the user requests reduced motion.
2292
+
2293
+ ```ts
2294
+ export function usePrefersReducedMotion(): boolean;
2295
+ ```
2296
+
2297
+ ### useColorScheme
2298
+
2299
+ Light/dark theme state synced to `<html>` and persisted to localStorage.
2300
+
2301
+ ```ts
2302
+ export function useColorScheme(options?: UseColorSchemeOptions): { theme: "light" | "dark"; setTheme: (theme: "light" | "dark") => void; toggle: () => void; isDark: boolean; };
2303
+ ```
2304
+
2305
+ ### useEventListener
2306
+
2307
+ Attach an event listener (window by default, or a ref/element) with cleanup.
2308
+
2309
+ ```ts
2310
+ export function useEventListener( eventName: string, handler: (event: any) => void, element?: React.RefObject<any> | EventTarget | null, options?: boolean | AddEventListenerOptions ): void;
2311
+ ```
2312
+
2313
+ ### useClickOutside
2314
+
2315
+ Call `handler` when a pointer/touch event lands outside `ref`.
2316
+
2317
+ ```ts
2318
+ export function useClickOutside<T extends HTMLElement = HTMLElement>( ref: React.RefObject<T>, handler: (event: Event) => void, events?: string[] ): void;
2319
+ ```
2320
+
2321
+ ### useKeyPress
2322
+
2323
+ Run `handler` on a keydown matching `key`, with optional modifier requirements.
2324
+
2325
+ ```ts
2326
+ export function useKeyPress( key: string | string[], handler: (event: KeyboardEvent) => void, options?: UseKeyPressOptions ): void;
2327
+ ```
2328
+
2329
+ ### useLocalStorage
2330
+
2331
+ State persisted to localStorage (JSON-serialized), SSR-safe.
2332
+
2333
+ ```ts
2334
+ export function useLocalStorage<T>( key: string, initialValue: T ): [T, (value: T | ((prev: T) => T)) => void];
2335
+ ```
2336
+
2337
+ ### useCopyToClipboard
2338
+
2339
+ Copy text to the clipboard with a `copied` flag that auto-resets.
2340
+
2341
+ ```ts
2342
+ export function useCopyToClipboard(timeout?: number): { copied: boolean; copy: (text: string) => Promise<boolean>; };
2343
+ ```
2344
+
2345
+ ### useDebouncedValue
2346
+
2347
+ Debounce a value — updates only after `delay` ms of no change.
2348
+
2349
+ ```ts
2350
+ export function useDebouncedValue<T>(value: T, delay?: number): T;
2351
+ ```
2352
+
2353
+ ### useDebouncedCallback
2354
+
2355
+ A debounced version of a callback.
2356
+
2357
+ ```ts
2358
+ export function useDebouncedCallback<A extends any[]>( callback: (...args: A) => void, delay?: number ): (...args: A) => void;
2359
+ ```
2360
+
2361
+ ### useInterval
2362
+
2363
+ Run `callback` every `delay` ms; pass `delay = null` to pause.
2364
+
2365
+ ```ts
2366
+ export function useInterval(callback: () => void, delay: number | null): void;
2367
+ ```
2368
+
2369
+ ### useTimeout
2370
+
2371
+ Run `callback` once after `delay` ms; pass `delay = null` to cancel.
2372
+
2373
+ ```ts
2374
+ export function useTimeout(callback: () => void, delay: number | null): void;
2375
+ ```
2376
+
2377
+ ### useWindowSize
2378
+
2379
+ The current window dimensions (0×0 on the server).
2380
+
2381
+ ```ts
2382
+ export function useWindowSize(): { width: number; height: number };
2383
+ ```
2384
+
2385
+ ### useHover
2386
+
2387
+ `true` while the pointer is over the element referenced by `ref`.
2388
+
2389
+ ```ts
2390
+ export function useHover<T extends HTMLElement = HTMLElement>(ref: React.RefObject<T>): boolean;
2391
+ ```
2392
+
2393
+ ### useIntersectionObserver
2394
+
2395
+ The latest IntersectionObserver entry for the element referenced by `ref`.
2396
+
2397
+ ```ts
2398
+ export function useIntersectionObserver<T extends HTMLElement = HTMLElement>( ref: React.RefObject<T>, options?: IntersectionObserverInit ): IntersectionObserverEntry | null;
2399
+ ```
2400
+
2401
+ ### useScrollLock
2402
+
2403
+ Lock body scroll while `locked` is true — for modals/drawers.
2404
+
2405
+ ```ts
2406
+ export function useScrollLock(locked?: boolean): void;
2407
+ ```
2408
+
2409
+ ### useId
2410
+
2411
+ A stable, SSR-safe unique id (optionally prefixed).
2412
+
2413
+ ```ts
2414
+ export function useId(prefix?: string): string;
2415
+ ```
2416
+
2417
+ ---
2418
+
2419
+ _Generated by `scripts/gen-llms.mjs` from twico-ui v1.0.2. Do not edit by hand — run `npm run gen:llms`. Source of truth: the component `.d.ts` files and `hooks/index.d.ts`._
2420
+