@foi/design-system 0.0.18 → 0.0.20

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.
Files changed (114) hide show
  1. package/README.md +665 -77
  2. package/dist/{DatePicker.utils-ytCEcs6T.js → DatePicker.utils-D6aKFBuv.js} +2 -2
  3. package/dist/{DatePicker.utils-ytCEcs6T.js.map → DatePicker.utils-D6aKFBuv.js.map} +1 -1
  4. package/dist/Toast-k4dxCveh.js +1366 -0
  5. package/dist/Toast-k4dxCveh.js.map +1 -0
  6. package/dist/components/atoms/Button/Button.interface.d.ts +1 -1
  7. package/dist/components/atoms/Checkbox/Checkbox.interface.d.ts +3 -3
  8. package/dist/components/atoms/Chip/Chip.d.ts +4 -0
  9. package/dist/components/atoms/Chip/Chip.emotion.d.ts +2 -0
  10. package/dist/components/atoms/Chip/Chip.interface.d.ts +14 -0
  11. package/dist/components/atoms/Chip/index.d.ts +5 -0
  12. package/dist/components/atoms/DatePicker/DatePicker.interface.d.ts +2 -2
  13. package/dist/components/atoms/DatePicker/DatePickerMenu/DatePickerMenu.interface.d.ts +3 -3
  14. package/dist/components/atoms/IconButton/IconButton.interface.d.ts +2 -2
  15. package/dist/components/atoms/NumberField/NumberField.interface.d.ts +2 -2
  16. package/dist/components/atoms/Radio/Radio.interface.d.ts +2 -2
  17. package/dist/components/atoms/Select/Select.interface.d.ts +2 -2
  18. package/dist/components/atoms/Select/SelectMenu/SelectMenu.interface.d.ts +1 -1
  19. package/dist/components/atoms/Slider/Slider.interface.d.ts +4 -4
  20. package/dist/components/atoms/Switch/Switch.interface.d.ts +1 -1
  21. package/dist/components/atoms/TextField/TextField.interface.d.ts +2 -2
  22. package/dist/components/molecules/CheckboxGroup/CheckboxGroup.interface.d.ts +2 -2
  23. package/dist/components/molecules/CheckboxTree/CheckboxTree.interface.d.ts +2 -2
  24. package/dist/components/molecules/Modal/Modal.interface.d.ts +4 -4
  25. package/dist/components/molecules/RadioGroup/RadioGroup.interface.d.ts +2 -2
  26. package/dist/components/molecules/Toast/Toast.context.d.ts +2 -0
  27. package/dist/components/molecules/Toast/Toast.d.ts +4 -0
  28. package/dist/components/molecules/Toast/Toast.emotion.d.ts +4 -0
  29. package/dist/components/molecules/Toast/Toast.hook.d.ts +1 -0
  30. package/dist/components/molecules/Toast/Toast.interface.d.ts +33 -0
  31. package/dist/components/molecules/Toast/Toast.provider.d.ts +3 -0
  32. package/dist/components/molecules/Toast/index.d.ts +3 -0
  33. package/dist/components/organisms/DataGrid/DataGrid.interface.d.ts +3 -3
  34. package/dist/components/organisms/DataGrid/DataGridMenu/DataGridMenu.interface.d.ts +1 -1
  35. package/dist/hocs/ThemeProvider/ThemeProvider.interface.d.ts +6 -0
  36. package/dist/hocs/ThemeProvider/components/{Button.d.ts → atoms/Button.d.ts} +1 -1
  37. package/dist/hocs/ThemeProvider/components/{Checkbox.d.ts → atoms/Checkbox.d.ts} +1 -1
  38. package/dist/hocs/ThemeProvider/components/atoms/Chip.d.ts +13 -0
  39. package/dist/hocs/ThemeProvider/components/{DatePicker.d.ts → atoms/DatePicker.d.ts} +1 -1
  40. package/dist/hocs/ThemeProvider/components/{DatePickerMenu.d.ts → atoms/DatePickerMenu.d.ts} +1 -1
  41. package/dist/hocs/ThemeProvider/components/{IconButton.d.ts → atoms/IconButton.d.ts} +1 -1
  42. package/dist/hocs/ThemeProvider/components/{NumberField.d.ts → atoms/NumberField.d.ts} +1 -1
  43. package/dist/hocs/ThemeProvider/components/{Pagination.d.ts → atoms/Pagination.d.ts} +1 -1
  44. package/dist/hocs/ThemeProvider/components/{PaginationMenu.d.ts → atoms/PaginationMenu.d.ts} +1 -1
  45. package/dist/hocs/ThemeProvider/components/{Radio.d.ts → atoms/Radio.d.ts} +1 -1
  46. package/dist/hocs/ThemeProvider/components/{Select.d.ts → atoms/Select.d.ts} +1 -1
  47. package/dist/hocs/ThemeProvider/components/{SelectMenu.d.ts → atoms/SelectMenu.d.ts} +1 -1
  48. package/dist/hocs/ThemeProvider/components/{Slider.d.ts → atoms/Slider.d.ts} +1 -1
  49. package/dist/hocs/ThemeProvider/components/{Switch.d.ts → atoms/Switch.d.ts} +1 -1
  50. package/dist/hocs/ThemeProvider/components/{TextField.d.ts → atoms/TextField.d.ts} +1 -1
  51. package/dist/hocs/ThemeProvider/components/index.d.ts +23 -17
  52. package/dist/hocs/ThemeProvider/components/{CheckboxGroup.d.ts → molecules/CheckboxGroup.d.ts} +1 -1
  53. package/dist/hocs/ThemeProvider/components/{CheckboxTree.d.ts → molecules/CheckboxTree.d.ts} +1 -1
  54. package/dist/hocs/ThemeProvider/components/{Modal.d.ts → molecules/Modal.d.ts} +1 -1
  55. package/dist/hocs/ThemeProvider/components/{RadioGroup.d.ts → molecules/RadioGroup.d.ts} +1 -1
  56. package/dist/hocs/ThemeProvider/components/molecules/Toast.d.ts +16 -0
  57. package/dist/hocs/ThemeProvider/components/{DataGrid.d.ts → organisms/DataGrid.d.ts} +1 -1
  58. package/dist/hocs/ThemeProvider/components/{DataGridMenu.d.ts → organisms/DataGridMenu.d.ts} +1 -1
  59. package/dist/hocs/ThemeProvider/interfaces/Components.interface.d.ts +2 -0
  60. package/dist/hooks.d.ts +2 -0
  61. package/dist/hooks.mjs +2 -2
  62. package/dist/index.d.ts +14 -11
  63. package/dist/index.mjs +5596 -509
  64. package/dist/index.mjs.map +1 -1
  65. package/dist/theme/dark/components/atoms/Chip.d.ts +13 -0
  66. package/dist/theme/dark/components/index.d.ts +287 -263
  67. package/dist/theme/dark/components/molecules/Toast.d.ts +17 -0
  68. package/dist/theme/dark/index.d.ts +287 -263
  69. package/dist/theme/index.d.ts +287 -263
  70. package/dist/{theme-CLkxVsoE.js → theme-BjrtNRDQ.js} +230 -218
  71. package/dist/theme-BjrtNRDQ.js.map +1 -0
  72. package/dist/theme.d.ts +22 -0
  73. package/dist/theme.mjs +1 -1
  74. package/dist/utilities.mjs +1 -1
  75. package/package.json +1 -13
  76. package/dist/RadioGroup-D_R-nwCD.js +0 -456
  77. package/dist/RadioGroup-D_R-nwCD.js.map +0 -1
  78. package/dist/RadioGroup.context-CdFGi5z1.js +0 -601
  79. package/dist/RadioGroup.context-CdFGi5z1.js.map +0 -1
  80. package/dist/Switch-DK5hEXUS.js +0 -4196
  81. package/dist/Switch-DK5hEXUS.js.map +0 -1
  82. package/dist/ThemeProvider-BlqXHfU_.js +0 -39
  83. package/dist/ThemeProvider-BlqXHfU_.js.map +0 -1
  84. package/dist/atoms.d.ts +0 -21
  85. package/dist/atoms.mjs +0 -3
  86. package/dist/emotion-react-jsx-runtime.browser.esm-Ct_bZ5JG.js +0 -995
  87. package/dist/emotion-react-jsx-runtime.browser.esm-Ct_bZ5JG.js.map +0 -1
  88. package/dist/hocs.d.ts +0 -2
  89. package/dist/hocs.mjs +0 -2
  90. package/dist/molecules.d.ts +0 -8
  91. package/dist/molecules.mjs +0 -2
  92. package/dist/theme-CLkxVsoE.js.map +0 -1
  93. package/dist/useStateCallback-B4O93zzK.js +0 -32
  94. package/dist/useStateCallback-B4O93zzK.js.map +0 -1
  95. /package/dist/theme/dark/components/{Button.d.ts → atoms/Button.d.ts} +0 -0
  96. /package/dist/theme/dark/components/{Checkbox.d.ts → atoms/Checkbox.d.ts} +0 -0
  97. /package/dist/theme/dark/components/{DatePicker.d.ts → atoms/DatePicker.d.ts} +0 -0
  98. /package/dist/theme/dark/components/{DatePickerMenu.d.ts → atoms/DatePickerMenu.d.ts} +0 -0
  99. /package/dist/theme/dark/components/{IconButton.d.ts → atoms/IconButton.d.ts} +0 -0
  100. /package/dist/theme/dark/components/{NumberField.d.ts → atoms/NumberField.d.ts} +0 -0
  101. /package/dist/theme/dark/components/{Pagination.d.ts → atoms/Pagination.d.ts} +0 -0
  102. /package/dist/theme/dark/components/{PaginationMenu.d.ts → atoms/PaginationMenu.d.ts} +0 -0
  103. /package/dist/theme/dark/components/{Radio.d.ts → atoms/Radio.d.ts} +0 -0
  104. /package/dist/theme/dark/components/{Select.d.ts → atoms/Select.d.ts} +0 -0
  105. /package/dist/theme/dark/components/{SelectMenu.d.ts → atoms/SelectMenu.d.ts} +0 -0
  106. /package/dist/theme/dark/components/{Slider.d.ts → atoms/Slider.d.ts} +0 -0
  107. /package/dist/theme/dark/components/{Switch.d.ts → atoms/Switch.d.ts} +0 -0
  108. /package/dist/theme/dark/components/{TextField.d.ts → atoms/TextField.d.ts} +0 -0
  109. /package/dist/theme/dark/components/{CheckboxGroup.d.ts → molecules/CheckboxGroup.d.ts} +0 -0
  110. /package/dist/theme/dark/components/{CheckboxTree.d.ts → molecules/CheckboxTree.d.ts} +0 -0
  111. /package/dist/theme/dark/components/{Modal.d.ts → molecules/Modal.d.ts} +0 -0
  112. /package/dist/theme/dark/components/{RadioGroup.d.ts → molecules/RadioGroup.d.ts} +0 -0
  113. /package/dist/theme/dark/components/{DataGrid.d.ts → organisms/DataGrid.d.ts} +0 -0
  114. /package/dist/theme/dark/components/{DataGridMenu.d.ts → organisms/DataGridMenu.d.ts} +0 -0
