@santi020k/lumen-react 3.0.1 → 4.1.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 (145) hide show
  1. package/README.md +692 -13
  2. package/dist/ai-surfaces.d.ts +49 -0
  3. package/dist/ai-surfaces.d.ts.map +1 -0
  4. package/dist/ai-surfaces.js +88 -0
  5. package/dist/amount-field.d.ts +11 -0
  6. package/dist/amount-field.d.ts.map +1 -0
  7. package/dist/amount-field.js +37 -0
  8. package/dist/attachments.d.ts +17 -0
  9. package/dist/attachments.d.ts.map +1 -0
  10. package/dist/attachments.js +23 -0
  11. package/dist/bullet-chart.d.ts +14 -0
  12. package/dist/bullet-chart.d.ts.map +1 -0
  13. package/dist/bullet-chart.js +22 -0
  14. package/dist/change-summary.d.ts +14 -0
  15. package/dist/change-summary.d.ts.map +1 -0
  16. package/dist/change-summary.js +4 -0
  17. package/dist/chart-inspection.d.ts +10 -0
  18. package/dist/chart-inspection.d.ts.map +1 -0
  19. package/dist/chart-inspection.js +11 -0
  20. package/dist/chart-interaction.d.ts +17 -0
  21. package/dist/chart-interaction.d.ts.map +1 -0
  22. package/dist/chart-interaction.js +50 -0
  23. package/dist/chart-label.d.ts +3 -0
  24. package/dist/chart-label.d.ts.map +1 -0
  25. package/dist/chart-label.js +1 -0
  26. package/dist/chart-recipes.d.ts +8 -0
  27. package/dist/chart-recipes.d.ts.map +1 -1
  28. package/dist/chart-recipes.js +35 -0
  29. package/dist/combobox.d.ts +9 -0
  30. package/dist/combobox.d.ts.map +1 -0
  31. package/dist/combobox.js +137 -0
  32. package/dist/comparison-chart.d.ts +13 -0
  33. package/dist/comparison-chart.d.ts.map +1 -0
  34. package/dist/comparison-chart.js +18 -0
  35. package/dist/components.d.ts +106 -70
  36. package/dist/components.d.ts.map +1 -1
  37. package/dist/components.js +4 -1949
  38. package/dist/dashboard.d.ts +20 -0
  39. package/dist/dashboard.d.ts.map +1 -0
  40. package/dist/dashboard.js +19 -0
  41. package/dist/data-table-range-model.d.ts +8 -0
  42. package/dist/data-table-range-model.d.ts.map +1 -0
  43. package/dist/data-table-range-model.js +31 -0
  44. package/dist/data-table-range.d.ts +17 -0
  45. package/dist/data-table-range.d.ts.map +1 -0
  46. package/dist/data-table-range.js +8 -0
  47. package/dist/data-table-saved-views.d.ts +30 -0
  48. package/dist/data-table-saved-views.d.ts.map +1 -0
  49. package/dist/data-table-saved-views.js +49 -0
  50. package/dist/data-table-selection-model.d.ts +23 -0
  51. package/dist/data-table-selection-model.d.ts.map +1 -0
  52. package/dist/data-table-selection-model.js +36 -0
  53. package/dist/data-table-selection.d.ts +11 -0
  54. package/dist/data-table-selection.d.ts.map +1 -0
  55. package/dist/data-table-selection.js +10 -0
  56. package/dist/data-table-state.d.ts +6 -0
  57. package/dist/data-table-state.d.ts.map +1 -0
  58. package/dist/data-table-state.js +38 -0
  59. package/dist/data-table-view.d.ts +88 -0
  60. package/dist/data-table-view.d.ts.map +1 -0
  61. package/dist/data-table-view.js +1 -0
  62. package/dist/data-table.d.ts +60 -0
  63. package/dist/data-table.d.ts.map +1 -0
  64. package/dist/data-table.js +1 -0
  65. package/dist/date-range-calendar.d.ts +29 -0
  66. package/dist/date-range-calendar.d.ts.map +1 -0
  67. package/dist/date-range-calendar.js +69 -0
  68. package/dist/date-range-input.d.ts +25 -0
  69. package/dist/date-range-input.d.ts.map +1 -0
  70. package/dist/date-range-input.js +195 -0
  71. package/dist/device-frame.d.ts +13 -0
  72. package/dist/device-frame.d.ts.map +1 -0
  73. package/dist/device-frame.js +23 -0
  74. package/dist/dialog-lifecycle.d.ts +3 -0
  75. package/dist/dialog-lifecycle.d.ts.map +1 -0
  76. package/dist/dialog-lifecycle.js +70 -0
  77. package/dist/expanded-charts.d.ts +26 -0
  78. package/dist/expanded-charts.d.ts.map +1 -0
  79. package/dist/expanded-charts.js +46 -0
  80. package/dist/floating-panel.d.ts +12 -0
  81. package/dist/floating-panel.d.ts.map +1 -0
  82. package/dist/floating-panel.js +1 -0
  83. package/dist/form-workflow.d.ts +77 -0
  84. package/dist/form-workflow.d.ts.map +1 -0
  85. package/dist/form-workflow.js +337 -0
  86. package/dist/hooks.d.ts +24 -21
  87. package/dist/hooks.d.ts.map +1 -1
  88. package/dist/hooks.js +1 -2468
  89. package/dist/image-comparison.d.ts +20 -0
  90. package/dist/image-comparison.d.ts.map +1 -0
  91. package/dist/image-comparison.js +55 -0
  92. package/dist/index.d.ts +25 -1
  93. package/dist/index.d.ts.map +1 -1
  94. package/dist/index.js +24 -0
  95. package/dist/interval-charts.d.ts +21 -0
  96. package/dist/interval-charts.d.ts.map +1 -0
  97. package/dist/interval-charts.js +23 -0
  98. package/dist/media-selection.d.ts +18 -0
  99. package/dist/media-selection.d.ts.map +1 -0
  100. package/dist/media-selection.js +12 -0
  101. package/dist/media-viewport.d.ts +16 -0
  102. package/dist/media-viewport.d.ts.map +1 -0
  103. package/dist/media-viewport.js +37 -0
  104. package/dist/motion.d.ts +597 -0
  105. package/dist/motion.d.ts.map +1 -0
  106. package/dist/motion.js +22 -0
  107. package/dist/review-workflow.d.ts +32 -0
  108. package/dist/review-workflow.d.ts.map +1 -0
  109. package/dist/review-workflow.js +76 -0
  110. package/dist/rich-text-editor.d.ts +21 -0
  111. package/dist/rich-text-editor.d.ts.map +1 -0
  112. package/dist/rich-text-editor.js +143 -0
  113. package/dist/select-form.d.ts +3 -0
  114. package/dist/select-form.d.ts.map +1 -0
  115. package/dist/select-form.js +1 -0
  116. package/dist/server-components.d.ts +33 -6
  117. package/dist/server-components.d.ts.map +1 -1
  118. package/dist/server-components.js +26 -10
  119. package/dist/server.d.ts +1 -0
  120. package/dist/server.d.ts.map +1 -1
  121. package/dist/server.js +1 -0
  122. package/dist/toast-context.d.ts +4 -0
  123. package/dist/toast-context.d.ts.map +1 -0
  124. package/dist/toast-context.js +10 -0
  125. package/dist/toast-provider.d.ts +3 -0
  126. package/dist/toast-provider.d.ts.map +1 -0
  127. package/dist/toast-provider.js +3 -0
  128. package/dist/use-copy-feedback.d.ts +17 -0
  129. package/dist/use-copy-feedback.d.ts.map +1 -0
  130. package/dist/use-copy-feedback.js +54 -0
  131. package/dist/virtual-list-data.d.ts +13 -0
  132. package/dist/virtual-list-data.d.ts.map +1 -0
  133. package/dist/virtual-list-data.js +87 -0
  134. package/dist/virtual-list.d.ts +14 -0
  135. package/dist/virtual-list.d.ts.map +1 -0
  136. package/dist/virtual-list.js +24 -0
  137. package/dist/visual-interactions.d.ts +16 -0
  138. package/dist/visual-interactions.d.ts.map +1 -0
  139. package/dist/visual-interactions.js +30 -0
  140. package/dist/world-map.d.ts +26 -0
  141. package/dist/world-map.d.ts.map +1 -0
  142. package/dist/world-map.js +81 -0
  143. package/package.json +99 -11
  144. package/styles/critical.css +696 -66
  145. package/styles/data-table-view.css +36 -0
