@vjdesigns/vjdesigns-ui 1.5.0 → 2.0.0

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 (107) hide show
  1. package/README.md +5 -4
  2. package/dist/ai/llms-full.txt +457 -448
  3. package/dist/ai/llms.txt +1 -1
  4. package/dist/ai/registry.json +126 -116
  5. package/dist/components/data-display/DataGrid/DataGrid.module.css.cjs.js +1 -1
  6. package/dist/components/data-display/DataGrid/DataGrid.module.css.mjs +1 -1
  7. package/dist/components/data-display/VirtualList/VirtualList.module.css.cjs.js +1 -1
  8. package/dist/components/data-display/VirtualList/VirtualList.module.css.mjs +1 -1
  9. package/dist/components/feedback/ErrorSummary/ErrorSummary.module.css.cjs.js +1 -1
  10. package/dist/components/feedback/ErrorSummary/ErrorSummary.module.css.mjs +1 -1
  11. package/dist/components/forms/DateTimePicker/DateTimePicker.module.css.cjs.js +1 -1
  12. package/dist/components/forms/DateTimePicker/DateTimePicker.module.css.mjs +1 -1
  13. package/dist/components/forms/Rating/Rating.module.css.cjs.js +1 -1
  14. package/dist/components/forms/Rating/Rating.module.css.mjs +1 -1
  15. package/dist/components/forms/TimePicker/TimePicker.module.css.cjs.js +1 -1
  16. package/dist/components/forms/TimePicker/TimePicker.module.css.mjs +1 -1
  17. package/dist/components/layout/ResizablePanels/ResizablePanels.cjs.js +1 -1
  18. package/dist/components/layout/ResizablePanels/ResizablePanels.mjs +1 -0
  19. package/dist/components/layout/ResizablePanels/ResizablePanels.module.css.cjs.js +1 -1
  20. package/dist/components/layout/ResizablePanels/ResizablePanels.module.css.mjs +1 -1
  21. package/dist/components/layout/ScrollArea/ScrollArea.module.css.cjs.js +1 -1
  22. package/dist/components/layout/ScrollArea/ScrollArea.module.css.mjs +1 -1
  23. package/dist/components/navigation/ContextMenu/ContextMenu.module.css.cjs.js +1 -1
  24. package/dist/components/navigation/ContextMenu/ContextMenu.module.css.mjs +1 -1
  25. package/dist/components/overlays/HoverCard/HoverCard.module.css.cjs.js +1 -1
  26. package/dist/components/overlays/HoverCard/HoverCard.module.css.mjs +2 -2
  27. package/dist/components/primitives/Button/Button.cjs.js +1 -1
  28. package/dist/components/primitives/Button/Button.mjs +37 -37
  29. package/dist/components/utilities/LocaleProvider/LocaleProvider.module.css.cjs.js +1 -1
  30. package/dist/components/utilities/LocaleProvider/LocaleProvider.module.css.mjs +1 -1
  31. package/dist/components/utilities/Theme/Theme.cjs.js +1 -1
  32. package/dist/components/utilities/Theme/Theme.d.ts +6 -6
  33. package/dist/components/utilities/Theme/Theme.mjs +49 -45
  34. package/dist/hooks/useFormValidation.cjs.js +1 -1
  35. package/dist/hooks/useFormValidation.mjs +1 -1
  36. package/dist/i18n/en.cjs.js +1 -1
  37. package/dist/i18n/en.mjs +50 -0
  38. package/dist/i18n/types.d.ts +56 -0
  39. package/dist/index.cjs.js +1 -1
  40. package/dist/index.mjs +106 -106
  41. package/dist/locales/ar.cjs.js +1 -1
  42. package/dist/locales/ar.mjs +50 -0
  43. package/dist/locales/bn.cjs.js +1 -1
  44. package/dist/locales/bn.mjs +50 -0
  45. package/dist/locales/cs.cjs.js +1 -1
  46. package/dist/locales/cs.mjs +50 -0
  47. package/dist/locales/da.cjs.js +1 -1
  48. package/dist/locales/da.mjs +50 -0
  49. package/dist/locales/de.cjs.js +1 -1
  50. package/dist/locales/de.mjs +50 -0
  51. package/dist/locales/el.cjs.js +1 -1
  52. package/dist/locales/el.mjs +50 -0
  53. package/dist/locales/es.cjs.js +1 -1
  54. package/dist/locales/es.mjs +50 -0
  55. package/dist/locales/fa.cjs.js +1 -1
  56. package/dist/locales/fa.mjs +50 -0
  57. package/dist/locales/fi.cjs.js +1 -1
  58. package/dist/locales/fi.mjs +50 -0
  59. package/dist/locales/fr.cjs.js +1 -1
  60. package/dist/locales/fr.mjs +50 -0
  61. package/dist/locales/he.cjs.js +1 -1
  62. package/dist/locales/he.mjs +50 -0
  63. package/dist/locales/hi.cjs.js +1 -1
  64. package/dist/locales/hi.mjs +50 -0
  65. package/dist/locales/hu.cjs.js +1 -1
  66. package/dist/locales/hu.mjs +50 -0
  67. package/dist/locales/id.cjs.js +1 -1
  68. package/dist/locales/id.mjs +50 -0
  69. package/dist/locales/it.cjs.js +1 -1
  70. package/dist/locales/it.mjs +50 -0
  71. package/dist/locales/ja.cjs.js +1 -1
  72. package/dist/locales/ja.mjs +50 -0
  73. package/dist/locales/ko.cjs.js +1 -1
  74. package/dist/locales/ko.mjs +50 -0
  75. package/dist/locales/nb.cjs.js +1 -1
  76. package/dist/locales/nb.mjs +50 -0
  77. package/dist/locales/nl.cjs.js +1 -1
  78. package/dist/locales/nl.mjs +50 -0
  79. package/dist/locales/pl.cjs.js +1 -1
  80. package/dist/locales/pl.mjs +50 -0
  81. package/dist/locales/pt-BR.cjs.js +1 -1
  82. package/dist/locales/pt-BR.mjs +50 -0
  83. package/dist/locales/ro.cjs.js +1 -1
  84. package/dist/locales/ro.mjs +50 -0
  85. package/dist/locales/ru.cjs.js +1 -1
  86. package/dist/locales/ru.mjs +50 -0
  87. package/dist/locales/sv.cjs.js +1 -1
  88. package/dist/locales/sv.mjs +50 -0
  89. package/dist/locales/th.cjs.js +1 -1
  90. package/dist/locales/th.mjs +50 -0
  91. package/dist/locales/tr.cjs.js +1 -1
  92. package/dist/locales/tr.mjs +50 -0
  93. package/dist/locales/uk.cjs.js +1 -1
  94. package/dist/locales/uk.mjs +50 -0
  95. package/dist/locales/ur.cjs.js +1 -1
  96. package/dist/locales/ur.mjs +50 -0
  97. package/dist/locales/vi.cjs.js +1 -1
  98. package/dist/locales/vi.mjs +50 -0
  99. package/dist/locales/zh-Hans.cjs.js +1 -1
  100. package/dist/locales/zh-Hans.mjs +50 -0
  101. package/dist/locales/zh-Hant.cjs.js +1 -1
  102. package/dist/locales/zh-Hant.mjs +50 -0
  103. package/dist/utils/brandPalette.cjs.js +3 -3
  104. package/dist/utils/brandPalette.d.ts +4 -2
  105. package/dist/utils/brandPalette.mjs +20 -20
  106. package/dist/vjdesigns-ui.css +1 -1
  107. package/package.json +1 -1
@@ -5,7 +5,7 @@
5
5
  - Install: `npm install @vjdesigns/vjdesigns-ui` (peer: react, react-dom 18 or 19).
6
6
  - Import the stylesheet once: `import "@vjdesigns/vjdesigns-ui/dist/vjdesigns-ui.css";`
7
7
  - Import components from the package root: `import { Button, Field, Input } from "@vjdesigns/vjdesigns-ui";`
8
- - Theme: wrap the app in `<Theme brand="blue" | "purple" | ... | "#hex" mode="light" | "dark" accent="brand" | "volt" | "#hex">`.
8
+ - Theme: wrap the app in `<Theme brand="blue" | "purple" | ... | "#hex" mode="light" | "dark" accent="brand" | "lime" | "#hex">`.
9
9
  - Language: `<LocaleProvider locale="es" messages={es}>` with `import es from "@vjdesigns/vjdesigns-ui/locales/es"`.
10
10
  - Forms: wrap each control in `Field` (label, hint, error, required); validate with `useFormValidation` + `ErrorSummary`.
11
11
  - Style with the props (variant, tone, size) and the --color-*, --space-*, --radius-* CSS variables; don't override internal class names.
@@ -281,13 +281,13 @@ import { Theme, Button } from "@vjdesigns/vjdesigns-ui";
281
281
  | --- | --- | --- | --- |
282
282
  | `mode` | `"light" \| "dark"` | inherited | Color mode for everything inside. |
283
283
  | `brand` | `"black" \| "blue" \| "gold" \| "green" \| "purple"`, a hex color, or `{ light, dark }` | inherited | Brand palette for buttons, links, focus rings and indicators. |