package/README.md CHANGED
@@ -46,12 +46,13 @@ npm publish --access public
46
46
  | ------------- | ------------------------------------------------------------------------------------------ |
47
47
  | `Button` | Primary action button with variant/size/color support |
48
48
  | `Checkbox` | Single checkbox — standalone, RHF, or inside a group |
49
+ | `Chip` | Compact inline label for statuses and tags — themed variants, accepts text or ReactNode |
49
50
  | `DatePicker` | Date input with calendar menu, integrates with RHF |
50
51
  | `Icon` | Font-based icon using Material Symbols Rounded — pass a `name` prop |
51
- | `IconButton` | Clickable icon with button semantics |
52
+ | `IconButton` | Clickable icon with button semantics |
52
53
  | `NumberField` | Numeric input with increment/decrement buttons, min/max/step support, and two layout modes |
53
54
  | `Pagination` | Page navigation bar with first/prev/next/last buttons and optional rows-per-page selector |
54
- | `Radio` | Single radio button — used inside `RadioGroup` |
55
+ | `Radio` | Single radio button — used inside `RadioGroup` |
55
56
  | `Select` | Dropdown select with RHF integration |
56
57
  | `Skeleton` | Animated placeholder shown while content is loading — rectangular or circular |
57
58
  | `Slider` | Range slider with RHF integration |
@@ -60,17 +61,18 @@ npm publish --access public
60
61
 
61
62
  ### Molecules
62
63
 
63
- | Component | Description |
64
- | --------------- | -------------------------------------------------------------- |
65
- | `CheckboxGroup` | Manages a set of `Checkbox` children as a `string[]` RHF field |
66
- | `CheckboxTree` | Hierarchical checkbox group with parent/child selection logic |
64
+ | Component | Description |
65
+ | --------------- | --------------------------------------------------------------------------------- |
66
+ | `CheckboxGroup` | Manages a set of `Checkbox` children as a `string[]` RHF field |
67
+ | `CheckboxTree` | Hierarchical checkbox group with parent/child selection logic |
67
68
  | `Modal` | Overlay dialog rendered via React portal — closes on ×, backdrop click, or Escape |
68
- | `RadioGroup` | Manages a set of `Radio` children as a single RHF field |
69
+ | `RadioGroup` | Manages a set of `Radio` children as a single RHF field |
70
+ | `Toast` | Notification system — push transient messages from anywhere via `useToast` |
69
71
 
70
72
  ### Organisms
71
73
 
72
- | Component | Description |
73
- | ---------- | -------------------------------------------------------------------------------------------------------- |
74
+ | Component | Description |
75
+ | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
74
76
  | `DataGrid` | Async data table with server-side sorting, filtering (search / multiselect), pagination or infinite scroll, skeleton loading state, and custom action columns via `render` |