package/README.md CHANGED
@@ -1,12 +1,91 @@
1
- # @santi020k/lumen-react
2
-
3
- React primitives for Lumen UI.
4
-
5
- This package provides React components for the shared Lumen primitive catalog using the standalone
6
- Lumen stylesheet.
1
+ <p align="center">
2
+ <a href="https://lumen.santi020k.com">
3
+ <picture>
4
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/santi020k/lumen/main/docs/assets/readme/package-dark.svg">
5
+ <img src="https://raw.githubusercontent.com/santi020k/lumen/main/docs/assets/readme/package-light.svg" alt="Lumen UI — Web. Native. Thoughtfully connected." width="1200" height="184">
6
+ </picture>
7
+ </a>
8
+ </p>
9
+
10
+ <h1 align="center">Lumen UI · React</h1>
11
+
12
+ <p align="center">React primitives · Shared styles · Typed component APIs</p>
13
+
14
+ <p align="center">
15
+ <a href="https://www.npmjs.com/package/@santi020k/lumen-react"><img src="https://img.shields.io/npm/v/@santi020k/lumen-react?style=flat-square&color=0369a0" alt="npm version"></a>
16
+ <a href="https://github.com/santi020k/lumen/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-13967e?style=flat-square" alt="MIT license"></a>
17
+ </p>
18
+
19
+ <p align="center">
20
+ <a href="https://lumen.santi020k.com/docs/frameworks/react">Documentation</a>
21
+ ·
22
+ <a href="https://www.npmjs.com/package/@santi020k/lumen-react">npm</a>
23
+ ·
24
+ <a href="https://github.com/santi020k/lumen/tree/main/packages/react">Source</a>
25
+ ·
26
+ <a href="https://github.com/santi020k/lumen/issues">Issues</a>
27
+ </p>
28
+
29
+ **Package:** `@santi020k/lumen-react`
30
+
31
+ **On this page:** [Install](#install) · [Usage](#usage) · [Appearance presets](#appearance-presets) · [Resources](#resources)
32
+
33
+ <details>
34
+ <summary>Browse all sections</summary>
35
+
36
+ - [Install](#install)
37
+ - [Selective imports](#selective-imports)
38
+ - [Appearance presets](#appearance-presets)
39
+ - [Usage](#usage)
40
+ - [Dropdown menus](#dropdown-menus)
41
+ - [Language selection](#language-selection)
42
+ - [Forms](#forms)
43
+ - [Error states](#error-states)
44
+ - [Data visualization](#data-visualization)
45
+ - [Next.js and server components](#nextjs-and-server-components)
46
+ - [Compatibility wrappers](#compatibility-wrappers)
47
+ - [Context navigation](#context-navigation)
48
+ - [Optimized images](#optimized-images)
49
+ - [Kanban boards](#kanban-boards)
50
+ - [Motion](#motion)
51
+ - [Glass surfaces](#glass-surfaces)
52
+ - [Semantic stat roots](#semantic-stat-roots)
53
+ - [Consumer composition recipes](#consumer-composition-recipes)
54
+ - [Exact amount fields](#exact-amount-fields)
55
+ - [Inline date range calendar](#inline-date-range-calendar)
56
+ - [Input-attached date range selection](#input-attached-date-range-selection)
57
+ - [Phone presentation in v4](#phone-presentation-in-v4)
58
+ - [Combobox keyboard behavior](#combobox-keyboard-behavior)
59
+ - [Content flow](#content-flow)
60
+ - [Attachment composition](#attachment-composition)
61
+ - [Chart datum actions](#chart-datum-actions)
62
+ - [Dashboard composition](#dashboard-composition)
63
+ - [Hook state updates](#hook-state-updates)
64
+ - [Advanced table views](#advanced-table-views)
65
+ - [Advanced form workflows](#advanced-form-workflows)
66
+ - [Device demonstrations](#device-demonstrations)
67
+ - [Visual interactions](#visual-interactions)
68
+ - [World map](#world-map)
69
+ - [Operational workflows](#operational-workflows)
70
+ - [Studio media workspace](#studio-media-workspace)
71
+ - [Resources](#resources)
72
+
73
+ </details>
74
+
75
+ React components and behavior hooks for Lumen UI, using the shared standalone stylesheet.
76
+ Start with one CSS import, then compose public primitives in your existing React application.
77
+
78
+ See the [shared web form contracts](https://github.com/santi020k/lumen/blob/main/docs/form-controls.md) for value ownership, reset,
79
+ submission, disabled state, and event behavior.
80
+
81
+ Form controls use `visualSize` (`visual-size` in Elements) with `default`, `sm` and `lg`.
82
+ Select, PhoneInput and Segmented follow Input and NativeSelect; numeric input/select `size` keeps
83
+ its native meaning. See the [v4 migration guide](https://github.com/santi020k/lumen/blob/main/docs/migrating-v3-to-v4.md#form-control-visual-sizing).
7
84
 
8
85
  ## Install
9
86
 
87
+ Requires React 19 or newer in the consuming application.
88
+
10
89
  ```bash
11
90
  pnpm add @santi020k/lumen-react
12
91
  ```
@@ -32,15 +111,41 @@ The stylesheet defaults `--ui-font` to `"Montserrat", "Avenir Next", "Segoe UI",
32
111
  It declares the family stack but does not bundle or load font files. Load Montserrat once through
33
112
  your preferred delivery path, or override `--ui-font` in application CSS.
34
113
 
114
+ ## Selective imports
115
+
116
+ Production builds compact component JavaScript without renaming identifiers or changing the
117
+ ES2022 target. Declaration files and public imports retain their existing contracts.
118
+
119
+ Root imports remain supported. Standalone component entrypoints include `/components/attachments`,
120
+ `/components/bullet-chart`, `/components/comparison-chart`, `/components/data-table`,
121
+ `/components/date-range-calendar`, `/components/date-range-input`, `/components/expanded-charts`,
122
+ `/components/image-comparison`, `/components/interval-charts`, and `/components/virtual-list`.
123
+ Import behavior hooks from `/hooks`; use `/server` for the server-safe primitive catalog.
124
+ Load the existing stylesheet once, regardless of the import path.
125
+
126
+ ```tsx
127
+ import { ImageComparison } from '@santi020k/lumen-react/components/image-comparison'
128
+ import { VirtualList } from '@santi020k/lumen-react/components/virtual-list'
129
+ ```
130
+
131
+ These entries use the same implementation and types as root imports. Interactive entries retain
132
+ `use client` for React Server Component consumers. A smaller module graph does not guarantee a
133
+ smaller final bundle: a bundler can already remove unused root exports. Run the repository's
134
+ `pnpm run measure:selective-imports` benchmark for the measured comparison.
135
+
136
+ ## Appearance presets
137
+
138
+ Use a scoped `data-lumen-preset="studio"` container, or select a preset with `useThemeBuilder`. See [appearance presets](https://github.com/santi020k/lumen/blob/main/docs/appearance-presets.md) for theme overrides and explicit glass surfaces.
139
+
35
140
  ## Usage
36
141
 
37
142
  ```tsx
38
- import { Button, Card, Input } from "@santi020k/lumen-react";
143
+ import { Button, Card, Input, Label } from "@santi020k/lumen-react";
39
144
 
40
145
  export function SubscribeForm() {
41
146
  return (
42
147
  <Card>
43
- <label htmlFor="email">Email</label>
148
+ <Label htmlFor="email">Email</Label>
44
149
  <Input id="email" type="email" placeholder="you@example.com" />
45
150
  <Button>Subscribe</Button>
46
151
  </Card>
@@ -66,6 +171,18 @@ Compose dropdown menus from the public trigger, content, item, and separator par
66
171
  menu after a successful selection; preventing the item's click event keeps it open. Disabled items
67
172
  remain unavailable, and `status` adds short trailing context to the item.
68
173
 
174
+ Disclosure keyboard navigation skips hidden or inert regions, invisible controls, and native
175
+ disabled controls, including a disabled fieldset. Available controls in a fieldset's first legend
176
+ retain their native keyboard behavior; removing `inert` makes a region available again.
177
+
178
+ Mentions keeps suggestion navigation on the textarea through `aria-activedescendant`; suggestion
179
+ buttons are excluded from the Tab sequence while Enter and pointer selection still insert a mention.
180
+
181
+ Select and `useSelect` treat Space like Enter on the trigger and options unless it continues an
182
+ active typeahead query, focus the target option after the opened listbox renders, and close the
183
+ listbox when focus leaves the field. Select passes its
184
+ `aria-label` or `aria-labelledby` to the listbox as well as the trigger.
185
+
69
186
  ```tsx
70
187
  import {
71
188
  DropdownMenu,
@@ -122,6 +239,13 @@ and Spanish defaults when `locales` is omitted.
122
239
  accessibility contract without replacing form state. Native-backed controls forward refs to their
123
240
  submitted DOM controls and work directly with React Hook Form's `register()`.
124
241
 
242
+ Native form resets defer past the browser's default action and honor a cancelled `reset` event:
243
+ `DatePicker`, `DateRangeInput`, `PhoneInput`, and `Combobox` only restore uncontrolled defaults when
244
+ the reset is not prevented, and leave a controlled `value` untouched. `DatePicker` calendar
245
+ selection fires the native `onChange` exactly once, matching typed input. `DateRangeInput` attaches
246
+ its reset listener even without `name`, honoring an explicit `form` id as well as the nearest
247
+ ancestor form.
248
+
125
249
  For controlled composites, install the optional adapter:
126
250
 
127
251
  ```bash
@@ -138,6 +262,13 @@ without wrapping or changing the resolver contract.
138
262
 
139
263
  See the [forms guide](https://lumen.santi020k.com/docs/forms/react-hook-form) for typed examples.
140
264
 
265
+ `ListBox` exposes a single focusable list with `label`, `aria-label`, or `aria-labelledby` naming.
266
+ External labels targeting its `id` and wrapping labels also name the visible list; explicit names take precedence.
267
+ Its native select retains form submission and required validation while staying out of the tab order
268
+ and accessibility tree. An unselected required single list remains invalid until a choice is made;
269
+ validation focuses the visible list. Canceled form resets preserve the current selection, and
270
+ `aria-activedescendant` identifies the enabled option reached by keyboard navigation.
271
+
141
272
  ## Error states
142
273
 
143
274
  Use `ErrorState` as a visible fallback when a page or region cannot show its primary content. It can
@@ -152,7 +283,7 @@ diagnostics, or decide whether retrying is safe.
152
283
  />
153
284
  ```
154
285
 
155
- See the repository [React error-handling guide](../../docs/error-handling.md#react) for the complete
286
+ See the repository [React error-handling guide](https://github.com/santi020k/lumen/blob/main/docs/error-handling.md#react) for the complete
156
287
  example, error-boundary ownership, announcement behavior, and verification guidance.
157
288
 
158
289
  For long articles, `Anchor` accepts optional `depth`, `index`, and `description` metadata plus an
@@ -188,15 +319,29 @@ either a `value` or a `target` selector; `toast` opts into Lumen Toast feedback.
188
319
  customize its visible feedback while the localized label props remain the accessible announcement.
189
320
  Stable `data-slot` hooks expose the idle, copied, and error parts.
190
321
 
322
+ Wrap React copy actions in `ToastProvider` to display their optional toast feedback. Target selectors
323
+ resolve in the clicked button's owning document, including iframe portals. Updating a closing or moved
324
+ toast reapplies the destination viewport limit without dismissing toasts in another placement.
325
+
191
326
  ## Data visualization
192
327
 
328
+ The web visualization milestone adds `WaterfallChart` for signed changes and explicit totals, and
329
+ `Histogram` for precomputed numeric bins (`frequency="density"` for unequal widths). Line charts
330
+ support explicit continuous axes, annotations, optional keyboard/pointer/touch inspection, and
331
+ synchronized cursors. Heatmaps show labeled axes, a color legend, and explicit missing cells.
332
+ See the [visualization contracts](https://github.com/santi020k/lumen/blob/main/docs/data-visualization.md) and
333
+ [interactive web example](https://lumen.santi020k.com/docs/web/data-visualization).
334
+
335
+ Use `interactive`, `syncGroup`, and optional `cursor`/`onCursorChange` on `LineChart`. A supplied
336
+ `cursor` is controlled; the owner must accept requests before the selection changes.
337
+
193
338
  `Sparkline`, `BarChart`, `LineChart`, `PieChart`, `ScatterChart`, `Heatmap`, `RangeChart`, and
194
339
  `ComboChart` use the shared chart contracts and
195
340
  render without an external charting dependency. Data charts expose a revealable semantic table by
196
341
  default. `PieChart` accepts one series and defaults to a donut presentation.
197
342
 
198
343
  Every chart includes a factual screen-reader summary by default and accepts `summary` for more
199
- useful domain context. See [data visualization](../../docs/data-visualization.md) for selection,
344
+ useful domain context. See [data visualization](https://github.com/santi020k/lumen/blob/main/docs/data-visualization.md) for selection,
200
345
  missing-data, live-data, and accessibility guidance.
201
346
 
202
347
  ```tsx
@@ -213,6 +358,10 @@ const series = [{
213
358
 
214
359
  ## Next.js and server components
215
360
 
361
+ React DatePicker, Calendar (including custom `useCalendar` roots), and InputOTP render
362
+ `data-ui-owner="react"`. The Astro runtime and Elements enhancers skip owned roots, so React islands
363
+ keep their ids, labels, and state on pages that also load those runtimes.
364
+
216
365
  The package entry is marked as a client module because the full catalog includes stateful
217
366
  components and behavior hooks. Next.js Server Components can import and render Lumen components;
218
367
  Next keeps the Lumen subtree behind the client boundary instead of evaluating React client APIs in
@@ -252,6 +401,11 @@ export default function Status() {
252
401
  }
253
402
  ```
254
403
 
404
+ Skeleton accepts `shape` (`rectangle`, `text`, `circle`), `width`, `height`, and `animation`
405
+ (`shimmer`, `pulse`, `none`). Numeric sizes are pixels, and explicit heights can be smaller than the
406
+ 1rem default. Unnamed Skeletons and Spinners are decorative; wrap a group in one `role="status"`
407
+ region instead of naming each placeholder.
408
+
255
409
  The server entrypoint contains Badge, Card and its compound parts, Container, Direction, Grid,
256
410
  Input, Label, Progress, Separator, Skeleton, Spinner, Stack, Textarea, Typography, and
257
411
  VisuallyHidden plus the component-name metadata. The package root reuses those exact
@@ -259,6 +413,11 @@ implementations and remains the full client catalog. Import interactive primitiv
259
413
  stateless primitives that receive event handlers from the root entrypoint inside a Client
260
414
  Component.
261
415
 
416
+ `DatePicker` reopens its calendar on the current selected month, including controlled values loaded
417
+ while it was closed. The visible trigger receives `aria-invalid`, `aria-describedby`, and
418
+ `aria-errormessage`. Native validation feedback clears after a valid update or an uncanceled reset;
419
+ explicit application validation attributes remain authoritative.
420
+
262
421
  ## Compatibility wrappers
263
422
 
264
423
  The common wrapper primitives preserve their underlying DOM handles with React 19's ref-as-prop
@@ -384,16 +543,22 @@ ARIA, keyboard, Escape, dismissal, context menu, form
384
543
  validation, calendar grids, OTP segmentation, date range syncing, rich text command, schedule
385
544
  drag/drop, controlled Kanban move requests, theme export and switching, resizable pane sizing, and
386
545
  toast controller semantics for React applications.
387
- `useRichTextEditor` also provides `getEditableProps`, value-bearing commands, common formatting
546
+ Toast timeouts pause while hovered or focused and resume only after both interactions end;
547
+ moving focus between a toast's controls preserves its remaining duration. Updating a closing toast
548
+ reopens it with a fresh lifetime using its current duration.
549
+ `useRichTextEditor` honors consumer event cancellation before toolbar and shortcut commands.
550
+ It also provides `getEditableProps`, value-bearing commands, common formatting
388
551
  shortcuts, active toolbar state, and `{ html, text }` change details.
389
552
  `DataTable` can render structured `columns` and `rows`; sortable columns use native header buttons,
390
553
  update `aria-sort`, and order string or numeric values without mutating the supplied rows. The
391
554
  shared selectable/sortable data attributes remain available for app-level adapters, and
392
- `VirtualList` exposes the shared range sizing attributes.
555
+ `VirtualList` provides built-in fixed-height windowing with the shared sizing attributes.
393
556
  `useTabs` keeps the selected trigger visible when a narrow horizontal list scrolls. The package
394
557
  also exports `LumenTabsChangeDetail` and `LumenTabsChangeEvent` for integrations that consume the
395
558
  shared `ui:tabs-change` contract.
396
559
 
560
+ Keyboard navigation stays within the current tab group when tabs are nested and skips disabled triggers.
561
+
397
562
  ## Kanban boards
398
563
 
399
564
  Use `KanbanBoard`, `KanbanColumn`, and ordinary `Card` items for status-based workspaces. The
@@ -446,7 +611,7 @@ or `variant="glass"` for selective translucency.
446
611
 
447
612
  ## Consumer composition recipes
448
613
 
449
- See [consumer UI recipes](../../docs/consumer-ui-recipes.md) for static React icons, responsive
614
+ See [consumer UI recipes](https://github.com/santi020k/lumen/blob/main/docs/consumer-ui-recipes.md) for static React icons, responsive
450
615
  record tables, keyboard-aware native sheets, whole-unit amount fields, adaptive editors, and
451
616
  asynchronous action states. Each recipe identifies the public primitives and the behavior that
452
617
  remains owned by the application.
@@ -462,3 +627,517 @@ import { Icon, Search } from '@santi020k/lumen-react/icons'
462
627
  The existing root `Icon name="search"` remains supported for runtime-selected names and icon packs.
463
628
  `Table layout="records"` opts into the responsive record recipe; its semantic child markup and
464
629
  shared mobile labels are documented in the consumer recipe linked above.
630
+
631
+ ## Exact amount fields
632
+
633
+ `AmountField` accepts editable ASCII decimal strings, formats a requested locale, and submits a
634
+ complete decimal string through its named hidden input. Use `defaultValue` for an uncontrolled
635
+ field or `value` with `onValueChange` for application-owned drafts. It preserves precision and
636
+ never rounds through a JavaScript number. Updating `defaultValue` changes the next reset baseline
637
+ without replacing the active draft. Currency rules and limits remain application-owned.
638
+
639
+ ```tsx
640
+ <AmountField name="amount" locale="es-CO" defaultValue="1234.50" aria-label="Amount COP" />
641
+ ```
642
+
643
+ See [consumer workflows](https://github.com/santi020k/lumen/blob/main/docs/consumer-ui-recipes.md#executable-consumer-workflows) for
644
+ validation, reset, operational-record actions and opt-in MessageScroller following.
645
+
646
+ ## Inline date range calendar
647
+
648
+ `DateRangeCalendar` is a controlled React range editor with two visible calendars,
649
+ inclusive range highlighting, a preset sidebar and the keyboard behavior of `useCalendar`,
650
+ including inherited RTL arrow navigation and focus movement in iframe documents.
651
+ On narrow screens the presets scroll horizontally and the calendars stack. It uses the shared Lumen stylesheet.
652
+
653
+ ```tsx
654
+ const [range, setRange] = useState({ start: '2026-09-01', end: '2026-09-30' })
655
+
656
+ <DateRangeCalendar
657
+ value={range}
658
+ onValueChange={setRange}
659
+ locale="en-US"
660
+ min="2000-01-01"
661
+ max="2100-12-31"
662
+ labels={{ start: 'From', end: 'To', presets: 'Quick range' }}
663
+ presets={[{ label: 'September', value: { start: '2026-09-01', end: '2026-09-30' } }]}
664
+ />
665
+ ```
666
+
667
+ Supply valid ISO date endpoints in ascending order. Choosing a start after the end,
668
+ or an end before the start, moves the opposite endpoint to the chosen day.
669
+ Presets outside `min`/`max` or in descending order are disabled. Only one matching
670
+ preset is highlighted, including when multiple presets resolve to the same range. `formatDate` can customize
671
+ the endpoint summaries without changing ISO values. The consumer owns draft state,
672
+ Apply/Cancel actions and domain limits such as maximum report duration. Labels are required;
673
+ `locale` controls month, weekday, navigation and day announcements. React date controls default
674
+ to English on both the server and browser; pass the application locale explicitly for localized
675
+ SSR and hydration.
676
+
677
+ ## Input-attached date range selection
678
+
679
+ `DateRangeInput` wraps `DateRangeCalendar` in an anchored, non-modal popover.
680
+ It measures and observes its owning viewport, including iframe documents. Apply and Cancel restore
681
+ trigger focus, and native reset follows the current associated form. Outside-focus dismissal
682
+ follows the owning document, including same-origin iframe and portal rendering. Use it
683
+ when the range should be edited directly from an input-like control. It keeps draft
684
+ changes internal and calls `onValueChange` only when the user chooses Apply.
685
+ The existing `DateRangePicker` and inline `DateRangeCalendar` remain available.
686
+
687
+ ```tsx
688
+ import { useState } from 'react'
689
+ import { DateRangeInput } from '@santi020k/lumen-react'
690
+ import '@santi020k/lumen-react/styles.css'
691
+
692
+ export function ReportPeriod() {
693
+ const [range, setRange] = useState({ start: '2026-09-01', end: '2026-09-30' })
694
+
695
+ return (
696
+ <DateRangeInput
697
+ value={range}
698
+ onValueChange={setRange}
699
+ label="Report period"
700
+ locale="en-US"
701
+ labels={{ start: 'From', end: 'To', presets: 'Quick ranges', apply: 'Apply', cancel: 'Cancel' }}
702
+ presets={[{ label: 'September', value: { start: '2026-09-01', end: '2026-09-30' } }]}
703
+ name={{ start: 'from', end: 'to' }}
704
+ validate={draft => draft.start < '2026-01-01' ? 'Choose dates in 2026 or later.' : undefined}
705
+ renderSummary={draft => `${draft.start} through ${draft.end}`}
706
+ />
707
+ )
708
+ }
709
+ ```
710
+
711
+ - `value` must contain real, ascending ISO dates (`YYYY-MM-DD`). `min`, `max`,
712
+ `presets`, `locale`, and `formatDate` follow the inline calendar contract.
713
+ - `label` names both the trigger and dialog. Supply localized start, end, presets,
714
+ apply, and cancel labels. For Spanish, use `Desde`, `Hasta`, `Períodos`, `Aplicar`,
715
+ and `Cancelar` with `locale="es-CO"`. Navigation announcements follow `locale`.
716
+ - `validate` runs on the draft; return a localized error to disable Apply. The error
717
+ is announced politely and associated with the Apply button. Keep validation pure.
718
+ - `renderSummary` optionally renders localized draft details. `formatDate` changes
719
+ visible dates, while optional hidden form fields always submit the applied ISO values.
720
+ - Cancel, Escape, outside pointer interaction, or moving focus outside discard the
721
+ draft. Apply, Cancel, and Escape return focus to the trigger. Opening again starts
722
+ from the latest controlled value. Calendar arrows retain their date-navigation behavior.
723
+ - The panel uses the browser Popover API top layer without a modal backdrop or focus
724
+ trap. A fixed-position fallback works where the API is unavailable; ancestor clipping
725
+ can affect that fallback. The page remains interactive.
726
+ - The panel tracks viewport changes, scroll, and trigger size. Narrow screens scroll
727
+ the trigger into view, stack the calendars, and scroll presets horizontally.
728
+ Only the body scrolls vertically; confirmation actions stay visible.
729
+ - `disabled` disables the trigger and optional form entries. `className` styles the
730
+ outer container. Import the shared stylesheet once at the application boundary.
731
+
732
+ This component is currently React-only. Publication is separate from local implementation.
733
+
734
+ ### Accessible code examples
735
+
736
+ `Code` and `CodeTabs` accept `copyLabel`, `copiedLabel`, `errorLabel`, and
737
+ `codeLabel`. Copy controls work directly in React, emit `ui:copy-success` or
738
+ `ui:copy-error`, and announce localized success or recovery guidance. Repeated
739
+ clicks restart feedback; unmounting clears its timer. No Astro runtime is needed.
740
+
741
+ Unwrapped code uses a named, keyboard-focusable region. `codeLabel` defaults to
742
+ `Code example`; set a descriptive localized name when several examples are
743
+ present. Highlighted `pre` children receive the same behavior while retaining
744
+ authored names and tab order. Wrapped code does not add an extra tab stop.
745
+
746
+ ### Server-sorted tables
747
+
748
+ `DataTable` accepts `sort` and `onSortChange` for controlled sorting, or
749
+ `defaultSort` for an initial uncontrolled sort. A `DataTableSort` contains a
750
+ column `key` and `direction: 'ascending' | 'descending'`; `null` means unsorted.
751
+ Header buttons request the next direction, and `aria-sort` describes the applied
752
+ state. Replacing `rows` does not reset that state.
753
+
754
+ Use `sortMode="manual"` with server pagination. In this mode the table preserves
755
+ the supplied row order, even when a header is activated. Apply the requested sort
756
+ to the complete dataset on the server before selecting the page, then supply the
757
+ returned rows and controlled sort. Keep authentication, query validation, loading
758
+ state, and network errors in the application. The default `sortMode="client"`
759
+ sorts a copy of the supplied rows.
760
+
761
+ ### Dialog dismissal and focus
762
+
763
+ `Dialog`, `AlertDialog`, and `useDialog` accept `dismissOnOutsidePress` and
764
+ `dismissOnEscape`. Ordinary dialogs allow both by default; alert dialogs ignore
765
+ outside presses by default. Set both to `false` while an application requires an
766
+ explicit decision or is completing a pending mutation. These policies govern
767
+ implicit dismissal; explicit close actions remain under application control.
768
+
769
+ Native Escape requests `onOpenChange(false)` without overriding a controlled
770
+ `open` value. Content padding and a drag that begins inside the dialog do not
771
+ count as backdrop dismissal. Consumer `onCancel` or `onClick` handlers can prevent
772
+ the corresponding default action.
773
+
774
+ A controlled dialog can open without hook trigger props: it captures the focused
775
+ opener before opening and returns focus on close or unmount, including when rendered into an iframe.
776
+ Focus capture and restoration use the dialog's own document. StrictMode replay
777
+ preserves that opener, and cleanup does not steal focus from a nested or
778
+ replacement dialog. Hook trigger props remain useful when the same component
779
+ owns the opener and dialog. Keep an accessible dialog name and logical initial
780
+ focus; native `autoFocus` can select the initial control.
781
+
782
+ ### Virtual list and editor ownership
783
+
784
+ `VirtualList` displays fixed-height rows with inert spacers that retain the full scroll extent.
785
+ Rows stay mounted; use pagination when the initial DOM cost matters. Scrolling, resizing, sizing
786
+ changes and direct row changes refresh the window. Focused rows remain available. See the
787
+ [fixed-height list contract](https://github.com/santi020k/lumen/blob/main/docs/ai-usage.md#fixed-height-virtual-lists).
788
+
789
+ External rich-text engines should handle the cancelable `ui:editor-command-request` event before
790
+ execution and use `ui:editor-command` only for completion notifications. React also supports
791
+ `useRichTextEditor({ commandHandler })`. Disable native toolbar state syncing when the external
792
+ engine owns it. See the [editor guidance](https://github.com/santi020k/lumen/blob/main/docs/ai-usage.md).
793
+
794
+ With native state enabled, `useRichTextEditor` initializes toolbar toggle states on mount,
795
+ including `aria-pressed`, before the first editing interaction. External command handlers
796
+ disable this synchronization by default so the application can own toolbar state.
797
+ Native toolbar and keyboard commands execute in the editor root's owning document, including
798
+ editors portaled into a same-origin iframe.
799
+
800
+ ## Phone presentation in v4
801
+
802
+ Phone inputs bundle the same offline flag artwork on every platform. The selected country shows
803
+ its flag and calling code inside one continuous input border. Country names remain in the native
804
+ picker and its accessible name; flags are supplementary. Unknown flag codes fall back to text.
805
+
806
+ Astro and React `PhoneInput` accept `disabled`, `readOnly`, `required`, `errorMessage`,
807
+ `showValidationError`, and `inputProps`. Their `id` targets the number input in v4; React also
808
+ accepts `inputRef`. Web Components use `disabled`, `readonly`, `required`, `error-message`,
809
+ `show-validation-error="false"`, and `input-id`, with native input attributes on the host.
810
+ Both controls lock together and validation remains associated with the input. React forwards
811
+ `inputProps.form` to the country picker and read-only country value as well as the number input,
812
+ so an external form receives both values. Legacy `countries` pickers also retain their selected
813
+ country while read-only and restore it after an accepted native form reset.
814
+
815
+ The `phone-input`, `phone-country`, and `country-flag` styling parts plus `--ui-phone-height`,
816
+ `--ui-phone-padding`, and `--ui-phone-country-gap` replace consumer CSS overlays.
817
+
818
+ Astro and React also export `CountryFlag` (`regionCode`, optional `decorative`) and `PhoneNumber`
819
+ (`value: LumenPhoneNumber`, optional `link`). A telephone link is rendered only for a complete
820
+ E.164 value. Use the model returned by the phone normalizer; keep domain persistence in your app.
821
+ Artwork attribution is shipped with the core package in `PHONE_FLAG_LICENSE.txt`.
822
+
823
+ ## Combobox keyboard behavior
824
+
825
+ Decoded Combobox options must be a dense string collection; invalid collections expose no options.
826
+ In v4, Combobox retains input focus and exposes its active option through `aria-activedescendant`.
827
+ Enter commits an active option; text editing and composition remain native. Escape dismisses one
828
+ nested control at a time. See the [shared keyboard contract](https://github.com/santi020k/lumen/blob/main/docs/ai-usage.md#combobox-keyboard-behavior-in-v4)
829
+ for dynamic options, controlled inputs and migration guidance.
830
+
831
+ Combobox also supports native form reset: resetting the owning form restores an uncontrolled
832
+ `defaultValue`, closes the open option list, and clears the active selection without emitting
833
+ `onChange`. A controlled `value` is left unchanged.
834
+
835
+ ## Content flow
836
+
837
+ Stack and Grid own sibling spacing. Their gap accepts `related`, `group` (default) and `section`,
838
+ or canonical `none`, `xs`, `sm`, `md`, `lg`, `xl`, `2xl`, `3xl` sizes. Card owns the inset and gap
839
+ between its visible parts; use `density="compact"`, `"comfortable"` (default) or `"spacious"`.
840
+ Elements uses the same names as attributes. Use a nested Stack for CardContent groups and Field
841
+ for label/control/feedback. See [content flow](https://github.com/santi020k/lumen/blob/main/docs/content-flow.md) and the
842
+ [v4 migration guide](https://github.com/santi020k/lumen/blob/main/docs/migrating-to-lumen.md) for ownership and changed explicit gaps.
843
+
844
+ ### Reading and complete compositions
845
+
846
+ Prose and Typography trim outer child margins and separate headings from preceding text.
847
+ Container gutters grow from 16px to 32px with viewport width; override `--ui-container-gutter`
848
+ when a product needs fixed gutters. Card allows interactive overflow; use AspectRatio to clip media.
849
+ Install `content-flow-header`, `content-flow-settings`, `content-flow-list` or `content-flow-actions`
850
+ with `lumen add <recipe> --target astro|react|elements`. MCP returns the same complete examples.
851
+ Connect application actions and replace sample IDs before reuse. See
852
+ [content flow](https://github.com/santi020k/lumen/blob/main/docs/content-flow.md) for composition and migration guidance.
853
+
854
+ ### Large fixed-height collections
855
+
856
+ React `VirtualList` accepts typed `items`, `getKey` and `renderItem` for data mode.
857
+ Only the visible window, overscan and focused neighbors mount. Stable keys retain row identity;
858
+ applications own offscreen editing state. See [data rendering](https://github.com/santi020k/lumen/blob/main/docs/virtual-list-data.md) for
859
+ setup, lifecycle, accessibility and the mounted-mode tradeoff.
860
+
861
+ ### Compound dialog tasks
862
+
863
+ `DialogHeader`, `DialogTitle`, `DialogBody`, `DialogFooter`, and `DialogClose`
864
+ compose long forms with fixed actions and an independently scrolling body. Keep
865
+ `DialogBody` directly inside `Dialog`. Give `DialogTitle` an `id` and reference it
866
+ from `Dialog aria-labelledby`; `as` supports `h2`, `h3`, and `h4`.
867
+ `DialogClose` accepts Button props, honors `onClick` cancellation, and requests
868
+ closure through the enclosing Dialog controller, including controlled dialogs.
869
+ The four static structural parts are also exported from `@santi020k/lumen-react/server`.
870
+
871
+ ```tsx
872
+ <Dialog open={open} onOpenChange={setOpen} aria-labelledby="record-title">
873
+ <DialogHeader><DialogTitle id="record-title">Edit record</DialogTitle></DialogHeader>
874
+ <DialogBody><Form id="record-form"><Input name="name" aria-label="Name" /></Form></DialogBody>
875
+ <DialogFooter>
876
+ <DialogClose variant="outline">Cancel</DialogClose>
877
+ <Button type="submit" form="record-form">Save</Button>
878
+ </DialogFooter>
879
+ </Dialog>
880
+ ```
881
+
882
+ `FileUpload selectedFilesLabel` accepts localized text containing `{count}`.
883
+ FileUpload keeps its native file input uncontrolled: `value` and `defaultValue` are excluded from its props and ignored in decoded JavaScript input.
884
+ Reset feedback follows the current native form owner, including replaced external forms.
885
+ Accepted native form resets clear selected-file feedback; cancelled resets preserve it.
886
+
887
+ ### Rich description rows
888
+
889
+ `DescriptionItem`, `DescriptionTerm`, and `DescriptionDetail` accept native props
890
+ and refs and render `div`, `dt`, and `dd`. Compose them inside `Descriptions` for
891
+ rich values, alongside the existing `items` array when needed. The three static
892
+ parts are also available from `@santi020k/lumen-react/server` for use inside a native `dl`.
893
+
894
+ ```tsx
895
+ <Descriptions>
896
+ <DescriptionItem>
897
+ <DescriptionTerm>Status</DescriptionTerm>
898
+ <DescriptionDetail><Badge variant="success">Active</Badge></DescriptionDetail>
899
+ </DescriptionItem>
900
+ </Descriptions>
901
+ ```
902
+
903
+ ## Attachment composition
904
+
905
+ Use `AttachmentList` to group native `li` children and `AttachmentPreview` for browser-owned images
906
+ with localized loading, error, and unsupported-file states. Compose independent actions rather
907
+ than nesting controls inside a linked Attachment. The application retains file validation,
908
+ authorization, persistence, and object URL cleanup. See the
909
+ [attachment composition recipe](https://github.com/santi020k/lumen/blob/main/docs/consumer-ui-recipes.md#attachment-previews-and-file-lists)
910
+ for adapter props, slots, child contracts, retry identity, and safe state events.
911
+
912
+ ## Chart datum actions
913
+
914
+ Pass `onDatumActivate(detail)` to BarChart, LineChart, PieChart, ScatterChart, ComboChart, Heatmap,
915
+ or RangeChart to enable drilldown. The callback receives `LumenChartDatumActivationDetail`,
916
+ exported from this package, with raw axes and optional datum IDs. Applications own navigation,
917
+ filtering, detail views, and authorization. React charts use their own event handling and do not
918
+ require `UIPrimitives`.
919
+
920
+ Each available plotted datum has an equivalent native button in the actions disclosure, even
921
+ with `showTable={false}` or hidden line markers. Translate `labels.exploreData` and
922
+ `labels.formatDatumAction(context)` alongside the chart's existing labels and value formatters.
923
+ Missing observations have no action and never reach datum-action value formatters; pie actions cover only positive slices. Updated values and
924
+ callbacks take effect on rerender, while stable datum identities retain focused action buttons.
925
+ Chart activation uses the owning document, including iframe rendering.
926
+ The chart's native `onClick` can cancel activation with `event.preventDefault()`.
927
+
928
+ ## Dashboard composition
929
+
930
+ `FilterBar` groups host-owned filtering controls, active criteria, reset actions, and a polite
931
+ result announcement. `ChangeSummary` presents explicit before/after values and application-owned
932
+ changed state. Neither component owns requests, persistence, parsing, or financial policy.
933
+ Omit `FilterBar.open` for native disclosure ownership. Unrelated rerenders preserve native toggles
934
+ when `defaultOpen` is unchanged. Pass `open` and `onOpenChange` for application-controlled disclosure.
935
+
936
+ ScatterChart supports independent X/Y formatting, explicit domains, logarithmic positive X values,
937
+ and labeled reference lines/regions. See [consumer UI recipes](https://github.com/santi020k/lumen/blob/main/docs/consumer-ui-recipes.md)
938
+ for dashboard tables, freshness, import review, activity inbox, and persistent Kanban patterns.
939
+
940
+ React DataTable adds `layout="records"`, rich `column.render`, and expandable `renderDetails`.
941
+ Use stable record IDs and controlled `expandedRowIds` across pages. Row identity uses `rowValue`,
942
+ then `id`, then a legacy `value` fallback; ordinary value cells do not override explicit IDs.
943
+ Rows without an explicit identity keep their original input index across client sorting.
944
+ Decoded row and column collections are validated before sorting or rendering. Invalid rows, columns, identities,
945
+ cell shapes, empty identities, and duplicate resolved identities fail closed to an empty table while
946
+ the column headers remain available. Client sorting only applies to columns marked `sortable`.
947
+ `DataTableSortControls` shares
948
+ `sort`/`onSortChange` with table headers; manual sorting preserves server page order.
949
+ Rows without an explicit `rowValue`, `value`, or `id` use their original input index as identity
950
+ during client sorting, preserving expanded details and local edits. Supply stable IDs when replacing,
951
+ filtering, or paginating the input rows.
952
+
953
+ Popover and DropdownMenu support anchored top-layer placement, viewport collision handling, logical
954
+ start/end alignment, and focus handoff in their owning document, including iframes.
955
+ Set `positioning="none"` for application-owned placement. Context menus dismiss outside pointer
956
+ presses; tooltips close when focus leaves their owner and cancel pending opening timers.
957
+
958
+ ### Actual-versus-target charts
959
+
960
+ `BulletChart` compares a nullable actual `value` with a finite `target` and optional
961
+ labeled `ranges`. A strong actual bar, target marker, readable value labels, and expandable exact
962
+ data work together. Domains include zero and all measurements; invalid inputs fail closed.
963
+ Invalid measurements do not reach `formatValue`.
964
+ Null values stay distinct from zero. See the [chart guide](https://github.com/santi020k/lumen/blob/main/docs/data-visualization.md#actual-values-and-targets)
965
+ for the input, localization, and domain contracts.
966
+
967
+ ### Rankings and paired comparisons
968
+
969
+ Use `LollipopChart` for zero-based rankings and `DumbbellChart` for paired measurements (native
970
+ `LumenLollipopChart` and `LumenDumbbellChart`). Supply ordered comparison data with `id`, `label`,
971
+ nullable `value`, optional nullable `reference`, and optional `tone`. Both charts preserve missing
972
+ values and expose exact data. See the [shared visualization contract](https://github.com/santi020k/lumen/blob/main/docs/data-visualization.md#rankings-and-paired-comparisons).
973
+
974
+ ### Calendar activity, ordered stages and distributions
975
+
976
+ `CalendarHeatmap`, `FunnelChart`, and `BoxPlot` share validated geometry with every Lumen adapter.
977
+ CalendarHeatmap takes date-only UTC `startDate`/`endDate`, nullable `{ date, value }` data and optional
978
+ `weekStartsOn` (0 for Sunday or 1 for Monday). It fills omitted dates as missing, keeps zero distinct,
979
+ and limits the inclusive range to 3,660 days. `weekdayLabels` always indexes Sunday through Saturday;
980
+ `formatDate` customizes readable dates without changing their identity.
981
+
982
+ FunnelChart takes ordered `{ id, label, value, tone? }` stages with nonnegative nullable values.
983
+ Stages retain the supplied order, including increasing values; the chart derives no conversion rates.
984
+ BoxPlot takes precomputed `{ id, label, min, q1, median, q3, max, outliers?, tone? }` statistics.
985
+ Statistics must be ordered and finite, or all five must be null for a missing row. Explicit domains
986
+ must contain all observations and outliers. `statisticLabels` localizes the six statistic names.
987
+
988
+ All three accept `formatValue`, chart `labels`, `summary`, and `showTable` (default true).
989
+ Exact data and missing measurements remain readable; invalid input fails closed instead of dropping
990
+ observations or clipping the domain. Prefer retaining the data table for complete visual inspection.
991
+
992
+ ```tsx
993
+ import { BoxPlot, CalendarHeatmap, FunnelChart } from '@santi020k/lumen-react'
994
+
995
+ <CalendarHeatmap heading="Daily visits" startDate="2026-01-01" endDate="2026-01-31"
996
+ data={[{ date: '2026-01-01', value: 42 }]} />
997
+ <FunnelChart heading="Checkout stages" data={[
998
+ { id: 'view', label: 'Viewed', value: 120 },
999
+ { id: 'paid', label: 'Paid', value: 32 }
1000
+ ]} />
1001
+ <BoxPlot heading="Response times" data={[
1002
+ { id: 'api', label: 'API', min: 20, q1: 45, median: 60, q3: 90, max: 140, outliers: [210] }
1003
+ ]} />
1004
+ ```
1005
+
1006
+ ## Hook state updates
1007
+
1008
+ Public hook setters accept React functional updates. In uncontrolled mode, consecutive calls in
1009
+ one event compose against the latest pending value. Change callbacks run once per setter call,
1010
+ including under Strict Mode. Controlled values remain owned by the application.
1011
+
1012
+ `useToast().dismiss()` closes only the newest open toast. Pass an ID to dismiss a specific toast.
1013
+
1014
+ Highlighted Code updates generated region labels and removes its generated focus attributes when
1015
+ wrapping is enabled, while preserving consumer-owned accessibility attributes.
1016
+
1017
+ ## Advanced table views
1018
+
1019
+ `DataTableView` is an opt-in TanStack Table companion for application-owned record tables. It adds
1020
+ search, exact column filters, column visibility, density, pagination and controlled sorting state
1021
+ while preserving your cells, details, actions and query ownership. Column keys must be nonempty
1022
+ and unique; column labels, accessors, boolean flags and dense filter-option arrays are validated before
1023
+ controls are constructed. Restored controlled and default snapshots require string search/filter values,
1024
+ boolean visibility/sort directions, valid density and integer pagination; malformed state is rejected
1025
+ before table processing. Import it from
1026
+ `@santi020k/lumen-react/components/data-table-view` or the package root. Load
1027
+ `@santi020k/lumen-react/styles/data-table-view.css` after the base stylesheet. See the
1028
+ [composition and server-mode guide](https://github.com/santi020k/lumen/blob/main/docs/data-table-views.md).
1029
+
1030
+ Existing `Table` and `DataTable` APIs remain available. This integration is React-specific and does
1031
+ not change the Astro or Elements runtime.
1032
+ ## Advanced form workflows
1033
+
1034
+ Use the optional `@santi020k/lumen-react/forms` entry point for validation, dependent fields,
1035
+ unsaved edit tracking, step navigation, stable repeatable rows, and cancelable asynchronous checks.
1036
+ Controls associated from outside the form through `form="id"` participate in edit tracking,
1037
+ blur validation and dependent-field validation. Portaled controls use React form callbacks without
1038
+ repeating validation through native document listeners. Each native edit is processed once, and
1039
+ controls associated with another form are ignored even when React bubbles their portal events.
1040
+ Adopted native controls retain checkbox, multiple-selection and file details in dirty-state tracking.
1041
+
1042
+ Accepted native reset buttons refresh dirty state after the browser restores defaults; canceled resets
1043
+ preserve the draft, errors, and attempted state, including cancellation by a native ancestor after
1044
+ React dispatch. `PromptComposer` keeps its standard `id` on the form for external submit controls
1045
+ and derives a separate labelled textarea ID.
1046
+
1047
+ See [composable form workflows](https://github.com/santi020k/lumen/blob/main/docs/powerful-forms.md). Applications retain schemas, financial
1048
+ rules, requests, draft storage, and authorization. Do not combine validation owners on one form.
1049
+
1050
+ `useResizable` accepts finite `panelCount` values up to 1,000. Fractions round down and negative values
1051
+ normalize to zero; non-finite or larger counts throw `RangeError` before allocating panel state.
1052
+ Context-menu item activation and keyboard focus follow the menu's owning document, including iframe
1053
+ portals. Date-range synchronization clamps an earlier end date before updating both input constraints.
1054
+
1055
+ ## Device demonstrations
1056
+
1057
+ `DeviceFrame` presents slotted HTML, an image, or a titled iframe inside `macbook-pro`, `macbook-air`, `imac`,
1058
+ `iphone`, `pixel`, and `ipad-pro` frames, with generic `laptop`, `desktop`, `android`, and `tablet` options.
1059
+ Device-specific enclosures include a MacBook Pro display notch and full front edge, a tapered MacBook Air deck, curved iMac
1060
+ stand and chin, rounded phone glass with separate rails and buttons, and an iPad Pro with even
1061
+ bezels and no Home button. The generic `tablet` retains its classic enclosure.
1062
+ Shells are decorative and do not emulate device hardware. `orientation` selects portrait or landscape; `tone` selects
1063
+ light or dark chrome independently of the screen content. Use `color="white"`,
1064
+ `color="black"`, or a 3-, 4-, 6-, or 8-digit hex color such as `color="#a9b8ac"` for
1065
+ a custom hardware finish. `color` takes precedence over `tone`; invalid values fall back to the
1066
+ tone. The finish stays independent of the content theme. For CSS-driven updates, set
1067
+ `--ui-device-color` to a CSS color on the frame.
1068
+
1069
+ Iframe layouts use the preset screen viewport and scale to the available width. Override
1070
+ `screenWidth` and `screenHeight` for a custom viewport (1–16384 CSS pixels). Slotted HTML shares
1071
+ the host viewport; container queries can adapt it to the screen. Images preserve their proportions.
1072
+ `scroll={false}` clips HTML overflow; an iframe manages its own scrolling.
1073
+
1074
+ Supply image alternative text and iframe titles. Consumers own iframe `sandbox`, `allow`,
1075
+ loading, and referrer policies. Remote sites can refuse embedding through their response headers.
1076
+ The decorative shell does not alter focus or intercept interactions. Camera and home-indicator
1077
+ details occupy separate chrome outside live HTML and iframe viewports. Direct phone images extend
1078
+ under the decorative camera and home indicator for a full-bleed presentation.
1079
+ Screen presets are 1280 × 800 for MacBook Pro and MacBook Air, 1440 × 810 for iMac, 390 × 844 for iPhone,
1080
+ 412 × 915 for Pixel, and 834 × 1194 for iPad Pro (1194 × 834 in landscape). These are demonstration
1081
+ viewports, not physical display specifications.
1082
+
1083
+ ```tsx
1084
+ import { DeviceFrame } from '@santi020k/lumen-react/components/device-frame'
1085
+
1086
+ <DeviceFrame device="laptop" screenWidth={1440} screenHeight={900}>
1087
+ <iframe src="/demo" title="Application demo" loading="lazy" />
1088
+ </DeviceFrame>
1089
+ ```
1090
+
1091
+ This component uses a client effect to maintain the scaled iframe viewport.
1092
+ ## Visual interactions
1093
+
1094
+ `MotionGroup`, `ChartMotion`, `VisualEffect`, `PromptComposer`, `StreamMessage`, `SourceCitation`,
1095
+ `ToolActivity`, and `ApprovalCard` are exported by the main package. `Tabs` accepts `indicator` for
1096
+ an optional moving selection underline. See [visual interactions](https://github.com/santi020k/lumen/blob/main/docs/visual-interactions.md).
1097
+ Install `motion@^14.0.0` only when using `@santi020k/lumen-react/motion`; its Motion wrappers remain
1098
+ separate from normal imports.
1099
+
1100
+ ## World map
1101
+
1102
+ WorldMap supports highlighted countries, location markers, dotted or solid styles, country selection,
1103
+ and theme customization. Use `initialView="highlighted"` (Elements: `initial-view`) to start with a
1104
+ regional view. Zoom toward the cursor with Ctrl/Cmd-scroll, or fit highlighted countries using the
1105
+ map controls. Import geography explicitly from
1106
+ `@santi020k/lumen-core/world-map-data`; it is excluded from root exports. See the
1107
+ [WorldMap usage guide](https://github.com/santi020k/lumen/blob/main/docs/world-map.md) for adapter examples, events, localization,
1108
+ accessibility, and customization.
1109
+
1110
+ For controlled selection, pair `selectedCountry` with `onSelectedCountryChange(countryId)` and
1111
+ store the requested value in application state. An empty string clears the selection while keeping
1112
+ the map controlled. `onCountrySelect(detail)` remains the country-detail callback for nonempty selections.
1113
+
1114
+ ## Operational workflows
1115
+
1116
+ Extend `DataTableView` with inclusive numeric/date ranges and explicit page-scoped selection.
1117
+ Use `DataTableSavedViews` for named preferences through application-owned persistence callbacks;
1118
+ `parseDataTableViewState` validates external preferences without retaining records or selection.
1119
+ See [table views](https://github.com/santi020k/lumen/blob/main/docs/data-table-views.md) for server pagination and selection boundaries.
1120
+
1121
+ `useLumenReviewWorkflow` from the root or `/forms` guards review, confirmation, stale revisions,
1122
+ duplicate submission and uncertain outcomes. Compose it with the installable React
1123
+ `review-workflow`, `import-review` and `record-workspace` recipes.
1124
+ See [operational recipes](https://github.com/santi020k/lumen/blob/main/docs/consumer-ui-recipes.md#installable-operational-workflows).
1125
+
1126
+ ## Studio media workspace
1127
+
1128
+ Compose MediaViewport, MediaThumbnail, MediaFilmstrip and ImageComparison modes with the
1129
+ [Studio media workspace recipes](https://github.com/santi020k/lumen/blob/main/docs/studio-media-workspaces.md). Applications retain
1130
+ media loading, selection, adjustment algorithms, processing, export and persistence.
1131
+
1132
+ ## Resources
1133
+
1134
+ | Guide | What you will find |
1135
+ | --- | --- |
1136
+ | [Consumer UI recipes](https://github.com/santi020k/lumen/blob/main/docs/consumer-ui-recipes.md) | Complete product compositions and application-owned behavior. |
1137
+ | [React Hook Form adapters](https://github.com/santi020k/lumen/blob/main/packages/react-hook-form/README.md) | Managed fields, composite controls, and schema validation. |
1138
+ | [Import and icon performance](https://github.com/santi020k/lumen/blob/main/docs/import-and-icon-performance.md) | Selective imports and reproducible bundle measurements. |
1139
+ | [Contributing](https://github.com/santi020k/lumen/blob/main/CONTRIBUTING.md) | Setup, checks, and contribution workflow. |
1140
+ | [Release history](https://github.com/santi020k/lumen/releases) | Published releases and version notes. |
1141
+
1142
+ Part of [Lumen UI](https://lumen.santi020k.com), created by [Santiago Molina](https://santi020k.com).
1143
+ Licensed under [MIT](https://github.com/santi020k/lumen/blob/main/LICENSE); third-party artwork retains its own notices.