284
- | `accent` | `"brand" | "volt"`, a hex color, or `{ light, dark }` | inherited (the brand's own) | The bright highlight on badges, switches, progress bars, sliders, steppers, and active navigation. |
284
+ | `accent` | `"brand" | "lime"`, a hex color, or `{ light, dark }` | inherited (the brand's own) | The bright highlight on badges, switches, progress bars, sliders, steppers, and active navigation. |
285
285
  | `fill` | `boolean` | `false` | Paints the theme's background and text color on the wrapper. |
286
286
 
287
287
  Outside React (or to theme the whole page), set the attributes on `<html>` instead:
288
288
 
289
289
  ```html
290
- <html data-theme="dark" data-brand="purple" data-accent="volt">
290
+ <html data-theme="dark" data-brand="purple" data-accent="lime">
291
291
  ```
292
292
 
293
293
  ### Accent color
@@ -300,13 +300,13 @@ The accent is the energetic highlight: badges, switches, progress bars, sliders,
300
300
  | `purple` | lavender `#c0b0ff` |
301
301
  | `gold` | amber `#f5ad5f` |
302
302
  | `green` | mint `#72d4ad` |
303
- | `black` | volt `#c8f031` (black has no hue to build on) |
303
+ | `black` | lime `#c8f031` (black has no hue to build on) |
304
304
  | a custom hex | generated from that color |
305
305
 
306
306
  To choose a different one:
307
307
 
308
308
  ```tsx
309
- <Theme accent="volt">…</Theme> // the original bright yellow-green
309
+ <Theme accent="lime">…</Theme> // the original bright yellow-green ("volt", its former name, still works)
310
310
  <Theme accent="#ff6b00">…</Theme> // any color
311
311
  <Theme accent="brand">…</Theme> // back to the brand's own, inside an area with another accent
312
312
  ```
@@ -1107,36 +1107,36 @@ import { DataGrid } from "@vjdesigns/vjdesigns-ui";
1107
1107
 
1108
1108
  Category: data-display · Storybook: Data display/DataGrid
1109
1109
 
1110
- An interactive data table (WAI-ARIA APG "Data Grid"): sortable columns
1111
- (with Shift for multi-column sorts), search filtering, single or
1112
- multiple row selection, pagination, and a full keyboard model. Works with
1113
- client-side data, or server-side data with `manual`.
1114
-
1115
- Keyboard: the grid is one Tab stop. Arrow keys move between cells
1116
- (mirrored in RTL), Home / End go to the row's first or last cell, Ctrl+Home
1117
- / Ctrl+End to the first or last cell of the grid, Page Up / Page Down move
1118
- ten rows. On a header, Enter sorts (Shift+Enter adds a secondary sort).
1119
- Space selects the row, Shift+click or Shift+Space selects a range, and
1120
- Ctrl/⌘+A selects every row. A cell with a single button, link, or
1121
- checkbox focuses it directly; for cells with several controls or a text
1122
- field, Enter or F2 moves into the cell, Tab cycles inside it, and Escape
1123
- returns to the grid.
1124
-
1125
- Screen readers hear sort changes and filter result counts; the grid is a
1126
- real `<table role="grid">` with `aria-sort`, `aria-selected`,
1127
- `aria-rowcount`, and `aria-rowindex`.
1128
-
1129
- ```tsx
1130
- <DataGrid
1131
- aria-label="Roster"
1132
- rows={players}
1133
- columns={[
1134
- { key: "name", header: "Player", sortable: true, rowHeader: true },
1135
- { key: "ppg", header: "PPG", sortable: true, numeric: true },
1136
- ]}
1137
- selectionMode="multiple"
1138
- pageSize={10}
1139
- />
1110
+ An interactive data table (WAI-ARIA APG "Data Grid"): sortable columns
1111
+ (with Shift for multi-column sorts), search filtering, single or
1112
+ multiple row selection, pagination, and a full keyboard model. Works with
1113
+ client-side data, or server-side data with `manual`.
1114
+
1115
+ Keyboard: the grid is one Tab stop. Arrow keys move between cells
1116
+ (mirrored in RTL), Home / End go to the row's first or last cell, Ctrl+Home
1117
+ / Ctrl+End to the first or last cell of the grid, Page Up / Page Down move
1118
+ ten rows. On a header, Enter sorts (Shift+Enter adds a secondary sort).
1119
+ Space selects the row, Shift+click or Shift+Space selects a range, and
1120
+ Ctrl/⌘+A selects every row. A cell with a single button, link, or
1121
+ checkbox focuses it directly; for cells with several controls or a text
1122
+ field, Enter or F2 moves into the cell, Tab cycles inside it, and Escape
1123
+ returns to the grid.
1124
+
1125
+ Screen readers hear sort changes and filter result counts; the grid is a
1126
+ real `<table role="grid">` with `aria-sort`, `aria-selected`,
1127
+ `aria-rowcount`, and `aria-rowindex`.
1128
+
1129
+ ```tsx
1130
+ <DataGrid
1131
+ aria-label="Roster"
1132
+ rows={players}
1133
+ columns={[
1134
+ { key: "name", header: "Player", sortable: true, rowHeader: true },
1135
+ { key: "ppg", header: "PPG", sortable: true, numeric: true },
1136
+ ]}
1137
+ selectionMode="multiple"
1138
+ pageSize={10}
1139
+ />
1140
1140
  ```
1141
1141
 
1142
1142
  ### Props
@@ -1816,25 +1816,25 @@ import { VirtualList } from "@vjdesigns/vjdesigns-ui";
1816
1816
 
1817
1817
  Category: data-display · Storybook: Data display/VirtualList
1818
1818
 
1819
- A list that renders only the items in view, so tens of thousands of rows
1820
- scroll smoothly. Items may have any height (they're measured as they
1821
- render). Built on `useVirtualizer` and `ScrollArea`.
1822
-
1823
- Accessibility: a real `role="list"` whose items carry `aria-setsize` and
1824
- `aria-posinset`, so screen readers announce "item 4,812 of 10,000" even
1825
- though only a few dozen exist in the page. The scroll area is keyboard
1826
- scrollable, and Tab through items' links and buttons keeps working (new
1827
- items render as focus scrolls them into view). Browser find-in-page only
1828
- sees rendered items, so offer search for long lists.
1829
-
1830
- ```tsx
1831
- <VirtualList
1832
- aria-label="All players"
1833
- items={players}
1834
- height="24rem"
1835
- getKey={(p) => p.id}
1836
- renderItem={(p) => <PlayerRow player={p} />}
1837
- />
1819
+ A list that renders only the items in view, so tens of thousands of rows
1820
+ scroll smoothly. Items may have any height (they're measured as they
1821
+ render). Built on `useVirtualizer` and `ScrollArea`.
1822
+
1823
+ Accessibility: a real `role="list"` whose items carry `aria-setsize` and
1824
+ `aria-posinset`, so screen readers announce "item 4,812 of 10,000" even
1825
+ though only a few dozen exist in the page. The scroll area is keyboard
1826
+ scrollable, and Tab through items' links and buttons keeps working (new
1827
+ items render as focus scrolls them into view). Browser find-in-page only
1828
+ sees rendered items, so offer search for long lists.
1829
+
1830
+ ```tsx
1831
+ <VirtualList
1832
+ aria-label="All players"
1833
+ items={players}
1834
+ height="24rem"
1835
+ getKey={(p) => p.id}
1836
+ renderItem={(p) => <PlayerRow player={p} />}
1837
+ />
1838
1838
  ```
1839
1839
 
1840
1840
  ### Props
@@ -1969,7 +1969,7 @@ Accessibility:
1969
1969
 
1970
1970
  - Playground
1971
1971
  - Tones: Every tone, solid. `accent` is the promo strip, always with dark text.
1972
- - Volt Promo: The accent promo strip: bold, dark text on accent, with a strong call to action.
1972
+ - Accent Promo: The accent promo strip: bold, dark text on accent, with a strong call to action.
1973
1973
  - Variants: Solid and soft side by side for every tone. Check both themes.
1974
1974
  - Sizes: Small, medium, and large. Buttons inside are one step smaller than the strip.
1975
1975
  - With Actions: Buttons get a verified default look for the strip. Links in the message use `tone="neutral"` so they take the strip's text color and stay underlined.
@@ -2013,15 +2013,15 @@ import { ErrorSummary } from "@vjdesigns/vjdesigns-ui";
2013
2013
 
2014
2014
  Category: feedback · Storybook: Feedback/ErrorSummary
2015
2015
 
2016
- The list of a form's errors, shown above it after a failed submit: a danger
2017
- `Alert` whose items link to their fields (activating one moves focus to the
2018
- field). It's focusable (`tabIndex={-1}`): give `useFormValidation`'s
2019
- `summaryRef` as `ref` and a failed submit moves focus here, so screen
2020
- readers announce the heading and the list. The title is a real heading
2021
- (`h2` by default; set `headingLevel` to fit the page outline).
2022
-
2023
- ```tsx
2024
- <ErrorSummary ref={form.summaryRef} errors={form.errorList} />
2016
+ The list of a form's errors, shown above it after a failed submit: a danger
2017
+ `Alert` whose items link to their fields (activating one moves focus to the
2018
+ field). It's focusable (`tabIndex={-1}`): give `useFormValidation`'s
2019
+ `summaryRef` as `ref` and a failed submit moves focus here, so screen
2020
+ readers announce the heading and the list. The title is a real heading
2021
+ (`h2` by default; set `headingLevel` to fit the page outline).
2022
+
2023
+ ```tsx
2024
+ <ErrorSummary ref={form.summaryRef} errors={form.errorList} />
2025
2025
  ```
2026
2026
 
2027
2027
  ### Props
@@ -2519,16 +2519,16 @@ import { ColorInput } from "@vjdesigns/vjdesigns-ui";
2519
2519
 
2520
2520
  Category: forms · Storybook: Forms/ColorPicker
2521
2521
 
2522
- A pill field showing a swatch and the color value; pressing it opens a
2523
- `ColorPicker` in a `Popover` (a non-modal dialog on the shared overlay
2524
- layer stack: Escape and outside presses close it, focus returns to the
2525
- field). Takes the same value, format, alpha, swatch, and state props as
2526
- `ColorPicker`.
2527
-
2528
- The trigger is a native `<button>` with `aria-haspopup="dialog"` and
2529
- `aria-expanded`. Its name is the label plus the current value and a plain
2530
- description ("Jersey color, #1d4ed8, dark blue"). Inside a `Field`, the
2531
- label, hint, error, invalid, disabled, and read-only state are wired to
2522
+ A pill field showing a swatch and the color value; pressing it opens a
2523
+ `ColorPicker` in a `Popover` (a non-modal dialog on the shared overlay
2524
+ layer stack: Escape and outside presses close it, focus returns to the
2525
+ field). Takes the same value, format, alpha, swatch, and state props as
2526
+ `ColorPicker`.
2527
+
2528
+ The trigger is a native `<button>` with `aria-haspopup="dialog"` and
2529
+ `aria-expanded`. Its name is the label plus the current value and a plain
2530
+ description ("Jersey color, #1d4ed8, dark blue"). Inside a `Field`, the
2531
+ label, hint, error, invalid, disabled, and read-only state are wired to
2532
2532
  it. A read-only field still opens, so the values can be inspected.
2533
2533
 
2534
2534
  ### Props
@@ -2729,22 +2729,22 @@ import { DatePicker } from "@vjdesigns/vjdesigns-ui";
2729
2729
 
2730
2730
  Category: forms · Storybook: Forms/DatePicker
2731
2731
 
2732
- A date field: type a date in the locale's short format (or ISO), or press
2733
- the calendar button to pick one from a `Calendar` in a popup dialog.
2734
-
2735
- The value is an ISO date string ("YYYY-MM-DD"), so it never shifts with
2736
- time zones. The text field shows it in the locale's numeric format; on
2737
- blur (or Enter), typed text is parsed and normalized, or rejected with an
2738
- inline error. Inside a `Field`, label, hint, error, required, disabled,
2739
- and invalid state are wired to the text input.
2740
-
2741
- The popup is a modal `Popover` on the shared overlay layer stack, so
2742
- Escape closes only the calendar, also inside a `Modal` or `Drawer`.
2743
-
2744
- ```tsx
2745
- <Field label="Game day">
2746
- <DatePicker name="gameDay" min="2026-10-01" onValueChange={setDay} />
2747
- </Field>
2732
+ A date field: type a date in the locale's short format (or ISO), or press
2733
+ the calendar button to pick one from a `Calendar` in a popup dialog.
2734
+
2735
+ The value is an ISO date string ("YYYY-MM-DD"), so it never shifts with
2736
+ time zones. The text field shows it in the locale's numeric format; on
2737
+ blur (or Enter), typed text is parsed and normalized, or rejected with an
2738
+ inline error. Inside a `Field`, label, hint, error, required, disabled,
2739
+ and invalid state are wired to the text input.
2740
+
2741
+ The popup is a modal `Popover` on the shared overlay layer stack, so
2742
+ Escape closes only the calendar, also inside a `Modal` or `Drawer`.
2743
+
2744
+ ```tsx
2745
+ <Field label="Game day">
2746
+ <DatePicker name="gameDay" min="2026-10-01" onValueChange={setDay} />
2747
+ </Field>
2748
2748
  ```
2749
2749
 
2750
2750
  ### Props
@@ -2816,25 +2816,25 @@ import { DateRangePicker } from "@vjdesigns/vjdesigns-ui";
2816
2816
 
2817
2817
  Category: forms · Storybook: Forms/DatePicker
2818
2818
 
2819
- A date range field: two text fields (start and end) typed in the locale's
2820
- short format, plus a calendar button that opens a range `Calendar` (two
2821
- months by default) in a modal popup.
2822
-
2823
- The value is `{ start, end }` with ISO dates ("YYYY-MM-DD"). In the
2824
- calendar, the first pick sets the start and the second the end; the range
2825
- is committed (and the popup closes) only when both are picked, so Escape
2826
- after one pick changes nothing. Typed dates are validated like
2827
- `DatePicker`'s, plus an end before the start is rejected.
2828
-
2829
- Accessibility: the pair is a `role="group"` named by the `Field` label
2830
- (or `aria-label`); each field is named by that label plus its own
2831
- visible label ("Season dates, Start date"). Field hint, error, required, disabled, and
2832
- invalid state reach both fields. Errors are announced politely.
2833
-
2834
- ```tsx
2835
- <Field label="Season dates">
2836
- <DateRangePicker startName="from" endName="to" onValueChange={setRange} />
2837
- </Field>
2819
+ A date range field: two text fields (start and end) typed in the locale's
2820
+ short format, plus a calendar button that opens a range `Calendar` (two
2821
+ months by default) in a modal popup.
2822
+
2823
+ The value is `{ start, end }` with ISO dates ("YYYY-MM-DD"). In the
2824
+ calendar, the first pick sets the start and the second the end; the range
2825
+ is committed (and the popup closes) only when both are picked, so Escape
2826
+ after one pick changes nothing. Typed dates are validated like
2827
+ `DatePicker`'s, plus an end before the start is rejected.
2828
+
2829
+ Accessibility: the pair is a `role="group"` named by the `Field` label
2830
+ (or `aria-label`); each field is named by that label plus its own
2831
+ visible label ("Season dates, Start date"). Field hint, error, required, disabled, and
2832
+ invalid state reach both fields. Errors are announced politely.
2833
+
2834
+ ```tsx
2835
+ <Field label="Season dates">
2836
+ <DateRangePicker startName="from" endName="to" onValueChange={setRange} />
2837
+ </Field>
2838
2838
  ```
2839
2839
 
2840
2840
  ### Props
@@ -2881,25 +2881,25 @@ import { DateTimePicker } from "@vjdesigns/vjdesigns-ui";
2881
2881
 
2882
2882
  Category: forms · Storybook: Forms/DateTimePicker
2883
2883
 
2884
- A date and a time together: a `DatePicker` and a `TimePicker` side by
2885
- side, with one value, "YYYY-MM-DDTHH:mm" (the format of a native
2886
- `datetime-local` input, with no time zone). Each half keeps its full
2887
- behavior: typed dates in the locale's format or the calendar popup, typed
2888
- times in any common form or the list of times.
2889
-
2890
- `min` and `max` limit the calendar by their dates, and the time list by
2891
- their times on those boundary dates. `defaultTime` fills the time when a
2892
- date is picked first.
2893
-
2894
- Accessibility: the pair is a `role="group"` named by the `Field` label
2895
- (or `aria-label`); each field is named by that label plus its own
2896
- visible label ("Tip-off, Date", "Tip-off, Time"). Field hint, error,
2897
- required, disabled, and invalid state reach both.
2898
-
2899
- ```tsx
2900
- <Field label="Tip-off">
2901
- <DateTimePicker name="tipoff" defaultTime="19:00" step={15} />
2902
- </Field>
2884
+ A date and a time together: a `DatePicker` and a `TimePicker` side by
2885
+ side, with one value, "YYYY-MM-DDTHH:mm" (the format of a native
2886
+ `datetime-local` input, with no time zone). Each half keeps its full
2887
+ behavior: typed dates in the locale's format or the calendar popup, typed
2888
+ times in any common form or the list of times.
2889
+
2890
+ `min` and `max` limit the calendar by their dates, and the time list by
2891
+ their times on those boundary dates. `defaultTime` fills the time when a
2892
+ date is picked first.
2893
+
2894
+ Accessibility: the pair is a `role="group"` named by the `Field` label
2895
+ (or `aria-label`); each field is named by that label plus its own
2896
+ visible label ("Tip-off, Date", "Tip-off, Time"). Field hint, error,
2897
+ required, disabled, and invalid state reach both.
2898
+
2899
+ ```tsx
2900
+ <Field label="Tip-off">
2901
+ <DateTimePicker name="tipoff" defaultTime="19:00" step={15} />
2902
+ </Field>
2903
2903
  ```
2904
2904
 
2905
2905
  ### Props
@@ -3586,10 +3586,10 @@ import { Rating } from "@vjdesigns/vjdesigns-ui";
3586
3586
 
3587
3587
  Category: forms · Storybook: Forms/Rating
3588
3588
 
3589
- An editable star (or custom icon) rating, built on real native radios
3590
- (visually hidden but focusable, sharing one `name`) inside
3591
- `role="radiogroup"`. Tab lands on the selected value (or the first option),
3592
- arrow keys move and select, and the value submits with a form. Supports
3589
+ An editable star (or custom icon) rating, built on real native radios
3590
+ (visually hidden but focusable, sharing one `name`) inside
3591
+ `role="radiogroup"`. Tab lands on the selected value (or the first option),
3592
+ arrow keys move and select, and the value submits with a form. Supports
3593
3593
  half values, clearing, hover preview, and Field/Fieldset wiring.
3594
3594
 
3595
3595
  ### Props
@@ -3645,9 +3645,9 @@ import { RatingDisplay } from "@vjdesigns/vjdesigns-ui";
3645
3645
 
3646
3646
  Category: forms · Storybook: Forms/Rating
3647
3647
 
3648
- A read-only rating: a row of icons with fractional fill, exposed to
3649
- assistive tech as a single `role="img"` named "Rated 4.5 out of 5",
3650
- optionally followed by the value ("4.5 / 5") and a review count.
3648
+ A read-only rating: a row of icons with fractional fill, exposed to
3649
+ assistive tech as a single `role="img"` named "Rated 4.5 out of 5",
3650
+ optionally followed by the value ("4.5 / 5") and a review count.
3651
3651
  Not focusable. For an editable rating use `Rating`.
3652
3652
 
3653
3653
  ### Props
@@ -3994,29 +3994,29 @@ import { TagInput } from "@vjdesigns/vjdesigns-ui";
3994
3994
 
3995
3995
  Category: forms · Storybook: Forms/TagInput
3996
3996
 
3997
- A text field that turns entries into removable tags (chips). Type and
3998
- press Enter (or a delimiter such as a comma) to add; paste "a, b, c" to
3999
- add several at once.
4000
-
4001
- Keyboard:
4002
- - Enter / delimiters (and Tab with `addOnTab`): add the typed text.
4003
- - Backspace in an empty input, or Left Arrow with the caret at the
4004
- start: move focus to the last tag's remove button. A second Backspace
4005
- (or Delete) removes that tag and focus returns to the next tag, or the
4006
- input. This two-step pattern avoids deleting a tag by accident and
4007
- lets screen readers announce which tag is about to be removed.
4008
- - On a tag: Left / Right Arrow move between tags and back to the input;
4009
- Home / End jump to the first tag / the input; Backspace or Delete removes.
4010
-
4011
- Inside a `Field`, the label, hint, error, required, disabled, read-only,
4012
- and size are wired automatically. `ref` points at the `<input>`;
4013
- native input props are spread onto it, while `className` and `style` go
4014
- to the outer wrapper.
4015
-
4016
- ```tsx
4017
- <Field label="Skills">
4018
- <TagInput defaultValue={["Defense"]} placeholder="Add a skill" />
4019
- </Field>
3997
+ A text field that turns entries into removable tags (chips). Type and
3998
+ press Enter (or a delimiter such as a comma) to add; paste "a, b, c" to
3999
+ add several at once.
4000
+
4001
+ Keyboard:
4002
+ - Enter / delimiters (and Tab with `addOnTab`): add the typed text.
4003
+ - Backspace in an empty input, or Left Arrow with the caret at the
4004
+ start: move focus to the last tag's remove button. A second Backspace
4005
+ (or Delete) removes that tag and focus returns to the next tag, or the
4006
+ input. This two-step pattern avoids deleting a tag by accident and
4007
+ lets screen readers announce which tag is about to be removed.
4008
+ - On a tag: Left / Right Arrow move between tags and back to the input;
4009
+ Home / End jump to the first tag / the input; Backspace or Delete removes.
4010
+
4011
+ Inside a `Field`, the label, hint, error, required, disabled, read-only,
4012
+ and size are wired automatically. `ref` points at the `<input>`;
4013
+ native input props are spread onto it, while `className` and `style` go
4014
+ to the outer wrapper.
4015
+
4016
+ ```tsx
4017
+ <Field label="Skills">
4018
+ <TagInput defaultValue={["Defense"]} placeholder="Add a skill" />
4019
+ </Field>
4020
4020
  ```
4021
4021
 
4022
4022
  ### Props
@@ -4125,23 +4125,23 @@ import { TimePicker } from "@vjdesigns/vjdesigns-ui";
4125
4125
 
4126
4126
  Category: forms · Storybook: Forms/TimePicker
4127
4127
 
4128
- A time field: type a time in almost any form ("2:30 pm", "1430", "230p",
4129
- "14h30", the locale's own format), or pick one from a list of times
4130
- `step` minutes apart. Built on `Combobox`, so the list follows the
4131
- WAI-ARIA combobox pattern: ArrowDown opens it on the selected time,
4132
- arrows move, Enter picks, Escape closes, and typing filters it.
4133
-
4134
- The value is a 24-hour "HH:mm" string, the same as a native
4135
- `<input type="time">`, so it never shifts with time zones. The field
4136
- shows it in the locale's clock (12- or 24-hour, override with
4137
- `hourCycle`). On blur (or Enter), typed text is parsed and normalized, or
4138
- rejected with an inline, announced error. Inside a `Field`, label, hint,
4139
- error, required, disabled, and invalid state are wired to the input.
4140
-
4141
- ```tsx
4142
- <Field label="Tip-off">
4143
- <TimePicker name="tipoff" min="08:00" max="22:00" step={15} />
4144
- </Field>
4128
+ A time field: type a time in almost any form ("2:30 pm", "1430", "230p",
4129
+ "14h30", the locale's own format), or pick one from a list of times
4130
+ `step` minutes apart. Built on `Combobox`, so the list follows the
4131
+ WAI-ARIA combobox pattern: ArrowDown opens it on the selected time,
4132
+ arrows move, Enter picks, Escape closes, and typing filters it.
4133
+
4134
+ The value is a 24-hour "HH:mm" string, the same as a native
4135
+ `<input type="time">`, so it never shifts with time zones. The field
4136
+ shows it in the locale's clock (12- or 24-hour, override with
4137
+ `hourCycle`). On blur (or Enter), typed text is parsed and normalized, or
4138
+ rejected with an inline, announced error. Inside a `Field`, label, hint,
4139
+ error, required, disabled, and invalid state are wired to the input.
4140
+
4141
+ ```tsx
4142
+ <Field label="Tip-off">
4143
+ <TimePicker name="tipoff" min="08:00" max="22:00" step={15} />
4144
+ </Field>
4145
4145
  ```
4146
4146
 
4147
4147
  ### Props
@@ -4689,24 +4689,24 @@ import { ScrollArea } from "@vjdesigns/vjdesigns-ui";
4689
4689
 
4690
4690
  Category: layout · Storybook: Layout/ScrollArea
4691
4691
 
4692
- A scrolling region with slim, on-brand scrollbars that work the same in
4693
- every browser. Scrolling itself stays native (wheel, touch, trackpad
4694
- momentum, keyboard, find-in-page, scroll anchoring); only the look of the
4695
- bars is custom. The thumbs can be dragged, and pressing the track pages
4696
- toward the press.
4697
-
4698
- Accessibility: while the content overflows, the viewport is focusable
4699
- (`tabIndex=0`) so keyboard users can scroll it with the arrow keys, Page
4700
- Up / Page Down, and Home / End. Give it a name with `aria-label` or
4701
- `aria-labelledby`, which also makes it a `region` landmark. The custom
4702
- bars are hidden from assistive technology (they duplicate native
4703
- scrolling). Edge shadows show there is more to see. In Windows High
4704
- Contrast mode the native scrollbars come back.
4705
-
4706
- ```tsx
4707
- <ScrollArea maxHeight="20rem" aria-label="Roster">
4708
- <List>…</List>
4709
- </ScrollArea>
4692
+ A scrolling region with slim, on-brand scrollbars that work the same in
4693
+ every browser. Scrolling itself stays native (wheel, touch, trackpad
4694
+ momentum, keyboard, find-in-page, scroll anchoring); only the look of the
4695
+ bars is custom. The thumbs can be dragged, and pressing the track pages
4696
+ toward the press.
4697
+
4698
+ Accessibility: while the content overflows, the viewport is focusable
4699
+ (`tabIndex=0`) so keyboard users can scroll it with the arrow keys, Page
4700
+ Up / Page Down, and Home / End. Give it a name with `aria-label` or
4701
+ `aria-labelledby`, which also makes it a `region` landmark. The custom
4702
+ bars are hidden from assistive technology (they duplicate native
4703
+ scrolling). Edge shadows show there is more to see. In Windows High
4704
+ Contrast mode the native scrollbars come back.
4705
+
4706
+ ```tsx
4707
+ <ScrollArea maxHeight="20rem" aria-label="Roster">
4708
+ <List>…</List>
4709
+ </ScrollArea>
4710
4710
  ```
4711
4711
 
4712
4712
  ### Props
@@ -4857,32 +4857,32 @@ import { ContextMenu } from "@vjdesigns/vjdesigns-ui";
4857
4857
 
4858
4858
  Category: navigation · Storybook: Navigation/ContextMenu
4859
4859
 
4860
- A menu that opens at the pointer on right-click (or a long press on touch
4861
- screens), for actions on the thing under the pointer. It is a `Menu` in
4862
- every other way: compose it from the same items (`ContextMenu.Item`,
4863
- `.CheckboxItem`, `.RadioGroup` / `.RadioItem`, `.Group` / `.Label`,
4864
- `.Separator`, and submenus), with the same keyboard model (arrows, Home /
4865
- End, typeahead, Escape, Tab) and RTL support.
4866
-
4867
- Keyboard: with focus inside the trigger area, Shift+F10 or the Menu key
4868
- opens the menu next to the focused element and focuses its first item.
4869
- On close, focus goes back to where it was.
4870
-
4871
- Right-click menus are hard to discover, and many touch screens and
4872
- assistive technologies never open them, so offer the same actions
4873
- somewhere visible too (a `Menu` button, a toolbar). Treat the context
4874
- menu as a shortcut, never the only way.
4875
-
4876
- ```tsx
4877
- <ContextMenu>
4878
- <ContextMenu.Trigger>
4879
- <Card tabIndex={0}>Game film</Card>
4880
- </ContextMenu.Trigger>
4881
- <ContextMenu.Content>
4882
- <ContextMenu.Item onSelect={rename}>Rename</ContextMenu.Item>
4883
- <ContextMenu.Item tone="danger" onSelect={remove}>Delete</ContextMenu.Item>
4884
- </ContextMenu.Content>
4885
- </ContextMenu>
4860
+ A menu that opens at the pointer on right-click (or a long press on touch
4861
+ screens), for actions on the thing under the pointer. It is a `Menu` in
4862
+ every other way: compose it from the same items (`ContextMenu.Item`,
4863
+ `.CheckboxItem`, `.RadioGroup` / `.RadioItem`, `.Group` / `.Label`,
4864
+ `.Separator`, and submenus), with the same keyboard model (arrows, Home /
4865
+ End, typeahead, Escape, Tab) and RTL support.
4866
+
4867
+ Keyboard: with focus inside the trigger area, Shift+F10 or the Menu key
4868
+ opens the menu next to the focused element and focuses its first item.
4869
+ On close, focus goes back to where it was.
4870
+
4871
+ Right-click menus are hard to discover, and many touch screens and
4872
+ assistive technologies never open them, so offer the same actions
4873
+ somewhere visible too (a `Menu` button, a toolbar). Treat the context
4874
+ menu as a shortcut, never the only way.
4875
+
4876
+ ```tsx
4877
+ <ContextMenu>
4878
+ <ContextMenu.Trigger>
4879
+ <Card tabIndex={0}>Game film</Card>
4880
+ </ContextMenu.Trigger>
4881
+ <ContextMenu.Content>
4882
+ <ContextMenu.Item onSelect={rename}>Rename</ContextMenu.Item>
4883
+ <ContextMenu.Item tone="danger" onSelect={remove}>Delete</ContextMenu.Item>
4884
+ </ContextMenu.Content>
4885
+ </ContextMenu>
4886
4886
  ```
4887
4887
 
4888
4888
  ### Props
@@ -4900,8 +4900,8 @@ menu as a shortcut, never the only way.
4900
4900
 
4901
4901
  Also exported as `ContextMenuTrigger`.
4902
4902
 
4903
- The area that opens the menu: right-click, a touch long press, or
4904
- Shift+F10 / the Menu key while focus is inside it. Make the area (or the
4903
+ The area that opens the menu: right-click, a touch long press, or
4904
+ Shift+F10 / the Menu key while focus is inside it. Make the area (or the
4905
4905
  items in it) focusable so keyboard users can reach the menu.
4906
4906
 
4907
4907
  - `children`: `ReactNode`. The area that answers right-clicks: plain content (wrapped in a `div`) or a single element.
@@ -4912,8 +4912,8 @@ items in it) focusable so keyboard users can reach the menu.
4912
4912
 
4913
4913
  Also exported as `ContextMenuContent`.
4914
4914
 
4915
- The floating menu (`role="menu"`), opened at the pointer. Takes the same
4916
- props as `Menu.Content`; `placement` is relative to the pointer, and
4915
+ The floating menu (`role="menu"`), opened at the pointer. Takes the same
4916
+ props as `Menu.Content`; `placement` is relative to the pointer, and
4917
4917
  flips and clamps to stay on screen. @see MenuContent
4918
4918
 
4919
4919
  - `children`: `ReactNode`. Menu items, groups, separators, and submenus.
@@ -5130,30 +5130,30 @@ import { Menu } from "@vjdesigns/vjdesigns-ui";
5130
5130
 
5131
5131
  Category: navigation · Storybook: Navigation/Menu
5132
5132
 
5133
- A dropdown menu of actions or options, opened from a button (WAI-ARIA APG
5134
- "Menu Button" and "Menu" patterns). Compose it from `Menu.Trigger`,
5135
- `Menu.Content`, `Menu.Item`, `Menu.CheckboxItem`, `Menu.RadioGroup` /
5136
- `Menu.RadioItem`, `Menu.Group` / `Menu.Label`, `Menu.Separator`, and
5137
- submenus (`Menu.Sub`, `Menu.SubTrigger`, `Menu.SubContent`).
5138
-
5139
- Keyboard: Enter, Space, or ArrowDown on the trigger opens the menu and
5140
- focuses the first item (ArrowUp: the last). Inside, ArrowUp/ArrowDown move
5141
- (wrapping), Home/End jump, typing a character jumps to the next matching
5142
- item, Enter/Space activates, ArrowRight opens a submenu and ArrowLeft
5143
- closes it (mirrored in RTL), Escape closes the current level and returns
5144
- focus to its trigger, and Tab closes the whole menu.
5145
-
5146
- Disabled items stay focusable (`aria-disabled="true"`) so keyboard and
5147
- screen reader users can still discover them, but can't be activated.
5148
-
5149
- ```tsx
5150
- <Menu>
5151
- <Menu.Trigger>Actions</Menu.Trigger>
5152
- <Menu.Content>
5153
- <Menu.Item onSelect={edit}>Edit</Menu.Item>
5154
- <Menu.Item tone="danger" onSelect={remove}>Delete</Menu.Item>
5155
- </Menu.Content>
5156
- </Menu>
5133
+ A dropdown menu of actions or options, opened from a button (WAI-ARIA APG
5134
+ "Menu Button" and "Menu" patterns). Compose it from `Menu.Trigger`,
5135
+ `Menu.Content`, `Menu.Item`, `Menu.CheckboxItem`, `Menu.RadioGroup` /
5136
+ `Menu.RadioItem`, `Menu.Group` / `Menu.Label`, `Menu.Separator`, and
5137
+ submenus (`Menu.Sub`, `Menu.SubTrigger`, `Menu.SubContent`).
5138
+
5139
+ Keyboard: Enter, Space, or ArrowDown on the trigger opens the menu and
5140
+ focuses the first item (ArrowUp: the last). Inside, ArrowUp/ArrowDown move
5141
+ (wrapping), Home/End jump, typing a character jumps to the next matching
5142
+ item, Enter/Space activates, ArrowRight opens a submenu and ArrowLeft
5143
+ closes it (mirrored in RTL), Escape closes the current level and returns
5144
+ focus to its trigger, and Tab closes the whole menu.
5145
+
5146
+ Disabled items stay focusable (`aria-disabled="true"`) so keyboard and
5147
+ screen reader users can still discover them, but can't be activated.
5148
+
5149
+ ```tsx
5150
+ <Menu>
5151
+ <Menu.Trigger>Actions</Menu.Trigger>
5152
+ <Menu.Content>
5153
+ <Menu.Item onSelect={edit}>Edit</Menu.Item>
5154
+ <Menu.Item tone="danger" onSelect={remove}>Delete</Menu.Item>
5155
+ </Menu.Content>
5156
+ </Menu>
5157
5157
  ```
5158
5158
 
5159
5159
  ### Props
@@ -5169,8 +5169,8 @@ screen reader users can still discover them, but can't be activated.
5169
5169
 
5170
5170
  Also exported as `MenuTrigger`.
5171
5171
 
5172
- The button that opens the menu. Adds `aria-haspopup="menu"`,
5173
- `aria-expanded`, and `aria-controls`. Pass a single `Button`/`IconButton`
5172
+ The button that opens the menu. Adds `aria-haspopup="menu"`,
5173
+ `aria-expanded`, and `aria-controls`. Pass a single `Button`/`IconButton`
5174
5174
  to use it as-is, or plain content to get a default `Button`.
5175
5175
 
5176
5176
  - `variant`: `ButtonVariant` (default "solid"). Visual style. Inherits from a surrounding `ButtonGroup`.
@@ -6266,27 +6266,27 @@ import { HoverCard } from "@vjdesigns/vjdesigns-ui";
6266
6266
 
6267
6267
  Category: overlays · Storybook: Overlays/HoverCard
6268
6268
 
6269
- A preview card that opens while the pointer rests on a link or button, or
6270
- while it has keyboard focus: a player's profile behind their name, a page
6271
- preview behind a link. Unlike `Tooltip`, the card can hold rich and
6272
- interactive content (links, buttons).
6273
-
6274
- Meets WCAG 1.4.13: the pointer can move onto the card, it stays open
6275
- while hovered or focused, and Escape closes it. Keyboard users get it
6276
- too: focusing the trigger opens it, Tab moves into the card's links, and
6277
- Tab past the last one continues to whatever follows the trigger.
6278
-
6279
- Touch screens have no hover, so the card never opens from a tap (the tap
6280
- follows the link). Treat it as a preview: everything in it must also be
6281
- reachable from the page the trigger leads to.
6282
-
6283
- ```tsx
6284
- <HoverCard>
6285
- <HoverCard.Trigger>
6286
- <Link href="/players/23">Jordan Reyes</Link>
6287
- </HoverCard.Trigger>
6288
- <HoverCard.Content>…profile…</HoverCard.Content>
6289
- </HoverCard>
6269
+ A preview card that opens while the pointer rests on a link or button, or
6270
+ while it has keyboard focus: a player's profile behind their name, a page
6271
+ preview behind a link. Unlike `Tooltip`, the card can hold rich and
6272
+ interactive content (links, buttons).
6273
+
6274
+ Meets WCAG 1.4.13: the pointer can move onto the card, it stays open
6275
+ while hovered or focused, and Escape closes it. Keyboard users get it
6276
+ too: focusing the trigger opens it, Tab moves into the card's links, and
6277
+ Tab past the last one continues to whatever follows the trigger.
6278
+
6279
+ Touch screens have no hover, so the card never opens from a tap (the tap
6280
+ follows the link). Treat it as a preview: everything in it must also be
6281
+ reachable from the page the trigger leads to.
6282
+
6283
+ ```tsx
6284
+ <HoverCard>
6285
+ <HoverCard.Trigger>
6286
+ <Link href="/players/23">Jordan Reyes</Link>
6287
+ </HoverCard.Trigger>
6288
+ <HoverCard.Content>…profile…</HoverCard.Content>
6289
+ </HoverCard>
6290
6290
  ```
6291
6291
 
6292
6292
  ### Props
@@ -6310,7 +6310,7 @@ The link or button that opens the card. Its own role and name are left untouched
6310
6310
 
6311
6311
  Also exported as `HoverCardContent`.
6312
6312
 
6313
- The floating card. Rendered in a portal, positioned against the trigger
6313
+ The floating card. Rendered in a portal, positioned against the trigger
6314
6314
  with flip and viewport clamping, and mounted only while open.
6315
6315
 
6316
6316
  - `placement`: `Placement` (default "bottom"). Preferred side and alignment. Flips to the other side when there isn't room.
@@ -6730,6 +6730,7 @@ is passed.
6730
6730
  - With Icons
6731
6731
  - Loading: The button keeps its width, sets `aria-busy`, ignores clicks, and stays focusable.
6732
6732
  - Disabled
6733
+ - Disabled but focusable: `aria-disabled="true"` instead of `disabled`: the button looks and acts unavailable (clicks are ignored) but stays focusable, so a tooltip or a countdown ("Resend code in 0:24") can still be reached and read.
6733
6734
  - As Router Link: `as` renders your router's link with the button's styling, and its props (here `to`) are typed. While disabled or loading it becomes a plain `<a aria-disabled="true">`, since a router link can't render without a destination.
6734
6735
  - As Link: Passing `href` renders an `<a>` with the same styling. Disabled links drop `href` and get `aria-disabled`.
6735
6736
  - Full Width
@@ -7064,12 +7065,12 @@ any part of it; themes nest (a blue area inside a purple app, a dark panel
7064
7065
  on a light page). Overlays opened inside (Popover, Menu, Modal, Tooltip,
7065
7066
  Toast, ...) render elsewhere in the page but keep this theme. For the whole
7066
7067
  page you can instead set the attributes on `<html>`:
7067
- `<html data-theme="dark" data-brand="purple" data-accent="volt">`.
7068
+ `<html data-theme="dark" data-brand="purple" data-accent="lime">`.
7068
7069
 
7069
7070
  ### Props
7070
7071
 
7071
7072
  - `brand`: `ThemeBrand | BrandColor`. Brand palette for everything inside: buttons, links, focus rings, selection, and indicators all switch at once. Leave unset to inherit. A preset name, or any hex color (`"#e11d48"`) for a custom palette generated in both modes. One hex is used as given in the mode where it meets WCAG AA (dark colors in light mode, light colors in dark mode) and the other mode gets a matching shade; pass `{ light, dark }` to choose both. A color that fails contrast is shifted lighter or darker (same hue) just enough to pass, with a console warning in development.
7072
- - `accent`: `ThemeAccent | BrandColor`. The accent: the energetic highlight on badges, switches, progress bars, sliders, steppers, and active navigation. Leave unset to inherit; with no accent set anywhere, each brand uses its own (a bright shade of its hue). `"volt"` for the original yellow-green, `"brand"` to go back to the brand's own inside an area with another accent, or any hex color for a custom accent. Accents always carry dark text, so a color too dark for that is lightened (same hue), with a console warning in development. An accent set here also holds inside nested brands.
7073
+ - `accent`: `ThemeAccent | BrandColor`. The accent: the energetic highlight on badges, switches, progress bars, sliders, steppers, and active navigation. Leave unset to inherit; with no accent set anywhere, each brand uses its own (a bright shade of its hue). `"lime"` for the original bright yellow-green, `"brand"` to go back to the brand's own inside an area with another accent, or any hex color for a custom accent. Accents always carry dark text, so a color too dark for that is lightened (same hue), with a console warning in development. An accent set here also holds inside nested brands.
7073
7074
  - `mode`: `ThemeMode`. Color mode for everything inside. Leave unset to inherit.
7074
7075
  - `fill`: `boolean` (default false). Paint the page background and text color of the theme on this wrapper (useful when `mode` differs from the page around it).
7075
7076
  - Plus every native `<div>` attribute (className, style, id, aria-*, data-*, event handlers), spread onto the root.
@@ -7084,7 +7085,7 @@ page you can instead set the attributes on `<html>`:
7084
7085
  - Custom colors per mode: `{ light, dark }` picks the color for each mode; each is still checked for contrast.
7085
7086
  - Custom colors in overlays: Overlays opened inside a custom-colored Theme keep the generated palette.
7086
7087
  - Theme builder: Pick any color and see the generated palette in both modes, with code to copy.
7087
- - Accents: The accent (badges, switches, progress, sliders, active navigation) follows the brand by default: a bright shade of its hue, always with dark text. `accent="volt"` restores the original yellow-green, and any hex makes a custom accent (kept accessible: too-dark colors are lightened).
7088
+ - Accents: The accent (badges, switches, progress, sliders, active navigation) follows the brand by default: a bright shade of its hue, always with dark text. `accent="lime"` restores the original bright yellow-green, and any hex makes a custom accent (kept accessible: too-dark colors are lightened).
7088
7089
  - Accent nesting: An explicit accent holds inside nested brands; `accent="brand"` goes back to the nearest brand's own accent. Overlays keep the accent of their area.
7089
7090
 
7090
7091
  ## VisuallyHidden
@@ -7303,13 +7304,13 @@ During server rendering it returns `base` (or `fallback`).
7303
7304
  useDisclosure(initial = false, options: UseDisclosureOptions = {}): [opened: boolean, handlers: DisclosureHandlers]
7304
7305
  ```
7305
7306
 
7306
- Open/closed state for anything that shows and hides (a Modal, Drawer,
7307
- Popover, a collapsible section), with stable handlers.
7308
-
7309
- ```tsx
7310
- const [opened, { open, close }] = useDisclosure();
7311
- <Button onClick={open}>Edit</Button>
7312
- <Modal open={opened} onOpenChange={(next) => (next ? open() : close())}>…</Modal>
7307
+ Open/closed state for anything that shows and hides (a Modal, Drawer,
7308
+ Popover, a collapsible section), with stable handlers.
7309
+
7310
+ ```tsx
7311
+ const [opened, { open, close }] = useDisclosure();
7312
+ <Button onClick={open}>Edit</Button>
7313
+ <Modal open={opened} onOpenChange={(next) => (next ? open() : close())}>…</Modal>
7313
7314
  ```
7314
7315
 
7315
7316
  ## useToggle
@@ -7318,13 +7319,13 @@ const [opened, { open, close }] = useDisclosure();
7318
7319
  useToggle(values: readonly T[] = [false, true] as unknown as readonly T[]): [value: T, toggle: (value?: T) => void]
7319
7320
  ```
7320
7321
 
7321
- Cycles through values: `[false, true]` by default (a boolean toggle), or
7322
- any list (`["list", "grid"]`). `toggle()` moves to the next value (wrapping);
7323
- `toggle(value)` jumps to one.
7324
-
7325
- ```tsx
7326
- const [view, toggleView] = useToggle(["list", "grid"] as const);
7327
- <Button onClick={() => toggleView()}>Show {view === "list" ? "grid" : "list"}</Button>
7322
+ Cycles through values: `[false, true]` by default (a boolean toggle), or
7323
+ any list (`["list", "grid"]`). `toggle()` moves to the next value (wrapping);
7324
+ `toggle(value)` jumps to one.
7325
+
7326
+ ```tsx
7327
+ const [view, toggleView] = useToggle(["list", "grid"] as const);
7328
+ <Button onClick={() => toggleView()}>Show {view === "list" ? "grid" : "list"}</Button>
7328
7329
  ```
7329
7330
 
7330
7331
  ## useDebouncedCallback
@@ -7333,13 +7334,13 @@ const [view, toggleView] = useToggle(["list", "grid"] as const);
7333
7334
  useDebouncedCallback(callback: (...args: A) => void, wait: number, { leading = false, maxWait }: { leading?: boolean; maxWait?: number } = {}): ControlledCallback<A>
7334
7335
  ```
7335
7336
 
7336
- A function that waits until calls stop for `wait` ms, then runs once with
7337
- the last arguments. Always calls the latest `callback`; a pending call is
7338
- dropped on unmount. Use it to save, search, or validate after typing pauses.
7339
-
7340
- ```tsx
7341
- const save = useDebouncedCallback((text: string) => api.saveDraft(text), 500);
7342
- <Textarea onChange={(e) => save(e.target.value)} />
7337
+ A function that waits until calls stop for `wait` ms, then runs once with
7338
+ the last arguments. Always calls the latest `callback`; a pending call is
7339
+ dropped on unmount. Use it to save, search, or validate after typing pauses.
7340
+
7341
+ ```tsx
7342
+ const save = useDebouncedCallback((text: string) => api.saveDraft(text), 500);
7343
+ <Textarea onChange={(e) => save(e.target.value)} />
7343
7344
  ```
7344
7345
 
7345
7346
  ## useThrottledCallback
@@ -7348,12 +7349,12 @@ const save = useDebouncedCallback((text: string) => api.saveDraft(text), 500);
7348
7349
  useThrottledCallback(callback: (...args: A) => void, wait: number): ControlledCallback<A>
7349
7350
  ```
7350
7351
 
7351
- A function that runs at most once every `wait` ms: the first call runs
7352
- right away, and the last call made during the wait runs when it ends.
7353
- Use it for scroll, resize, or pointer-move handlers.
7354
-
7355
- ```tsx
7356
- const track = useThrottledCallback((x: number) => setPosition(x), 100);
7352
+ A function that runs at most once every `wait` ms: the first call runs
7353
+ right away, and the last call made during the wait runs when it ends.
7354
+ Use it for scroll, resize, or pointer-move handlers.
7355
+
7356
+ ```tsx
7357
+ const track = useThrottledCallback((x: number) => setPosition(x), 100);
7357
7358
  ```
7358
7359
 
7359
7360
  ## useDebouncedValue
@@ -7362,13 +7363,13 @@ const track = useThrottledCallback((x: number) => setPosition(x), 100);
7362
7363
  useDebouncedValue(value: T, wait: number): [debounced: T, controls: { cancel: () => void; flush: () => void }]
7363
7364
  ```
7364
7365
 
7365
- The value, updated only after it stops changing for `wait` ms. Use it to
7366
- search or fetch as someone types, without a request per keystroke.
7367
-
7368
- ```tsx
7369
- const [query, setQuery] = useState("");
7370
- const [debouncedQuery] = useDebouncedValue(query, 300);
7371
- useEffect(() => { search(debouncedQuery); }, [debouncedQuery]);
7366
+ The value, updated only after it stops changing for `wait` ms. Use it to
7367
+ search or fetch as someone types, without a request per keystroke.
7368
+
7369
+ ```tsx
7370
+ const [query, setQuery] = useState("");
7371
+ const [debouncedQuery] = useDebouncedValue(query, 300);
7372
+ useEffect(() => { search(debouncedQuery); }, [debouncedQuery]);
7372
7373
  ```
7373
7374
 
7374
7375
  ## useInterval
@@ -7377,12 +7378,12 @@ useEffect(() => { search(debouncedQuery); }, [debouncedQuery]);
7377
7378
  useInterval(callback: () => void, delay: number | null): void
7378
7379
  ```
7379
7380
 
7380
- Calls `callback` every `delay` ms while `delay` is a number; pass `null`
7381
- to pause. Always calls the latest `callback`, and stops on unmount.
7382
-
7383
- ```tsx
7384
- const [running, setRunning] = useState(true);
7385
- useInterval(() => setSeconds((s) => s + 1), running ? 1000 : null);
7381
+ Calls `callback` every `delay` ms while `delay` is a number; pass `null`
7382
+ to pause. Always calls the latest `callback`, and stops on unmount.
7383
+
7384
+ ```tsx
7385
+ const [running, setRunning] = useState(true);
7386
+ useInterval(() => setSeconds((s) => s + 1), running ? 1000 : null);
7386
7387
  ```
7387
7388
 
7388
7389
  ## useTimeout
@@ -7391,12 +7392,12 @@ useInterval(() => setSeconds((s) => s + 1), running ? 1000 : null);
7391
7392
  useTimeout(callback: () => void, delay: number | null): { clear: () => void; reset: () => void }
7392
7393
  ```
7393
7394
 
7394
- Calls `callback` once, `delay` ms after mount (or after `delay` changes);
7395
- pass `null` to not schedule it. Returns `clear` and `reset` (restart the
7396
- wait).
7397
-
7398
- ```tsx
7399
- const { reset } = useTimeout(() => setSaved(false), 2000);
7395
+ Calls `callback` once, `delay` ms after mount (or after `delay` changes);
7396
+ pass `null` to not schedule it. Returns `clear` and `reset` (restart the
7397
+ wait).
7398
+
7399
+ ```tsx
7400
+ const { reset } = useTimeout(() => setSaved(false), 2000);
7400
7401
  ```
7401
7402
 
7402
7403
  ## usePrevious
@@ -7405,10 +7406,10 @@ const { reset } = useTimeout(() => setSaved(false), 2000);
7405
7406
  usePrevious(value: T): T | undefined
7406
7407
  ```
7407
7408
 
7408
- The value from before its last change (undefined until it first changes).
7409
-
7410
- ```tsx
7411
- const previousCount = usePrevious(count);
7409
+ The value from before its last change (undefined until it first changes).
7410
+
7411
+ ```tsx
7412
+ const previousCount = usePrevious(count);
7412
7413
  ```
7413
7414
 
7414
7415
  ## useLocalStorage
@@ -7417,13 +7418,13 @@ const previousCount = usePrevious(count);
7417
7418
  useLocalStorage(key: string, defaultValue: T, options?: UseStorageOptions<T>)
7418
7419
  ```
7419
7420
 
7420
- State saved in `localStorage` under `key`: it survives reloads and stays in
7421
- sync across every component using the key and across browser tabs. Values
7422
- are JSON by default. Returns `defaultValue` during server rendering, when
7423
- nothing is stored, or when storage is blocked (it never throws).
7424
-
7425
- ```tsx
7426
- const [density, setDensity] = useLocalStorage("table-density", "comfortable");
7421
+ State saved in `localStorage` under `key`: it survives reloads and stays in
7422
+ sync across every component using the key and across browser tabs. Values
7423
+ are JSON by default. Returns `defaultValue` during server rendering, when
7424
+ nothing is stored, or when storage is blocked (it never throws).
7425
+
7426
+ ```tsx
7427
+ const [density, setDensity] = useLocalStorage("table-density", "comfortable");
7427
7428
  ```
7428
7429
 
7429
7430
  ## useSessionStorage
@@ -7440,14 +7441,14 @@ Like `useLocalStorage`, in `sessionStorage`: kept until the tab closes.
7440
7441
  useClipboard({ timeout = 2000 }: { timeout?: number } = {}): UseClipboardResult
7441
7442
  ```
7442
7443
 
7443
- Copies text to the clipboard, with a `copied` flag that resets after
7444
- `timeout` ms. Uses the Clipboard API, with a fallback for browsers or
7445
- contexts that block it. Pair a visible "Copied" change with an
7446
- announcement (`announce`) so screen readers hear it too.
7447
-
7448
- ```tsx
7449
- const { copy, copied } = useClipboard();
7450
- <Button onClick={() => copy(inviteLink)}>{copied ? "Copied" : "Copy link"}</Button>
7444
+ Copies text to the clipboard, with a `copied` flag that resets after
7445
+ `timeout` ms. Uses the Clipboard API, with a fallback for browsers or
7446
+ contexts that block it. Pair a visible "Copied" change with an
7447
+ announcement (`announce`) so screen readers hear it too.
7448
+
7449
+ ```tsx
7450
+ const { copy, copied } = useClipboard();
7451
+ <Button onClick={() => copy(inviteLink)}>{copied ? "Copied" : "Copy link"}</Button>
7451
7452
  ```
7452
7453
 
7453
7454
  ## useHotkeys
@@ -7456,19 +7457,19 @@ const { copy, copied } = useClipboard();
7456
7457
  useHotkeys(hotkeys: HotkeyItem[], { enabled = true, target }: UseHotkeysOptions = {}): void
7457
7458
  ```
7458
7459
 
7459
- Keyboard shortcuts for the whole page (or one element). `mod` is ⌘ on Mac
7460
- and Ctrl elsewhere; separate alternatives with commas (`"mod+k, /"`).
7461
- Shortcuts with ⌘, Ctrl, or Alt work everywhere; plain keys don't fire while
7462
- typing in a text field. Show each shortcut to users (`Kbd`,
7463
- `aria-keyshortcuts`) so it can be found, and avoid single-key shortcuts that
7464
- screen readers or browsers use, or let users turn them off (WCAG 2.1.4).
7465
-
7466
- ```tsx
7467
- useHotkeys([
7468
- ["mod+k", () => setPaletteOpen(true)],
7469
- ["/", () => searchRef.current?.focus()],
7470
- ["shift+?", () => setHelpOpen(true)],
7471
- ]);
7460
+ Keyboard shortcuts for the whole page (or one element). `mod` is ⌘ on Mac
7461
+ and Ctrl elsewhere; separate alternatives with commas (`"mod+k, /"`).
7462
+ Shortcuts with ⌘, Ctrl, or Alt work everywhere; plain keys don't fire while
7463
+ typing in a text field. Show each shortcut to users (`Kbd`,
7464
+ `aria-keyshortcuts`) so it can be found, and avoid single-key shortcuts that
7465
+ screen readers or browsers use, or let users turn them off (WCAG 2.1.4).
7466
+
7467
+ ```tsx
7468
+ useHotkeys([
7469
+ ["mod+k", () => setPaletteOpen(true)],
7470
+ ["/", () => searchRef.current?.focus()],
7471
+ ["shift+?", () => setHelpOpen(true)],
7472
+ ]);
7472
7473
  ```
7473
7474
 
7474
7475
  ## useEventListener
@@ -7477,13 +7478,13 @@ useHotkeys([
7477
7478
  useEventListener(type: K, handler: (event: WindowEventMap[K]) => void, target?: ListenerTarget | RefObject<HTMLElement | null>, options?: boolean | AddEventListenerOptions): void
7478
7479
  ```
7479
7480
 
7480
- Adds an event listener and removes it on unmount. Always calls the latest
7481
- `handler` (no re-subscribing when it changes). `target` defaults to
7482
- `window`; pass an element, `document`, or a ref.
7483
-
7484
- ```tsx
7485
- useEventListener("beforeunload", (e) => { if (dirty) e.preventDefault(); });
7486
- useEventListener("scroll", onScroll, panelRef, { passive: true });
7481
+ Adds an event listener and removes it on unmount. Always calls the latest
7482
+ `handler` (no re-subscribing when it changes). `target` defaults to
7483
+ `window`; pass an element, `document`, or a ref.
7484
+
7485
+ ```tsx
7486
+ useEventListener("beforeunload", (e) => { if (dirty) e.preventDefault(); });
7487
+ useEventListener("scroll", onScroll, panelRef, { passive: true });
7487
7488
  ```
7488
7489
 
7489
7490
  ## useClickOutside
@@ -7492,14 +7493,14 @@ useEventListener("scroll", onScroll, panelRef, { passive: true });
7492
7493
  useClickOutside(handler: (event: Event) => void, { enabled = true, ignore = [] }: { enabled?: boolean; ignore?: Array<RefObject<HTMLElement | null> | HTMLElement | null> } = {}): RefObject<T | null>
7493
7494
  ```
7494
7495
 
7495
- Calls `handler` when a pointer press or a focus move lands outside the
7496
- returned ref's element (and outside any `ignore` elements). Use it for
7497
- custom popups; the library's own overlays already close on outside
7498
- presses, through the shared layer stack.
7499
-
7500
- ```tsx
7501
- const ref = useClickOutside<HTMLDivElement>(() => setOpen(false));
7502
- <div ref={ref}>…</div>
7496
+ Calls `handler` when a pointer press or a focus move lands outside the
7497
+ returned ref's element (and outside any `ignore` elements). Use it for
7498
+ custom popups; the library's own overlays already close on outside
7499
+ presses, through the shared layer stack.
7500
+
7501
+ ```tsx
7502
+ const ref = useClickOutside<HTMLDivElement>(() => setOpen(false));
7503
+ <div ref={ref}>…</div>
7503
7504
  ```
7504
7505
 
7505
7506
  ## useIntersection
@@ -7508,13 +7509,13 @@ const ref = useClickOutside<HTMLDivElement>(() => setOpen(false));
7508
7509
  useIntersection({ root, rootMargin, threshold = 0, once = false, }: UseIntersectionOptions = {}): { ref: (node: T | null) => void; entry: IntersectionObserverEntry | null; isIntersecting: boolean }
7509
7510
  ```
7510
7511
 
7511
- Whether an element is in view (IntersectionObserver). Attach the returned
7512
- `ref`; read `isIntersecting` or the full `entry`. Returns false during
7513
- server rendering.
7514
-
7515
- ```tsx
7516
- const { ref, isIntersecting } = useIntersection<HTMLDivElement>({ rootMargin: "200px", once: true });
7517
- <div ref={ref}>{isIntersecting && <HeavyChart />}</div>
7512
+ Whether an element is in view (IntersectionObserver). Attach the returned
7513
+ `ref`; read `isIntersecting` or the full `entry`. Returns false during
7514
+ server rendering.
7515
+
7516
+ ```tsx
7517
+ const { ref, isIntersecting } = useIntersection<HTMLDivElement>({ rootMargin: "200px", once: true });
7518
+ <div ref={ref}>{isIntersecting && <HeavyChart />}</div>
7518
7519
  ```
7519
7520
 
7520
7521
  ## useElementSize
@@ -7523,13 +7524,13 @@ const { ref, isIntersecting } = useIntersection<HTMLDivElement>({ rootMargin: "2
7523
7524
  useElementSize(): { ref: (node: T | null) => void; width: number; height: number; }
7524
7525
  ```
7525
7526
 
7526
- An element's content-box size, kept current as it resizes
7527
- (ResizeObserver). Attach the returned `ref`. Both are 0 until measured
7528
- (and during server rendering).
7529
-
7530
- ```tsx
7531
- const { ref, width } = useElementSize<HTMLDivElement>();
7532
- <div ref={ref}>{width > 600 ? <WideLayout /> : <NarrowLayout />}</div>
7527
+ An element's content-box size, kept current as it resizes
7528
+ (ResizeObserver). Attach the returned `ref`. Both are 0 until measured
7529
+ (and during server rendering).
7530
+
7531
+ ```tsx
7532
+ const { ref, width } = useElementSize<HTMLDivElement>();
7533
+ <div ref={ref}>{width > 600 ? <WideLayout /> : <NarrowLayout />}</div>
7533
7534
  ```
7534
7535
 
7535
7536
  ## useHydrated
@@ -7538,13 +7539,13 @@ const { ref, width } = useElementSize<HTMLDivElement>();
7538
7539
  useHydrated(): boolean
7539
7540
  ```
7540
7541
 
7541
- False during server rendering and hydration, true after. Use it to render
7542
- browser-only content (dates in the user's time zone, `window` values)
7543
- without a hydration mismatch.
7544
-
7545
- ```tsx
7546
- const hydrated = useHydrated();
7547
- return <span>{hydrated ? new Date().toLocaleTimeString() : null}</span>;
7542
+ False during server rendering and hydration, true after. Use it to render
7543
+ browser-only content (dates in the user's time zone, `window` values)
7544
+ without a hydration mismatch.
7545
+
7546
+ ```tsx
7547
+ const hydrated = useHydrated();
7548
+ return <span>{hydrated ? new Date().toLocaleTimeString() : null}</span>;
7548
7549
  ```
7549
7550
 
7550
7551
  ## useReducedMotion
@@ -7553,8 +7554,8 @@ return <span>{hydrated ? new Date().toLocaleTimeString() : null}</span>;
7553
7554
  useReducedMotion(): boolean
7554
7555
  ```
7555
7556
 
7556
- True when the user asked the system to reduce motion. The library's own
7557
- transitions already respect it (through the duration tokens); use this for
7557
+ True when the user asked the system to reduce motion. The library's own
7558
+ transitions already respect it (through the duration tokens); use this for
7558
7559
  motion you drive from JavaScript (autoplay, scroll animations).
7559
7560
 
7560
7561
  ## useColorScheme
@@ -7563,7 +7564,7 @@ motion you drive from JavaScript (autoplay, scroll animations).
7563
7564
  useColorScheme(): "light" | "dark"
7564
7565
  ```
7565
7566
 
7566
- The system color scheme, `"light"` or `"dark"`, kept in sync (`"light"`
7567
+ The system color scheme, `"light"` or `"dark"`, kept in sync (`"light"`
7567
7568
  during server rendering). To follow it, pass it to `Theme mode`.
7568
7569
 
7569
7570
  ## useOnline
@@ -7572,8 +7573,8 @@ during server rendering). To follow it, pass it to `Theme mode`.
7572
7573
  useOnline(): boolean
7573
7574
  ```
7574
7575
 
7575
- Whether the browser has a network connection, kept in sync (true during
7576
- server rendering). Pair a visible change (a Banner) with a polite
7576
+ Whether the browser has a network connection, kept in sync (true during
7577
+ server rendering). Pair a visible change (a Banner) with a polite
7577
7578
  announcement when it goes offline.
7578
7579
 
7579
7580
  ## useDocumentVisibility
@@ -7582,7 +7583,7 @@ announcement when it goes offline.
7582
7583
  useDocumentVisibility(): DocumentVisibilityState
7583
7584
  ```
7584
7585
 
7585
- `"visible"` or `"hidden"` (another tab, minimized window), kept in sync.
7586
+ `"visible"` or `"hidden"` (another tab, minimized window), kept in sync.
7586
7587
  Use it to pause polling, video, or animations in the background.
7587
7588
 
7588
7589
  ## useViewportSize
@@ -7591,8 +7592,8 @@ Use it to pause polling, video, or animations in the background.
7591
7592
  useViewportSize(): { width: number; height: number }
7592
7593
  ```
7593
7594
 
7594
- The viewport's width and height in CSS pixels, kept in sync (0 × 0 during
7595
- server rendering). For layout decisions, prefer CSS or `useMediaQuery`;
7595
+ The viewport's width and height in CSS pixels, kept in sync (0 × 0 during
7596
+ server rendering). For layout decisions, prefer CSS or `useMediaQuery`;
7596
7597
  use this when you need the numbers.
7597
7598
 
7598
7599
  ## useVirtualizer
@@ -7601,27 +7602,27 @@ use this when you need the numbers.
7601
7602
  useVirtualizer({ count, scrollElement, estimateSize, overscan = 6, gap = 0, paddingStart = 0, paddingEnd = 0, initialCount = 10, }: UseVirtualizerOptions): Virtualizer
7602
7603
  ```
7603
7604
 
7604
- Headless list virtualization: renders only the items in view (plus
7605
- `overscan`), so lists of 100,000 rows stay fast. Works with variable
7606
- heights by measuring rendered items, and is SSR-safe (the server renders
7607
- the first `initialCount` items). `VirtualList` is built on it; use the
7608
- hook directly for custom layouts.
7609
-
7610
- ```tsx
7611
- const scrollRef = useRef<HTMLDivElement>(null);
7612
- const { virtualItems, totalSize, measureElement } = useVirtualizer({
7613
- count: rows.length, scrollElement: scrollRef, estimateSize: 48,
7614
- });
7615
- <div ref={scrollRef} style={{ overflow: "auto", height: 400 }}>
7616
- <div style={{ position: "relative", height: totalSize }}>
7617
- {virtualItems.map((item) => (
7618
- <div key={item.index} data-index={item.index} ref={measureElement}
7619
- style={{ position: "absolute", top: 0, width: "100%", transform: `translateY(${item.start}px)` }}>
7620
- {rows[item.index].name}
7621
- </div>
7622
- ))}
7623
- </div>
7624
- </div>
7605
+ Headless list virtualization: renders only the items in view (plus
7606
+ `overscan`), so lists of 100,000 rows stay fast. Works with variable
7607
+ heights by measuring rendered items, and is SSR-safe (the server renders
7608
+ the first `initialCount` items). `VirtualList` is built on it; use the
7609
+ hook directly for custom layouts.
7610
+
7611
+ ```tsx
7612
+ const scrollRef = useRef<HTMLDivElement>(null);
7613
+ const { virtualItems, totalSize, measureElement } = useVirtualizer({
7614
+ count: rows.length, scrollElement: scrollRef, estimateSize: 48,
7615
+ });
7616
+ <div ref={scrollRef} style={{ overflow: "auto", height: 400 }}>
7617
+ <div style={{ position: "relative", height: totalSize }}>
7618
+ {virtualItems.map((item) => (
7619
+ <div key={item.index} data-index={item.index} ref={measureElement}
7620
+ style={{ position: "absolute", top: 0, width: "100%", transform: `translateY(${item.start}px)` }}>
7621
+ {rows[item.index].name}
7622
+ </div>
7623
+ ))}
7624
+ </div>
7625
+ </div>
7625
7626
  ```
7626
7627
 
7627
7628
  ## useFillSlot
@@ -7648,7 +7649,7 @@ Pixel widths for each breakpoint, for use in JS.
7648
7649
  getLocaleDirection(locale: string): "ltr" | "rtl"
7649
7650
  ```
7650
7651
 
7651
- The writing direction of a BCP 47 locale: `"rtl"` for Arabic, Hebrew,
7652
+ The writing direction of a BCP 47 locale: `"rtl"` for Arabic, Hebrew,
7652
7653
  Persian, Urdu, and other right-to-left languages and scripts, else `"ltr"`.
7653
7654
 
7654
7655
  ## plural
@@ -7657,11 +7658,11 @@ Persian, Urdu, and other right-to-left languages and scripts, else `"ltr"`.
7657
7658
  plural(locale: string): PluralFn
7658
7659
  ```
7659
7660
 
7660
- Plural selector bound to a locale:
7661
-
7662
- ```ts
7663
- const p = plural("ru");
7664
- p(3, { one: "# файл", few: "# файла", many: "# файлов", other: "# файла" }); // "3 файла"
7661
+ Plural selector bound to a locale:
7662
+
7663
+ ```ts
7664
+ const p = plural("ru");
7665
+ p(3, { one: "# файл", few: "# файла", many: "# файлов", other: "# файла" }); // "3 файла"
7665
7666
  ```
7666
7667
 
7667
7668
  ## en
@@ -7678,7 +7679,7 @@ English: the default messages, and the template for every other locale.
7678
7679
  mergeMessages(base: Messages, override: PartialMessages | undefined): Messages
7679
7680
  ```
7680
7681
 
7681
- Overlays partial messages on a complete set: missing strings keep the
7682
+ Overlays partial messages on a complete set: missing strings keep the
7682
7683
  base value. Functions and arrays replace whole.
7683
7684
 
7684
7685
  ## resolveElement
@@ -7822,12 +7823,12 @@ Locks page scrolling. Returns a function that releases this lock (safe to call t
7822
7823
  getHotkeyHandler(hotkeys: HotkeyItem[])
7823
7824
  ```
7824
7825
 
7825
- A `keydown` handler for one element (an editor, a list): pass it to
7826
- `onKeyDown`. Same shortcut format as `useHotkeys`; shortcuts fire while
7827
- typing in that element.
7828
-
7829
- ```tsx
7830
- <Textarea onKeyDown={getHotkeyHandler([["mod+enter", send]])} />
7826
+ A `keydown` handler for one element (an editor, a list): pass it to
7827
+ `onKeyDown`. Same shortcut format as `useHotkeys`; shortcuts fire while
7828
+ typing in that element.
7829
+
7830
+ ```tsx
7831
+ <Textarea onKeyDown={getHotkeyHandler([["mod+enter", send]])} />
7831
7832
  ```
7832
7833
 
7833
7834
  ## cx
@@ -8029,13 +8030,21 @@ createBrandPalette(color: BrandColor): BrandPalette
8029
8030
  Builds an accessible brand palette, for both modes, from any hex color.
8030
8031
  Throws a TypeError for anything that isn't `#rgb` or `#rrggbb`.
8031
8032
 
8033
+ ## LIME_ACCENT
8034
+
8035
+ ```ts
8036
+ LIME_ACCENT: AccentPalette
8037
+ ```
8038
+
8039
+ The lime accent (`accent="lime"`, and the black brand's accent): the original bright yellow-green.
8040
+
8032
8041
  ## VOLT_ACCENT
8033
8042
 
8034
8043
  ```ts
8035
- VOLT_ACCENT: AccentPalette
8044
+ VOLT_ACCENT
8036
8045
  ```
8037
8046
 
8038
- The original volt accent (`accent="volt"`, and the black brand's accent).
8047
+ @deprecated Renamed to `LIME_ACCENT`.
8039
8048
 
8040
8049
  ## createAccentPalette
8041
8050
 
@@ -8056,7 +8065,7 @@ brandAccent(primary: string): AccentPalette
8056
8065
 
8057
8066
  The default accent of a brand: a bright, lighter shade of its primary
8058
8067
  hue (blue gets a sky blue, purple a lavender, gold an amber, green a
8059
- mint). Colorless brands (black) keep the volt.
8068
+ mint). Colorless brands (black) keep the lime.
8060
8069
 
8061
8070
  ## accentPaletteVars
8062
8071
 
@@ -8108,9 +8117,9 @@ Then use `<html data-brand="acme">`. Names are letters, digits, and dashes.
8108
8117
  isEmail(value: string, { requireTld = true }: EmailOptions = {}): boolean
8109
8118
  ```
8110
8119
 
8111
- True for an email address in the usual format: the HTML spec's rules
8112
- (what browsers check for `type="email"`), plus no leading, trailing, or
8113
- doubled dots before the `@`, length limits (64 before the `@`, 254 in all),
8120
+ True for an email address in the usual format: the HTML spec's rules
8121
+ (what browsers check for `type="email"`), plus no leading, trailing, or
8122
+ doubled dots before the `@`, length limits (64 before the `@`, 254 in all),
8114
8123
  and, by default, a top-level domain.
8115
8124
 
8116
8125
  ## isIPv4
@@ -8119,7 +8128,7 @@ and, by default, a top-level domain.
8119
8128
  isIPv4(value: string): boolean
8120
8129
  ```
8121
8130
 
8122
- True for a dotted IPv4 address: four numbers 0-255 (`192.168.0.1`), no
8131
+ True for a dotted IPv4 address: four numbers 0-255 (`192.168.0.1`), no
8123
8132
  leading zeros (`010.0.0.1` is ambiguous, so it's rejected).
8124
8133
 
8125
8134
  ## isIPv6
@@ -8128,9 +8137,9 @@ leading zeros (`010.0.0.1` is ambiguous, so it's rejected).
8128
8137
  isIPv6(value: string): boolean
8129
8138
  ```
8130
8139
 
8131
- True for an IPv6 address: eight groups of 1-4 hex digits, with `::`
8132
- standing for one or more zero groups (once at most), and an optional
8133
- IPv4 tail (`::ffff:192.168.0.1`). Zone ids (`%eth0`) and brackets aren't
8140
+ True for an IPv6 address: eight groups of 1-4 hex digits, with `::`
8141
+ standing for one or more zero groups (once at most), and an optional
8142
+ IPv4 tail (`::ffff:192.168.0.1`). Zone ids (`%eth0`) and brackets aren't
8134
8143
  accepted.
8135
8144
 
8136
8145
  ## isIP
@@ -8163,10 +8172,10 @@ True when a value counts as empty (nothing, blank text, or an empty file input).
8163
8172
  rules
8164
8173
  ```
8165
8174
 
8166
- Rule helpers for `useFormValidation`, with messages from the locale:
8167
-
8168
- ```ts
8169
- useFormValidation({ rules: { email: rules.email(), server: rules.ip({ version: 4 }) } });
8175
+ Rule helpers for `useFormValidation`, with messages from the locale:
8176
+
8177
+ ```ts
8178
+ useFormValidation({ rules: { email: rules.email(), server: rules.ip({ version: 4 }) } });
8170
8179
  ```
8171
8180
 
8172
8181
  ## detectPlatform
@@ -8183,8 +8192,8 @@ The current platform from the browser (`"windows"` during server rendering).
8183
8192
  parseHotkey(hotkey: string, platform: HotkeyPlatform = detectPlatform()): ParsedHotkey | null
8184
8193
  ```
8185
8194
 
8186
- Parses a shortcut like `"mod+k"`, `"shift+mod+p"`, `"ctrl+/"`, or
8187
- `"escape"` into modifiers and a key. `mod` is ⌘ on Mac and Ctrl
8195
+ Parses a shortcut like `"mod+k"`, `"shift+mod+p"`, `"ctrl+/"`, or
8196
+ `"escape"` into modifiers and a key. `mod` is ⌘ on Mac and Ctrl
8188
8197
  elsewhere. Returns null when there is no key.
8189
8198
 
8190
8199
  ## matchesHotkey
@@ -8217,6 +8226,6 @@ True when the element accepts typed text, so a global hotkey must not steal its
8217
8226
  copyToClipboard(text: string): Promise<void>
8218
8227
  ```
8219
8228
 
8220
- Copies text to the clipboard: the async Clipboard API when it's allowed,
8221
- else a hidden-textarea fallback (insecure contexts, denied permission,
8229
+ Copies text to the clipboard: the async Clipboard API when it's allowed,
8230
+ else a hidden-textarea fallback (insecure contexts, denied permission,
8222
8231
  older browsers). Focus returns to where it was. Rejects when both fail.