75
77
 
76
78
  ## Usage with React Hook Form
@@ -81,9 +83,7 @@ All field components share the same integration pattern. Define a Zod schema, pa
81
83
  import { useForm } from 'react-hook-form';
82
84
  import { zodResolver } from '@hookform/resolvers/zod';
83
85
  import { z } from 'zod';
84
- import TextField from '@components/atoms/TextField';
85
- import NumberField from '@components/atoms/NumberField';
86
- import Button from '@components/atoms/Button';
86
+ import { TextField, NumberField, Button } from '@foi/design-system';
87
87
 
88
88
  const schema = z.object({
89
89
  email: z.string().email('Invalid email'),
@@ -139,31 +139,30 @@ A text input field with a floating label, helper text, and full error state inte
139
139
 
140
140
  ### Props
141
141
 
142
- | Prop | Type | Default | Description |
143
- | --------------- | ----------------------------------------- | ---------- | ---------------------------------------------------- |
144
- | `name` | `string` | — | Field name, used by RHF |
145
- | `label` | `string` | — | Floating label text |
146
- | `control` | `Control` | — | RHF control object; omit for uncontrolled mode |
147
- | `value` | `string` | — | Value in uncontrolled mode |
148
- | `onValueChange` | `(value: string) => void` | — | Change handler in uncontrolled mode |
149
- | `type` | `'text' \| 'password' \| 'email'` | `'text'` | HTML input type |
150
- | `width` | `'small' \| 'medium' \| 'large' \| 'full'` | — | Preset width |
151
- | `helperText` | `string` | — | Hint text shown below the field when there is no error |
152
- | `showErrorText` | `boolean` | `true` | Show the validation error message below the field |
153
- | `hasPadding` | `boolean` | `false` | Add bottom padding to reserve space for helper/error text |
154
- | `startAdornment`| `JSX.Element` | — | Element rendered to the left of the input |
155
- | `endAdornment` | `JSX.Element` | — | Element rendered to the right of the input |
156
- | `regex` | `RegExp` | — | Block keystrokes that don't match the pattern |
157
- | `format` | `(value: string) => string` | — | Transform the value before it is stored |
158
- | `disabled` | `boolean` | `false` | Disable the field |
159
- | `theme` | `EVENTS` | — | One-off token override for this instance |
160
- | `variant` | `string` | `'default'`| Theme variant name |
142
+ | Prop | Type | Default | Description |
143
+ | ---------------- | ------------------------------------------ | ----------- | --------------------------------------------------------- |
144
+ | `name` | `string` | — | Field name, used by RHF |
145
+ | `label` | `string` | — | Floating label text |
146
+ | `control` | `Control` | — | RHF control object; omit for uncontrolled mode |
147
+ | `value` | `string` | — | Value in uncontrolled mode |
148
+ | `onValueChange` | `(value: string) => void` | — | Change handler in uncontrolled mode |
149
+ | `type` | `'text' \| 'password' \| 'email'` | `'text'` | HTML input type |
150
+ | `width` | `'small' \| 'medium' \| 'large' \| 'full'` | — | Preset width |
151
+ | `helperText` | `string` | — | Hint text shown below the field when there is no error |
152
+ | `showErrorText` | `boolean` | `true` | Show the validation error message below the field |
153
+ | `hasPadding` | `boolean` | `false` | Add bottom padding to reserve space for helper/error text |
154
+ | `startAdornment` | `JSX.Element` | — | Element rendered to the left of the input |
155
+ | `endAdornment` | `JSX.Element` | — | Element rendered to the right of the input |
156
+ | `regex` | `RegExp` | — | Block keystrokes that don't match the pattern |
157
+ | `format` | `(value: string) => string` | — | Transform the value before it is stored |
158
+ | `disabled` | `boolean` | `false` | Disable the field |
159
+ | `theme` | `EVENTS` | — | One-off token override for this instance |
160
+ | `variant` | `string` | `'default'` | Theme variant name |
161
161
 
162
162
  ### Example
163
163
 
164
164
  ```tsx
165
- import TextField from '@components/atoms/TextField';
166
- import Icon from '@components/atoms/Icon';
165
+ import { TextField, Icon } from '@foi/design-system';
167
166
 
168
167
  <TextField
169
168
  name='email'
@@ -172,7 +171,7 @@ import Icon from '@components/atoms/Icon';
172
171
  type='email'
173
172
  startAdornment={<Icon name='mail' />}
174
173
  helperText='We will never share your email'
175
- />
174
+ />;
176
175
  ```
177
176
 
178
177
  ## NumberField
@@ -183,12 +182,12 @@ A numeric input that stores a `number` in RHF (or `undefined` when empty). The f
183
182
 
184
183
  All `TextField` props apply except `type`, `endAdornment`, and `startAdornment` (in `spinner` mode). The following props are specific to `NumberField`:
185
184
 
186
- | Prop | Type | Default | Description |
187
- | --------- | ----------------------------- | ------------ | ------------------------------------------------------------------------ |
188
- | `min` | `number` | — | Minimum allowed value; decrement button disables at this bound |
189
- | `max` | `number` | — | Maximum allowed value; increment button disables at this bound |
190
- | `step` | `number` | `1` | Amount added or subtracted on each button click |
191
- | `display` | `'standard' \| 'spinner'` | `'standard'` | `standard`: both buttons on the right; `spinner`: remove left, input centre, add right |
185
+ | Prop | Type | Default | Description |
186
+ | --------- | ------------------------- | ------------ | -------------------------------------------------------------------------------------- |
187
+ | `min` | `number` | — | Minimum allowed value; decrement button disables at this bound |
188
+ | `max` | `number` | — | Maximum allowed value; increment button disables at this bound |
189
+ | `step` | `number` | `1` | Amount added or subtracted on each button click |
190
+ | `display` | `'standard' \| 'spinner'` | `'standard'` | `standard`: both buttons on the right; `spinner`: remove left, input centre, add right |
192
191
 
193
192
  ### Display modes
194
193
 
@@ -207,7 +206,7 @@ All `TextField` props apply except `type`, `endAdornment`, and `startAdornment`
207
206
  ### Example
208
207
 
209
208
  ```tsx
210
- import NumberField from '@components/atoms/NumberField';
209
+ import { NumberField } from '@foi/design-system';
211
210
 
212
211
  // Standard — quantity picker with bounds
213
212
  <NumberField
@@ -235,8 +234,8 @@ import NumberField from '@components/atoms/NumberField';
235
234
  Wrap your app in `ThemeProvider` and pass one or more named themes. The active theme is selected by the `theme` prop and its tokens are injected as CSS custom properties that every component consumes.
236
235
 
237
236
  ```tsx
238
- import ThemeProvider from '@hocs/ThemeProvider';
239
- import darkTheme from './themes/dark';
237
+ import { ThemeProvider } from '@foi/design-system';
238
+ import { darkTheme } from '@foi/design-system/theme';
240
239
 
241
240
  <ThemeProvider themes={[darkTheme]} theme='dark'>
242
241
  <App />
@@ -249,11 +248,35 @@ A theme is a plain object with a `name`, an optional `fonts` map, and a `compone
249
248
 
250
249
  Each variant contains:
251
250
 
252
- - `ROOT` — static styles applied unconditionally (e.g. `border-radius`)
253
- - `EVENTS` — styles scoped to interaction states (`ENABLED`, `HOVER`, `FOCUS`, `ERROR`, `DISABLED`, etc.)
251
+ - `ROOT` — static styles applied unconditionally (e.g. `border-radius`, `background-color`)
252
+ - `EVENTS` — styles scoped to interaction states. The complete set of possible states is:
253
+
254
+ | State | Description |
255
+ | ---------------- | ------------------------------------------------------------------------------- |
256
+ | `ENABLED` | Default resting state — element is interactive and fully visible |
257
+ | `VALUE` | The filled or selected part of the element (e.g. track fill, typed text colour) |
258
+ | `HOVER` | While the pointer is over the element |
259
+ | `ACTIVE` | While the element is being pressed |
260
+ | `FOCUS` | When the element has keyboard focus |
261
+ | `ERROR` | Container styles when validation fails |
262
+ | `ERROR_VALUE` | Value part when in error state |
263
+ | `ERROR_HOVER` | Hover while in error state |
264
+ | `ERROR_ACTIVE` | Press while in error state |
265
+ | `ERROR_FOCUS` | Focus while in error state |
266
+ | `DISABLED` | When the element is not interactive |
267
+ | `DISABLED_VALUE` | Value part when disabled |
268
+ | `LOADING` | During a pending async operation |
269
+ | `LOADING_VALUE` | Value part during loading |
270
+
271
+ Not every component uses all states — each component defines only the states relevant to its interaction model. The TypeScript type exported from the component's theme file tells you exactly which states are available:
272
+
273
+ ```ts
274
+ import type { BUTTON } from '@foi/design-system/theme';
275
+ // BUTTON includes the EVENTS type — autocomplete shows only the states Button supports
276
+ ```
254
277
 
255
278
  ```ts
256
- import type { BUTTON } from '@hocs/ThemeProvider/components/Button';
279
+ import type { BUTTON } from '@foi/design-system/theme';
257
280
 
258
281
  const component = {
259
282
  BUTTON: {
@@ -321,15 +344,19 @@ An async data table that delegates all data operations to the server via `onFetc
321
344
  ### Defining columns
322
345
 
323
346
  ```tsx
324
- import DataGrid from '@components/organisms/DataGrid';
325
- import type { DataGridColumn } from '@components/organisms/DataGrid';
347
+ import { DataGrid } from '@foi/design-system';
348
+ import type { DataGridColumn } from '@foi/design-system';
326
349
 
327
- interface Product { id: number; name: string; price: number; }
350
+ interface Product {
351
+ id: number;
352
+ name: string;
353
+ price: number;
354
+ }
328
355
 
329
356
  const columns: DataGridColumn<Product>[] = [
330
357
  {
331
358
  key: 'name',
332
- label: 'Product', // string → wrapped in the themed --DATAGRID-thLabel span
359
+ label: 'Product', // string → wrapped in the themed --DATAGRID-thLabel span
333
360
  type: 'text',
334
361
  filter: { type: 'search' },
335
362
  },
@@ -345,7 +372,7 @@ const columns: DataGridColumn<Product>[] = [
345
372
  `label` accepts any `ReactNode`. A plain string is automatically wrapped in the themed `--DATAGRID-thLabel` span so it inherits the header typography. Pass a custom node to render icons, badges, or anything else without that wrapper:
346
373
 
347
374
  ```tsx
348
- import Icon from '@components/atoms/Icon';
375
+ import { Icon } from '@foi/design-system';
349
376
 
350
377
  {
351
378
  key: 'price',
@@ -369,7 +396,7 @@ const onFetch = async ({ page, pageSize, sort, filters }) => {
369
396
  return { data: response.items, total: response.total };
370
397
  };
371
398
 
372
- <DataGrid<Product> columns={columns} onFetch={onFetch} />
399
+ <DataGrid<Product> columns={columns} onFetch={onFetch} />;
373
400
  ```
374
401
 
375
402
  ### Action columns
@@ -398,11 +425,11 @@ const columnsWithActions: DataGridColumn<Product>[] = [
398
425
 
399
426
  ### Pagination modes
400
427
 
401
- | Prop | Value | Behaviour |
402
- | ----------------- | -------------- | ---------------------------------------------- |
428
+ | Prop | Value | Behaviour |
429
+ | ----------------- | -------------- | ------------------------------------------------------ |
403
430
  | `paginationType` | `'pagination'` | Renders a `<Pagination>` bar below the table (default) |
404
- | `paginationType` | `'scroll'` | Appends rows as the user scrolls down |
405
- | `pageSizeOptions` | `number[]` | Shows a rows-per-page selector in the bar |
431
+ | `paginationType` | `'scroll'` | Appends rows as the user scrolls down |
432
+ | `pageSizeOptions` | `number[]` | Shows a rows-per-page selector in the bar |
406
433
 
407
434
  ## Modal
408
435
 
@@ -411,9 +438,7 @@ A portal-based overlay dialog. Renders into `document.body` so it sits above all
411
438
  ### Basic usage
412
439
 
413
440
  ```tsx
414
- import Modal from '@components/molecules/Modal';
415
- import Button from '@components/atoms/Button';
416
- import Icon from '@components/atoms/Icon';
441
+ import { Modal, Button, Icon } from '@foi/design-system';
417
442
 
418
443
  const [open, setOpen] = useState(false);
419
444
 
@@ -428,38 +453,601 @@ const [open, setOpen] = useState(false);
428
453
  }
429
454
  footer={
430
455
  <>
431
- <Button onClick={() => setOpen(false)} variant='ghost'>Cancelar</Button>
456
+ <Button onClick={() => setOpen(false)} variant='ghost'>
457
+ Cancelar
458
+ </Button>
432
459
  <Button onClick={() => setOpen(false)}>Aceptar</Button>
433
460
  </>
434
461
  }
435
462
  >
436
463
  Contenido del modal.
437
- </Modal>
464
+ </Modal>;
438
465
  ```
439
466
 
440
467
  Apply `--MODAL-title` to the title element inside `header` to receive the themed font and colour styles.
441
468
 
442
469
  ### Props
443
470
 
444
- | Prop | Type | Default | Description |
445
- | ---------------- | ------------- | ------- | ------------------------------------------------- |
446
- | `open` | `boolean` | — | Controls visibility |
447
- | `onClose` | `() => void` | — | Called when the modal requests to close |
448
- | `header` | `ReactNode` | — | Full header bar content (rendered before ×) |
449
- | `showCloseButton`| `boolean` | `true` | Whether to render the × icon button |
450
- | `size` | `'md' \| 'lg'`| `'md'` | `'md'` = 480 px, `'lg'` = 720 px |
451
- | `children` | `ReactNode` | — | Main body content |
452
- | `footer` | `ReactNode` | — | Footer row content (typically action buttons) |
453
- | `staticBackdrop` | `boolean` | `false` | When `true`, backdrop click and Escape do nothing |
454
- | `className` | `string` | — | Extra CSS class on the modal panel |
471
+ | Prop | Type | Default | Description |
472
+ | ----------------- | -------------- | ------- | ------------------------------------------------- |
473
+ | `open` | `boolean` | — | Controls visibility |
474
+ | `onClose` | `() => void` | — | Called when the modal requests to close |
475
+ | `header` | `ReactNode` | — | Full header bar content (rendered before ×) |
476
+ | `showCloseButton` | `boolean` | `true` | Whether to render the × icon button |
477
+ | `size` | `'md' \| 'lg'` | `'md'` | `'md'` = 480 px, `'lg'` = 720 px |
478
+ | `children` | `ReactNode` | — | Main body content |
479
+ | `footer` | `ReactNode` | — | Footer row content (typically action buttons) |
480
+ | `staticBackdrop` | `boolean` | `false` | When `true`, backdrop click and Escape do nothing |
481
+ | `className` | `string` | — | Extra CSS class on the modal panel |
455
482
 
456
483
  ### Close triggers
457
484
 
458
- | Trigger | `staticBackdrop=false` | `staticBackdrop=true` |
459
- | ---------------- | ---------------------- | --------------------- |
460
- | × button | ✓ | ✓ |
461
- | Backdrop click | ✓ | — |
462
- | Escape key | ✓ | — |
485
+ | Trigger | `staticBackdrop=false` | `staticBackdrop=true` |
486
+ | -------------- | ---------------------- | --------------------- |
487
+ | × button | ✓ | ✓ |
488
+ | Backdrop click | ✓ | — |
489
+ | Escape key | ✓ | — |
490
+
491
+ ## Button
492
+
493
+ The primary interactive element. Accepts any `ReactNode` as children and natively supports all standard `<button>` HTML attributes.
494
+
495
+ ### Props
496
+
497
+ | Prop | Type | Default | Description |
498
+ | ----------- | ---------------------- | ----------- | ----------------------------------------------- |
499
+ | `children` | `ReactNode` | — | Button label or content |
500
+ | `type` | `'button' \| 'submit'` | `'button'` | HTML button type |
501
+ | `iconStart` | `JSX.Element` | — | Icon rendered before the label |
502
+ | `iconEnd` | `JSX.Element` | — | Icon rendered after the label |
503
+ | `loading` | `boolean` | `false` | Shows a loading indicator; button stays mounted |
504
+ | `disabled` | `boolean` | `false` | Prevents interaction |
505
+ | `className` | `string` | — | Extra CSS class |
506
+ | `variant` | `string` | `'default'` | Theme variant |
507
+ | `theme` | `EVENTS` | — | One-off token override |
508
+
509
+ ### Example
510
+
511
+ ```tsx
512
+ import { Button, Icon } from '@foi/design-system';
513
+
514
+ <Button onClick={handleSave}>Guardar</Button>
515
+ <Button type='submit' iconStart={<Icon name='send' />}>Enviar</Button>
516
+ <Button loading={isSaving} disabled={isSaving}>Guardando…</Button>
517
+ <Button variant='danger' onClick={handleDelete}>Eliminar</Button>
518
+ ```
519
+
520
+ ## Icon
521
+
522
+ Renders a single icon from the [Material Symbols Rounded](https://fonts.google.com/icons) font. The font is loaded automatically by `ThemeProvider`.
523
+
524
+ ### Props
525
+
526
+ | Prop | Type | Default | Description |
527
+ | ----------- | ------------------------------ | ------- | ---------------------------------- |
528
+ | `name` | `string` | — | Material Symbols ligature name |
529
+ | `fill` | `boolean` | `false` | Use the filled variant of the icon |
530
+ | `size` | `'sm' \| 'md' \| 'lg' \| 'xl'` | `'md'` | Icon size |
531
+ | `className` | `string` | — | Extra CSS class |
532
+ | `style` | `CSSProperties` | — | Inline styles |
533
+
534
+ ### Example
535
+
536
+ ```tsx
537
+ import { Icon } from '@foi/design-system';
538
+
539
+ <Icon name='home' />
540
+ <Icon name='search' size='lg' />
541
+ <Icon name='favorite' fill />
542
+ ```
543
+
544
+ ## IconButton
545
+
546
+ A button that renders a single icon with no visible text. Accepts all standard `<button>` HTML attributes.
547
+
548
+ ### Props
549
+
550
+ | Prop | Type | Default | Description |
551
+ | ----------- | ------------- | ----------- | ------------------------------------------------- |
552
+ | `icon` | `JSX.Element` | — | Icon element to render (use `<Icon />`) |
553
+ | `onClick` | `function` | — | Click handler |
554
+ | `isFlipped` | `boolean` | `false` | Mirrors the icon horizontally (useful for arrows) |
555
+ | `disabled` | `boolean` | `false` | Prevents interaction |
556
+ | `className` | `string` | — | Extra CSS class |
557
+ | `variant` | `string` | `'default'` | Theme variant |
558
+ | `theme` | `EVENTS` | — | One-off token override |
559
+
560
+ ### Example
561
+
562
+ ```tsx
563
+ import { IconButton, Icon } from '@foi/design-system';
564
+
565
+ <IconButton icon={<Icon name='close' />} onClick={() => setOpen(false)} />
566
+ <IconButton icon={<Icon name='arrow_forward' />} isFlipped onClick={goBack} />
567
+ ```
568
+
569
+ ## Checkbox
570
+
571
+ A single checkbox. Can be used standalone (controlled or RHF), or as a child of `CheckboxGroup` / `CheckboxTree`.
572
+
573
+ ### Props
574
+
575
+ | Prop | Type | Default | Description |
576
+ | --------------- | ---------------------------- | ----------- | ------------------------------------------------ |
577
+ | `name` | `string` | — | RHF field name (required in standalone RHF mode) |
578
+ | `control` | `Control` | — | RHF control object |
579
+ | `checked` | `boolean` | — | Controlled mode value (use with `onChecked`) |
580
+ | `onChecked` | `(checked: boolean) => void` | — | Controlled mode change handler |
581
+ | `label` | `string` | — | Text label next to the checkbox |
582
+ | `icon` | `JSX.Element` | checkmark | Custom icon shown when checked |
583
+ | `disabled` | `boolean` | `false` | Prevents interaction |
584
+ | `helperText` | `string` | — | Hint text below the checkbox |
585
+ | `showErrorText` | `boolean` | `true` | Show validation error message |
586
+ | `variant` | `string` | `'default'` | Theme variant |
587
+ | `theme` | `EVENTS` | — | One-off token override |
588
+
589
+ ### Example
590
+
591
+ ```tsx
592
+ import { Checkbox } from '@foi/design-system';
593
+
594
+ // RHF
595
+ <Checkbox name='agree' control={control} label='Acepto los términos' />;
596
+
597
+ // Controlled
598
+ const [checked, setChecked] = useState(false);
599
+ <Checkbox checked={checked} onChecked={setChecked} label='Recordarme' />;
600
+ ```
601
+
602
+ ## Radio
603
+
604
+ A single radio button. Always used inside `RadioGroup` — it receives its state from the group context automatically.
605
+
606
+ ### Props
607
+
608
+ | Prop | Type | Default | Description |
609
+ | ---------- | ------------- | ----------- | -------------------------------------------- |
610
+ | `value` | `string` | — | The value this option represents |
611
+ | `label` | `string` | — | Text label next to the radio |
612
+ | `icon` | `JSX.Element` | filled dot | Custom icon shown when selected |
613
+ | `disabled` | `boolean` | `false` | Overrides the group `disabled` for this item |
614
+ | `variant` | `string` | `'default'` | Theme variant |
615
+ | `theme` | `EVENTS` | — | One-off token override |
616
+
617
+ ## Select
618
+
619
+ A dropdown select field backed by a virtual-scroll list. Integrates with RHF and stores the selected `value` string.
620
+
621
+ ### Props
622
+
623
+ | Prop | Type | Default | Description |
624
+ | ------------------ | -------------- | ----------- | --------------------------------------------------- |
625
+ | `name` | `string` | — | RHF field name |
626
+ | `label` | `string` | — | Floating label |
627
+ | `control` | `Control` | — | RHF control object |
628
+ | `options` | `OptionProp[]` | — | List of `{ value, label, description? }` items |
629
+ | `disabled` | `boolean` | `false` | Prevents interaction |
630
+ | `range` | `number` | `100` | Options per page (virtual scroll activates above 3) |
631
+ | `hasSearch` | `boolean` | `false` | Shows a search input — filters after 3 characters |
632
+ | `hasDescription` | `boolean` | `false` | Renders a secondary description line per option |
633
+ | `hasStaticOptions` | `boolean` | `false` | Renders dropdown inline (no absolute positioning) |
634
+ | `helperText` | `string` | — | Hint text below the field |
635
+ | `showErrorText` | `boolean` | `true` | Show validation error message |
636
+ | `hasPadding` | `boolean` | `false` | Add bottom padding for helper/error text |
637
+ | `variant` | `string` | `'default'` | Theme variant |
638
+ | `theme` | `EVENTS` | — | One-off token override |
639
+
640
+ ### Example
641
+
642
+ ```tsx
643
+ import { Select } from '@foi/design-system';
644
+
645
+ const options = [
646
+ { value: 'cl', label: 'Chile' },
647
+ { value: 'ar', label: 'Argentina', description: 'Buenos Aires' },
648
+ ];
649
+
650
+ <Select name='country' control={control} label='País' options={options} hasSearch />;
651
+ ```
652
+
653
+ ## DatePicker
654
+
655
+ A date input field with an inline or floating calendar. Stores a `Date` value in RHF.
656
+
657
+ ### Props
658
+
659
+ | Prop | Type | Default | Description |
660
+ | ------------------ | -------------- | ----------- | ----------------------------------------------------- |
661
+ | `name` | `string` | — | RHF field name |
662
+ | `label` | `string` | — | Floating label |
663
+ | `control` | `Control` | — | RHF control object |
664
+ | `language` | `'es' \| 'en'` | browser | Date format and labels (`DD/MM/YYYY` vs `MM/DD/YYYY`) |
665
+ | `minDate` | `Date` | — | Earliest selectable date |
666
+ | `maxDate` | `Date` | — | Latest selectable date |
667
+ | `disablePast` | `boolean` | `false` | Disables all dates before today |
668
+ | `disableFuture` | `boolean` | `false` | Disables all dates after today |
669
+ | `hasStaticOptions` | `boolean` | `false` | Renders calendar inline instead of floating |
670
+ | `disabled` | `boolean` | `false` | Prevents interaction |
671
+ | `helperText` | `string` | — | Hint text below the field |
672
+ | `showErrorText` | `boolean` | `true` | Show validation error message |
673
+ | `hasPadding` | `boolean` | `false` | Add bottom padding for helper/error text |
674
+ | `variant` | `string` | `'default'` | Theme variant |
675
+ | `theme` | `EVENTS` | — | One-off token override |
676
+
677
+ ### Example
678
+
679
+ ```tsx
680
+ import { DatePicker } from '@foi/design-system';
681
+
682
+ <DatePicker
683
+ name='birthDate'
684
+ control={control}
685
+ label='Fecha de nacimiento'
686
+ language='es'
687
+ disableFuture
688
+ maxDate={new Date('2005-01-01')}
689
+ />;
690
+ ```
691
+
692
+ ## Switch
693
+
694
+ A toggle switch that stores a `boolean` value in RHF.
695
+
696
+ ### Props
697
+
698
+ | Prop | Type | Default | Description |
699
+ | ------------ | ------------- | ----------- | ----------------------------- |
700
+ | `name` | `string` | — | RHF field name |
701
+ | `control` | `Control` | — | RHF control object |
702
+ | `label` | `string` | — | Text label next to the switch |
703
+ | `iconOn` | `JSX.Element` | — | Icon on the thumb when on |
704
+ | `iconOff` | `JSX.Element` | — | Icon on the thumb when off |
705
+ | `disabled` | `boolean` | `false` | Prevents interaction |
706
+ | `helperText` | `string` | — | Hint text below the switch |
707
+ | `variant` | `string` | `'default'` | Theme variant |
708
+ | `theme` | `EVENTS` | — | One-off token override |
709
+
710
+ ### Example
711
+
712
+ ```tsx
713
+ import { Switch, Icon } from '@foi/design-system';
714
+
715
+ <Switch
716
+ name='notifications'
717
+ control={control}
718
+ label='Activar notificaciones'
719
+ iconOn={<Icon name='notifications' />}
720
+ iconOff={<Icon name='notifications_off' />}
721
+ />;
722
+ ```
723
+
724
+ ## Slider
725
+
726
+ A range slider that stores a `number[]` value in RHF. Single-thumb mode uses a 1-element array; range mode uses a 2-element array.
727
+
728
+ ### Props
729
+
730
+ | Prop | Type | Default | Description |
731
+ | ------------ | ---------------------------- | -------------- | ------------------------------------------------- |
732
+ | `name` | `string` | — | RHF field name |
733
+ | `control` | `Control` | — | RHF control object |
734
+ | `min` | `number` | — | Minimum value |
735
+ | `max` | `number` | — | Maximum value (`Infinity` disables click-to-seek) |
736
+ | `step` | `number` | `1` | Snap granularity |
737
+ | `distance` | `number` | `0` | Minimum gap between thumbs (range mode only) |
738
+ | `direction` | `'horizontal' \| 'vertical'` | `'horizontal'` | Slider orientation |
739
+ | `track` | `'normal' \| 'inverted'` | `'normal'` | Fill direction of the track |
740
+ | `showMarks` | `boolean` | `false` | Show a dot at every `step` position |
741
+ | `iconMin` | `JSX.Element` | — | Icon inside the min/single thumb |
742
+ | `iconMax` | `JSX.Element` | — | Icon inside the max thumb (range mode only) |
743
+ | `helperText` | `string` | — | Hint text below the slider |
744
+ | `disabled` | `boolean` | `false` | Prevents interaction |
745
+ | `variant` | `string` | `'default'` | Theme variant |
746
+ | `theme` | `EVENTS` | — | One-off token override |
747
+
748
+ ### Example
749
+
750
+ ```tsx
751
+ import { Slider } from '@foi/design-system';
752
+
753
+ // Single thumb — value is [number]
754
+ <Slider name='volume' control={control} min={0} max={100} />
755
+
756
+ // Range — value is [number, number]
757
+ <Slider name='priceRange' control={control} min={0} max={10000} step={100} distance={500} showMarks />
758
+ ```
759
+
760
+ ## Skeleton
761
+
762
+ An animated placeholder rendered while content is loading. Has no logic — simply swap it out once your data arrives.
763
+
764
+ ### Props
765
+
766
+ | Prop | Type | Default | Description |
767
+ | ----------- | ----------------------------- | --------------- | -------------------------- |
768
+ | `variant` | `'rectangular' \| 'circular'` | `'rectangular'` | Shape of the placeholder |
769
+ | `width` | `string \| number` | — | CSS width or pixel number |
770
+ | `height` | `string \| number` | — | CSS height or pixel number |
771
+ | `className` | `string` | — | Extra CSS class |
772
+
773
+ ### Example
774
+
775
+ ```tsx
776
+ import { Skeleton } from '@foi/design-system';
777
+
778
+ // While loading a user avatar
779
+ {
780
+ isLoading ? <Skeleton variant='circular' width={40} height={40} /> : <Avatar src={user.photo} />;
781
+ }
782
+
783
+ // While loading a text block
784
+ {
785
+ isLoading ? <Skeleton width='100%' height={20} /> : <p>{content}</p>;
786
+ }
787
+ ```
788
+
789
+ ## Pagination
790
+
791
+ A standalone page-navigation bar. `DataGrid` uses this internally, but it can also be used independently.
792
+
793
+ ### Props
794
+
795
+ | Prop | Type | Default | Description |
796
+ | ------------------ | ------------------------ | ------- | ------------------------------------------------- |
797
+ | `page` | `number` | — | Zero-based current page index |
798
+ | `total` | `number` | — | Total row count (used to compute page count) |
799
+ | `pageSize` | `number` | — | Rows per page |
800
+ | `onPageChange` | `(page: number) => void` | — | Fired when the user navigates to a different page |
801
+ | `pageSizeOptions` | `number[]` | — | Shows a rows-per-page selector with these options |
802
+ | `onPageSizeChange` | `(size: number) => void` | — | Fired when the user changes the page size |
803
+ | `loading` | `boolean` | `false` | Disables all controls while data is fetching |
804
+
805
+ ### Example
806
+
807
+ ```tsx
808
+ import { Pagination } from '@foi/design-system';
809
+
810
+ <Pagination
811
+ page={page}
812
+ total={total}
813
+ pageSize={pageSize}
814
+ onPageChange={setPage}
815
+ pageSizeOptions={[10, 25, 50]}
816
+ onPageSizeChange={setPageSize}
817
+ />;
818
+ ```
819
+
820
+ ## CheckboxGroup
821
+
822
+ Manages a set of `Checkbox` children as a `string[]` RHF field. The group value is the array of `value` props of all checked children.
823
+
824
+ ### Props
825
+
826
+ | Prop | Type | Default | Description |
827
+ | --------------- | ---------------------------- | ------------ | ---------------------------------------------- |
828
+ | `name` | `string` | — | RHF field name |
829
+ | `control` | `Control` | — | RHF control object |
830
+ | `children` | `ReactNode` | — | `<Checkbox>` elements (each must have `value`) |
831
+ | `label` | `string` | — | Group label above the checkboxes |
832
+ | `direction` | `'vertical' \| 'horizontal'` | `'vertical'` | Layout direction of the items |
833
+ | `disabled` | `boolean` | `false` | Disables all children |
834
+ | `helperText` | `string` | — | Hint text below the group |
835
+ | `showErrorText` | `boolean` | `true` | Show validation error message |
836
+ | `variant` | `string` | `'default'` | Theme variant |
837
+ | `theme` | `EVENTS` | — | One-off token override |
838
+
839
+ ### Example
840
+
841
+ ```tsx
842
+ import { CheckboxGroup, Checkbox } from '@foi/design-system';
843
+
844
+ const schema = z.object({
845
+ skills: z.array(z.string()).min(1, 'Selecciona al menos una'),
846
+ });
847
+
848
+ <CheckboxGroup name='skills' control={control} label='Habilidades' direction='horizontal'>
849
+ <Checkbox value='react' label='React' />
850
+ <Checkbox value='vue' label='Vue' />
851
+ <Checkbox value='svelte' label='Svelte' />
852
+ </CheckboxGroup>;
853
+ ```
854
+
855
+ ## CheckboxTree
856
+
857
+ A hierarchical checkbox group. The parent checkbox controls all children simultaneously and shows an indeterminate state when some (but not all) children are checked.
858
+
859
+ ### Props
860
+
861
+ | Prop | Type | Default | Description |
862
+ | ------------------- | ------------- | ----------- | ------------------------------------------------- |
863
+ | `name` | `string` | — | RHF field name |
864
+ | `control` | `Control` | — | RHF control object |
865
+ | `children` | `ReactNode` | — | `<Checkbox>` elements (each must have `value`) |
866
+ | `label` | `string` | — | Label on the parent (select-all) checkbox |
867
+ | `iconChecked` | `JSX.Element` | checkmark | Icon on the parent when all children are checked |
868
+ | `iconIndeterminate` | `JSX.Element` | dash | Icon on the parent when some children are checked |
869
+ | `disabled` | `boolean` | `false` | Disables the parent and all children |
870
+ | `helperText` | `string` | — | Hint text below the tree |
871
+ | `showErrorText` | `boolean` | `true` | Show validation error message |
872
+ | `variant` | `string` | `'default'` | Theme variant |
873
+ | `theme` | `EVENTS` | — | One-off token override |
874
+
875
+ ### Example
876
+
877
+ ```tsx
878
+ import { CheckboxTree, Checkbox } from '@foi/design-system';
879
+
880
+ <CheckboxTree name='permissions' control={control} label='Seleccionar todo'>
881
+ <Checkbox value='read' label='Leer' />
882
+ <Checkbox value='write' label='Escribir' />
883
+ <Checkbox value='delete' label='Eliminar' />
884
+ </CheckboxTree>;
885
+ ```
886
+
887
+ ## RadioGroup
888
+
889
+ Manages a set of `Radio` children as a single `string` RHF field. The value is the `value` prop of the selected child.
890
+
891
+ ### Props
892
+
893
+ | Prop | Type | Default | Description |
894
+ | --------------- | ---------------------------- | ------------ | ------------------------------------------- |
895
+ | `name` | `string` | — | RHF field name |
896
+ | `control` | `Control` | — | RHF control object |
897
+ | `children` | `ReactNode` | — | `<Radio>` elements (each must have `value`) |
898
+ | `label` | `string` | — | Group label above the radios |
899
+ | `direction` | `'vertical' \| 'horizontal'` | `'vertical'` | Layout direction of the items |
900
+ | `disabled` | `boolean` | `false` | Disables all children |
901
+ | `helperText` | `string` | — | Hint text below the group |
902
+ | `showErrorText` | `boolean` | `true` | Show validation error message |
903
+ | `variant` | `string` | `'default'` | Theme variant |
904
+ | `theme` | `EVENTS` | — | One-off token override |
905
+
906
+ ### Example
907
+
908
+ ```tsx
909
+ import { RadioGroup, Radio } from '@foi/design-system';
910
+
911
+ const schema = z.object({
912
+ plan: z.string().min(1, 'Selecciona un plan'),
913
+ });
914
+
915
+ <RadioGroup name='plan' control={control} label='Plan' direction='horizontal'>
916
+ <Radio value='free' label='Gratis' />
917
+ <Radio value='pro' label='Pro' />
918
+ <Radio value='enterprise' label='Enterprise' />
919
+ </RadioGroup>;
920
+ ```
921
+
922
+ ## Chip
923
+
924
+ A compact inline label for displaying statuses, tags, or categories. Height is fixed at 24px and width adapts to its content.
925
+
926
+ ### Props
927
+
928
+ | Prop | Type | Default | Description |
929
+ | ----------- | ----------- | ----------- | ---------------------------------------------------------- |
930
+ | `children` | `ReactNode` | — | Label text or custom content |
931
+ | `variant` | `string` | `'default'` | Theme variant — any key defined under `CHIP` in your theme |
932
+ | `className` | `string` | — | Extra CSS class |
933
+ | `theme` | `EVENTS` | — | One-off token override |
934
+
935
+ When `children` is a plain string it is wrapped in a `span.--CHIP-label` that applies horizontal padding. Pass a `ReactNode` to render custom content (e.g. icon + text) directly inside the chip.
936
+
937
+ ### Example
938
+
939
+ ```tsx
940
+ import { Chip } from '@foi/design-system';
941
+
942
+ // Plain string — wrapped in a padded span automatically
943
+ <Chip>Activo</Chip>
944
+ <Chip variant='myVariant'>Pendiente</Chip>
945
+
946
+ // Custom content — ReactNode renders directly inside the chip
947
+ <Chip variant='myVariant'>
948
+ <Icon name='star' />
949
+ <span>Destacado</span>
950
+ </Chip>
951
+ ```
952
+
953
+ ## Toast
954
+
955
+ A notification system built around a React context. `ThemeProvider` automatically includes the provider — no additional setup required. Call `useToast` from anywhere inside the tree to push transient messages.
956
+
957
+ ### Setup
958
+
959
+ `ThemeProvider` includes `ToastProvider` internally. Optionally configure position and default duration via the `toast` prop:
960
+
961
+ ```tsx
962
+ <ThemeProvider themes={[darkTheme]} theme='dark' toast={{ position: 'top-right', duration: 4000 }}>
963
+ <App />
964
+ </ThemeProvider>
965
+ ```
966
+
967
+ ### `useToast`
968
+
969
+ ```tsx
970
+ import { useToast } from '@foi/design-system/hooks';
971
+
972
+ const { push } = useToast();
973
+
974
+ push('Archivo guardado', { variant: 'success', icon: 'check_circle' });
975
+ ```
976
+
977
+ ### `push` options
978
+
979
+ | Option | Type | Default | Description |
980
+ | ---------- | --------- | ----------- | -------------------------------------------------- |
981
+ | `variant` | `string` | `'default'` | Theme variant (maps to a key defined in the theme) |
982
+ | `icon` | `string` | — | Material Symbols icon name rendered on the left |
983
+ | `duration` | `number` | `3000` | Milliseconds before the toast auto-dismisses |
984
+ | `closable` | `boolean` | `false` | Render a × button so the user can dismiss manually |
985
+
986
+ ### `ToastPosition`
987
+
988
+ | Value | Description |
989
+ | ----------------- | ------------- |
990
+ | `'bottom-right'` | Default |
991
+ | `'bottom-center'` | Bottom center |
992
+ | `'bottom-left'` | Bottom left |
993
+ | `'top-right'` | Top right |
994
+ | `'top-center'` | Top center |
995
+ | `'top-left'` | Top left |
996
+
997
+ ### Example
998
+
999
+ ```tsx
1000
+ import { useToast } from '@foi/design-system/hooks';
1001
+
1002
+ const Notifier = () => {
1003
+ const { push } = useToast();
1004
+
1005
+ const save = async () => {
1006
+ try {
1007
+ await api.save();
1008
+ push('Guardado correctamente', { icon: 'check_circle' });
1009
+ } catch {
1010
+ push('No se pudo guardar', { icon: 'error', closable: true, duration: 8000 });
1011
+ }
1012
+ };
1013
+
1014
+ return <Button onClick={save}>Guardar</Button>;
1015
+ };
1016
+ ```
1017
+
1018
+ ### Defining toast variants in the theme
1019
+
1020
+ Toast variants are defined in the theme under the `TOAST` key. Each variant must be self-contained (all tokens, not just overrides) because `useCreateComponentStyles` loads only the requested variant:
1021
+
1022
+ ```ts
1023
+ import type { TOAST } from '@foi/design-system/theme';
1024
+
1025
+ const toast = {
1026
+ TOAST: {
1027
+ DEFAULT: {
1028
+ ROOT: { 'background-color': '#1e293b', 'border-radius': '6px' },
1029
+ EVENTS: {
1030
+ ENABLED: {
1031
+ 'color-primary': '#f8fafc',
1032
+ 'border-color': '#334155',
1033
+ 'icon-color': '#94a3b8',
1034
+ },
1035
+ },
1036
+ },
1037
+ // Add as many named variants as your design requires
1038
+ MY_VARIANT: {
1039
+ ROOT: { 'background-color': '#1e293b', 'border-radius': '6px' },
1040
+ EVENTS: {
1041
+ ENABLED: {
1042
+ 'color-primary': '#f8fafc',
1043
+ 'border-color': '#10b981',
1044
+ 'icon-color': '#10b981',
1045
+ },
1046
+ },
1047
+ },
1048
+ },
1049
+ } as const satisfies TOAST;
1050
+ ```
463
1051
 
464
1052
  ## Project structure
465
1053