ionbase-ui 0.51.0 → 0.69.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 (174) hide show
  1. package/dist/components/Accordion.d.ts +54 -0
  2. package/dist/components/Accordion.d.ts.map +1 -0
  3. package/dist/components/Accordion.js +68 -0
  4. package/dist/components/Accordion.js.map +1 -0
  5. package/dist/components/AgentActivity.d.ts +2 -1
  6. package/dist/components/AgentActivity.d.ts.map +1 -1
  7. package/dist/components/AgentActivity.js +4 -17
  8. package/dist/components/AgentActivity.js.map +1 -1
  9. package/dist/components/Breadcrumb.d.ts +45 -0
  10. package/dist/components/Breadcrumb.d.ts.map +1 -0
  11. package/dist/components/Breadcrumb.js +31 -0
  12. package/dist/components/Breadcrumb.js.map +1 -0
  13. package/dist/components/Calendar.d.ts +20 -0
  14. package/dist/components/Calendar.d.ts.map +1 -0
  15. package/dist/components/Calendar.js +151 -0
  16. package/dist/components/Calendar.js.map +1 -0
  17. package/dist/components/CalendarPopover.d.ts +26 -0
  18. package/dist/components/CalendarPopover.d.ts.map +1 -0
  19. package/dist/components/CalendarPopover.js +45 -0
  20. package/dist/components/CalendarPopover.js.map +1 -0
  21. package/dist/components/Checkbox.d.ts +2 -1
  22. package/dist/components/Checkbox.d.ts.map +1 -1
  23. package/dist/components/Checkbox.js +4 -2
  24. package/dist/components/Checkbox.js.map +1 -1
  25. package/dist/components/Combobox.d.ts +86 -0
  26. package/dist/components/Combobox.d.ts.map +1 -0
  27. package/dist/components/Combobox.js +261 -0
  28. package/dist/components/Combobox.js.map +1 -0
  29. package/dist/components/DateField.d.ts +15 -0
  30. package/dist/components/DateField.d.ts.map +1 -0
  31. package/dist/components/DateField.js +74 -0
  32. package/dist/components/DateField.js.map +1 -0
  33. package/dist/components/DatePicker.d.ts +81 -0
  34. package/dist/components/DatePicker.d.ts.map +1 -0
  35. package/dist/components/DatePicker.js +102 -0
  36. package/dist/components/DatePicker.js.map +1 -0
  37. package/dist/components/DateRangePicker.d.ts +117 -0
  38. package/dist/components/DateRangePicker.d.ts.map +1 -0
  39. package/dist/components/DateRangePicker.js +152 -0
  40. package/dist/components/DateRangePicker.js.map +1 -0
  41. package/dist/components/Drawer.d.ts +52 -0
  42. package/dist/components/Drawer.d.ts.map +1 -0
  43. package/dist/components/Drawer.js +66 -0
  44. package/dist/components/Drawer.js.map +1 -0
  45. package/dist/components/FileUpload.d.ts +79 -0
  46. package/dist/components/FileUpload.d.ts.map +1 -0
  47. package/dist/components/FileUpload.js +218 -0
  48. package/dist/components/FileUpload.js.map +1 -0
  49. package/dist/components/Header.d.ts +10 -0
  50. package/dist/components/Header.d.ts.map +1 -1
  51. package/dist/components/Header.js +18 -3
  52. package/dist/components/Header.js.map +1 -1
  53. package/dist/components/NumberInput.d.ts +63 -0
  54. package/dist/components/NumberInput.d.ts.map +1 -0
  55. package/dist/components/NumberInput.js +102 -0
  56. package/dist/components/NumberInput.js.map +1 -0
  57. package/dist/components/ProgressBar.d.ts +46 -0
  58. package/dist/components/ProgressBar.d.ts.map +1 -0
  59. package/dist/components/ProgressBar.js +52 -0
  60. package/dist/components/ProgressBar.js.map +1 -0
  61. package/dist/components/PromptInput.d.ts +89 -0
  62. package/dist/components/PromptInput.d.ts.map +1 -0
  63. package/dist/components/PromptInput.js +130 -0
  64. package/dist/components/PromptInput.js.map +1 -0
  65. package/dist/components/SegmentedControl.d.ts +63 -0
  66. package/dist/components/SegmentedControl.d.ts.map +1 -0
  67. package/dist/components/SegmentedControl.js +79 -0
  68. package/dist/components/SegmentedControl.js.map +1 -0
  69. package/dist/components/Select.d.ts +9 -2
  70. package/dist/components/Select.d.ts.map +1 -1
  71. package/dist/components/Select.js.map +1 -1
  72. package/dist/components/Sidebar.d.ts +109 -0
  73. package/dist/components/Sidebar.d.ts.map +1 -0
  74. package/dist/components/Sidebar.js +106 -0
  75. package/dist/components/Sidebar.js.map +1 -0
  76. package/dist/components/Skeleton.d.ts +39 -0
  77. package/dist/components/Skeleton.d.ts.map +1 -0
  78. package/dist/components/Skeleton.js +50 -0
  79. package/dist/components/Skeleton.js.map +1 -0
  80. package/dist/components/Spinner.d.ts +45 -0
  81. package/dist/components/Spinner.d.ts.map +1 -0
  82. package/dist/components/Spinner.js +44 -0
  83. package/dist/components/Spinner.js.map +1 -0
  84. package/dist/components/Stepper.d.ts +73 -0
  85. package/dist/components/Stepper.d.ts.map +1 -0
  86. package/dist/components/Stepper.js +82 -0
  87. package/dist/components/Stepper.js.map +1 -0
  88. package/dist/components/Toggle.d.ts +2 -1
  89. package/dist/components/Toggle.d.ts.map +1 -1
  90. package/dist/components/Toggle.js +4 -2
  91. package/dist/components/Toggle.js.map +1 -1
  92. package/dist/components/ToolCall.d.ts +66 -0
  93. package/dist/components/ToolCall.d.ts.map +1 -0
  94. package/dist/components/ToolCall.js +78 -0
  95. package/dist/components/ToolCall.js.map +1 -0
  96. package/dist/components/agent-status.d.ts +14 -0
  97. package/dist/components/agent-status.d.ts.map +1 -0
  98. package/dist/components/agent-status.js +16 -0
  99. package/dist/components/agent-status.js.map +1 -0
  100. package/dist/components/index.d.ts +33 -0
  101. package/dist/components/index.d.ts.map +1 -1
  102. package/dist/components/index.js +16 -0
  103. package/dist/components/index.js.map +1 -1
  104. package/dist/components/iso-date.d.ts +53 -0
  105. package/dist/components/iso-date.d.ts.map +1 -0
  106. package/dist/components/iso-date.js +102 -0
  107. package/dist/components/iso-date.js.map +1 -0
  108. package/dist/components/resolve-selection.d.ts +36 -0
  109. package/dist/components/resolve-selection.d.ts.map +1 -0
  110. package/dist/components/resolve-selection.js +14 -0
  111. package/dist/components/resolve-selection.js.map +1 -0
  112. package/dist/figma-descriptions.json +255 -145
  113. package/dist/figma-map.json +3205 -1860
  114. package/dist/meta/Accordion.json +123 -0
  115. package/dist/meta/AccordionItem.json +90 -0
  116. package/dist/meta/AgentActivityStep.json +2 -2
  117. package/dist/meta/Breadcrumb.json +87 -0
  118. package/dist/meta/BreadcrumbItem.json +78 -0
  119. package/dist/meta/Checkbox.json +18 -1
  120. package/dist/meta/Combobox.json +265 -0
  121. package/dist/meta/DatePicker.json +270 -0
  122. package/dist/meta/DateRangePicker.json +284 -0
  123. package/dist/meta/Drawer.json +234 -0
  124. package/dist/meta/EmptyState.json +1 -1
  125. package/dist/meta/FileUpload.json +246 -0
  126. package/dist/meta/Header.json +32 -2
  127. package/dist/meta/NumberInput.json +407 -0
  128. package/dist/meta/PhoneInput.json +39 -39
  129. package/dist/meta/Popover.json +3 -3
  130. package/dist/meta/ProgressBar.json +166 -0
  131. package/dist/meta/PromptInput.json +212 -0
  132. package/dist/meta/SegmentedControl.json +217 -0
  133. package/dist/meta/SegmentedControlItem.json +122 -0
  134. package/dist/meta/Select.json +4 -3
  135. package/dist/meta/Sidebar.json +150 -0
  136. package/dist/meta/SidebarItem.json +184 -0
  137. package/dist/meta/SidebarSection.json +131 -0
  138. package/dist/meta/Skeleton.json +120 -0
  139. package/dist/meta/Spinner.json +123 -0
  140. package/dist/meta/Stepper.json +135 -0
  141. package/dist/meta/StepperStep.json +148 -0
  142. package/dist/meta/Tabs.json +5 -0
  143. package/dist/meta/Toggle.json +18 -1
  144. package/dist/meta/ToolCall.json +192 -0
  145. package/dist/meta/Tooltip.json +3 -3
  146. package/dist/meta/components.json +6759 -2705
  147. package/dist/meta/contrast.json +11075 -3098
  148. package/dist/meta/index.json +266 -8
  149. package/dist/meta/patterns/AgentRun.json +27 -0
  150. package/dist/meta/patterns/Form.json +9 -0
  151. package/dist/meta/patterns/PageShell.json +43 -3
  152. package/dist/meta/patterns/Wizard.json +20 -1
  153. package/dist/meta/patterns/index.json +10 -1
  154. package/dist/styles/accordion.css +102 -0
  155. package/dist/styles/breadcrumb.css +72 -0
  156. package/dist/styles/combobox.css +154 -0
  157. package/dist/styles/date-picker.css +420 -0
  158. package/dist/styles/date-range-picker.css +280 -0
  159. package/dist/styles/drawer.css +181 -0
  160. package/dist/styles/empty-state.css +16 -4
  161. package/dist/styles/file-upload.css +236 -0
  162. package/dist/styles/header.css +26 -0
  163. package/dist/styles/index.css +23 -0
  164. package/dist/styles/number-input.css +83 -0
  165. package/dist/styles/progress-bar.css +107 -0
  166. package/dist/styles/prompt-input.css +127 -0
  167. package/dist/styles/segmented-control.css +182 -0
  168. package/dist/styles/sidebar.css +307 -0
  169. package/dist/styles/skeleton.css +76 -0
  170. package/dist/styles/spinner.css +70 -0
  171. package/dist/styles/stepper.css +250 -0
  172. package/dist/styles/tool-call.css +202 -0
  173. package/llms.txt +2 -2
  174. package/package.json +2 -1
@@ -0,0 +1,265 @@
1
+ {
2
+ "name": "Combobox",
3
+ "source": "src/components/Combobox.tsx",
4
+ "propsType": "ComboboxProps",
5
+ "description": "Combobox — a text field that filters a list, with one selected value.\n\nDrawn in Figma as `Combobox` (1370:2359) — three sizes by seven states, the\nsame axes `Input` carries, because the field IS Input's box. The open list is\na second Figma component, `Combobox Menu`, with no React export behind it.\n\nWHY THIS IS NOT `Select` WITH A SEARCH BOX\n\n`Select` wraps a native `<select>`, whose list the browser owns: it cannot be\nfiltered, and its rows can only hold text. Everything above about twenty\noptions needs filtering, and `Select`'s own contract has pointed at \"a\ncombobox — not yet in this system\" since it was written. This is it.\n\nThe cost is that everything the native control gave away for free — keyboard\nhandling, the mobile picker, screen-reader semantics — now has to be built.\nReact Aria's `useComboBox` builds it: `role=\"combobox\"` with `aria-expanded`,\n`aria-controls` and `aria-activedescendant`, arrow keys that move a virtual\nfocus while the real focus stays in the input, and Escape that reverts.\n\nFILTERING IS LOCALE-AWARE, NOT `toLowerCase().includes()`\n\n`useFilter({ sensitivity: 'base' })` is `Intl.Collator` underneath, so\n\"resume\" matches \"résumé\" and Turkish dotted/dotless I behave the way a\nTurkish reader expects. The lowercase-and-includes version fails both, and\nfails them silently in exactly the locales least likely to be tested.",
6
+ "import": "import { Combobox } from 'ionbase-ui';",
7
+ "status": "stable",
8
+ "summary": "A text field that filters a list of options down to one selected value.",
9
+ "useWhen": [
10
+ "the list is long enough that scanning it is worse than typing",
11
+ "the user picks exactly one value from a known set",
12
+ "the options carry a second line of context the native <select> cannot render"
13
+ ],
14
+ "useInstead": [
15
+ {
16
+ "when": "the list is short and fully scannable",
17
+ "use": "Select",
18
+ "why": "the native control brings keyboard handling and the mobile platform picker for free; this rebuilds all of it"
19
+ },
20
+ {
21
+ "when": "there are two or three mutually exclusive choices worth showing at once",
22
+ "use": "RadioGroup"
23
+ },
24
+ {
25
+ "when": "the value is free text and the list is only a convenience",
26
+ "use": "Input",
27
+ "why": "or set `allowsCustomValue`, which is the same decision made explicitly"
28
+ },
29
+ {
30
+ "when": "the rows are commands rather than values",
31
+ "use": "Menu",
32
+ "why": "options are selected and stay selected; menu items are invoked and the menu closes"
33
+ }
34
+ ],
35
+ "variants": {
36
+ "size": {
37
+ "sm": {
38
+ "use": "dense forms, table filters"
39
+ },
40
+ "md": {
41
+ "use": "the default"
42
+ },
43
+ "lg": {
44
+ "use": "prominent single-field forms"
45
+ }
46
+ }
47
+ },
48
+ "a11y": {
49
+ "guarantees": [
50
+ "role=combobox with aria-expanded, aria-controls and aria-activedescendant, from react-aria's useComboBox",
51
+ "real focus never leaves the input; arrow keys move a virtual focus through the list, which is what keeps typing and browsing possible at once",
52
+ "the popover is non-modal, so the page behind stays readable and focus stays in the field",
53
+ "Escape reverts to the last committed value rather than clearing the field",
54
+ "filtering is Intl.Collator-based, so accents and locale-specific casing behave correctly",
55
+ "the empty message is a sibling of the listbox, not an option in it, so it is never counted, focused or selectable",
56
+ "the menu matches the field's width, clamped to the space right of it so a full-bleed field cannot push the menu past the viewport edge"
57
+ ],
58
+ "requires": [
59
+ "`label`, or an `aria-label` when no visible label is rendered",
60
+ "`emptyLabel` wording that says why nothing is listed, if the default is too terse"
61
+ ]
62
+ },
63
+ "antiPatterns": [
64
+ {
65
+ "dont": "giving the disclosure button a tab stop",
66
+ "why": "the input already opens the list with ArrowDown. A second tab stop doubles the cost of tabbing past every combobox in a form and adds nothing"
67
+ },
68
+ {
69
+ "dont": "styling the focused option with :hover or :focus",
70
+ "why": "no option is ever either — focus is virtual. Use [data-focused], which react-aria sets for both pointer and keyboard"
71
+ },
72
+ {
73
+ "dont": "posting the input's text in a form",
74
+ "why": "the text is the label and the value is the key; pass `name` and the component posts the key in a hidden input"
75
+ },
76
+ {
77
+ "dont": "filtering with toLowerCase().includes()",
78
+ "why": "it fails on accents and on Turkish dotted/dotless I, silently, in the locales least likely to be tested"
79
+ },
80
+ {
81
+ "dont": "writing a width onto the menu from a ResizeObserver or any effect that runs while it is open",
82
+ "why": "a positioned popover that changes size feeds back into react-aria's positioning, and any scrollbar that results is read by useCloseOnScroll as a real scroll. The menu opens and closes within about a hundred milliseconds, which presents as a keyboard bug rather than a layout one"
83
+ }
84
+ ],
85
+ "slots": {
86
+ "emptyLabel": {
87
+ "expects": "what to show when the filter matches nothing"
88
+ }
89
+ },
90
+ "stylesheet": "src/styles/combobox.css",
91
+ "tokens": [
92
+ "--border-subtle",
93
+ "--border-width-default",
94
+ "--font-family-sans",
95
+ "--font-weight-medium",
96
+ "--icon-disabled",
97
+ "--icon-secondary",
98
+ "--ion-duration-base",
99
+ "--ion-ease-out",
100
+ "--ion-input-icon-size",
101
+ "--ion-shadow-shadow-lg",
102
+ "--radius-md",
103
+ "--radius-sm",
104
+ "--spacing-12",
105
+ "--spacing-2",
106
+ "--spacing-4",
107
+ "--spacing-6",
108
+ "--spacing-8",
109
+ "--surface-hover",
110
+ "--surface-primary-subtle",
111
+ "--surface-raised",
112
+ "--text-default",
113
+ "--text-disabled",
114
+ "--text-tertiary",
115
+ "--type-body",
116
+ "--type-body-line-height",
117
+ "--type-body-sm",
118
+ "--type-body-sm-line-height"
119
+ ],
120
+ "props": {
121
+ "options": {
122
+ "type": "readonly ComboboxOption[]",
123
+ "required": true,
124
+ "origin": "own",
125
+ "description": "The full option list. Filtering happens here, against what is typed."
126
+ },
127
+ "label": {
128
+ "type": "React.ReactNode",
129
+ "required": false,
130
+ "origin": "own",
131
+ "description": "Field label. Required for a usable control — see `a11y.requires`."
132
+ },
133
+ "description": {
134
+ "type": "React.ReactNode",
135
+ "required": false,
136
+ "origin": "own",
137
+ "description": "Helper text below the field."
138
+ },
139
+ "errorMessage": {
140
+ "type": "React.ReactNode",
141
+ "required": false,
142
+ "origin": "own",
143
+ "description": "Replaces the helper text when `isInvalid` is set."
144
+ },
145
+ "isInvalid": {
146
+ "type": "boolean | undefined",
147
+ "required": false,
148
+ "origin": "own"
149
+ },
150
+ "isDisabled": {
151
+ "type": "boolean | undefined",
152
+ "required": false,
153
+ "origin": "own"
154
+ },
155
+ "isReadOnly": {
156
+ "type": "boolean | undefined",
157
+ "required": false,
158
+ "origin": "own"
159
+ },
160
+ "size": {
161
+ "type": "ComboboxSize | undefined",
162
+ "required": false,
163
+ "origin": "own",
164
+ "description": "Matches Input's `Size` variant: Small, Medium, Large.",
165
+ "values": [
166
+ "sm",
167
+ "md",
168
+ "lg"
169
+ ]
170
+ },
171
+ "placeholder": {
172
+ "type": "string | undefined",
173
+ "required": false,
174
+ "origin": "own"
175
+ },
176
+ "selectedKey": {
177
+ "type": "string | null | undefined",
178
+ "required": false,
179
+ "origin": "own",
180
+ "description": "Controlled selection. `null` means nothing is selected."
181
+ },
182
+ "defaultSelectedKey": {
183
+ "type": "string | undefined",
184
+ "required": false,
185
+ "origin": "own"
186
+ },
187
+ "onSelectionChange": {
188
+ "type": "((key: string | null) => void) | undefined",
189
+ "required": false,
190
+ "origin": "own"
191
+ },
192
+ "inputValue": {
193
+ "type": "string | undefined",
194
+ "required": false,
195
+ "origin": "own",
196
+ "description": "Controlled text. Usually only needed for async/remote filtering."
197
+ },
198
+ "defaultInputValue": {
199
+ "type": "string | undefined",
200
+ "required": false,
201
+ "origin": "own"
202
+ },
203
+ "onInputChange": {
204
+ "type": "((value: string) => void) | undefined",
205
+ "required": false,
206
+ "origin": "own"
207
+ },
208
+ "allowsCustomValue": {
209
+ "type": "boolean | undefined",
210
+ "required": false,
211
+ "origin": "own",
212
+ "description": "Accept text that matches no option. Off by default: a combobox whose value\nis a free string is a text field with suggestions, and the caller should\nhave to say that is what they want."
213
+ },
214
+ "menuTrigger": {
215
+ "type": "\"focus\" | \"input\" | \"manual\" | undefined",
216
+ "required": false,
217
+ "origin": "own",
218
+ "description": "What opens the list. `input` — react-aria's default and this one — opens it\non the first keystroke; the chevron opens it on demand. `focus` opens it\nthe moment the field is tabbed into, which is loud in a long form.",
219
+ "values": [
220
+ "focus",
221
+ "input",
222
+ "manual"
223
+ ]
224
+ },
225
+ "emptyLabel": {
226
+ "type": "React.ReactNode",
227
+ "required": false,
228
+ "origin": "own",
229
+ "description": "Shown in place of the list when nothing matches."
230
+ },
231
+ "buttonLabel": {
232
+ "type": "string | undefined",
233
+ "required": false,
234
+ "origin": "own",
235
+ "description": "Accessible label for the disclosure button."
236
+ },
237
+ "name": {
238
+ "type": "string | undefined",
239
+ "required": false,
240
+ "origin": "own",
241
+ "description": "Posts the selected value under this name, for an uncontrolled form."
242
+ },
243
+ "className": {
244
+ "type": "string | undefined",
245
+ "required": false,
246
+ "origin": "own"
247
+ },
248
+ "wrapperClassName": {
249
+ "type": "string | undefined",
250
+ "required": false,
251
+ "origin": "own"
252
+ },
253
+ "id": {
254
+ "type": "string | undefined",
255
+ "required": false,
256
+ "origin": "own"
257
+ }
258
+ },
259
+ "propCounts": {
260
+ "own": 23,
261
+ "aria": 0,
262
+ "dom": 0,
263
+ "other": 0
264
+ }
265
+ }
@@ -0,0 +1,270 @@
1
+ {
2
+ "name": "DatePicker",
3
+ "source": "src/components/DatePicker.tsx",
4
+ "propsType": "DatePickerProps",
5
+ "description": "DatePicker — a segmented date field with a calendar attached.\n\nWHY NOT `<input type=\"date\">`\n\nThe native control is genuinely good on mobile and genuinely unusable as a\ndesign system component on desktop: its calendar cannot be styled at all, it\ncannot express an unavailable date that is not part of a continuous min/max\nrange, and Safari on macOS renders no calendar button whatsoever. A system\nwhose date field looks like a different control in every browser is not a\ndesign system, so this rebuilds it — and pays for that by having to rebuild\nthe keyboard handling and the screen-reader semantics the native control gave\naway for free. React Aria's `useDatePicker` is what builds them back.\n\nTHE VALUE IS A STRING, THE DISPLAY IS NOT\n\n`value` is always `YYYY-MM-DD` in the Gregorian calendar. What the user SEES\nis their own locale's field order and their own calendar system, resolved\nfrom `Intl` — so a German reader edits DD.MM.YYYY and the value still comes\nback as `2026-04-12`. See `iso-date.ts` for why the public type is not\nreact-aria's `CalendarDate`.\n\nTHE FIELD IS EDITABLE WITHOUT THE CALENDAR, AND THAT IS THE POINT\n\nTyping a date is faster than clicking one for anybody who knows the date they\nwant, and paging a calendar back to a birth year is miserable. The segments\ntake digits and arrow keys directly; the calendar is for the cases where the\ndate is being chosen rather than recalled.",
6
+ "import": "import { DatePicker } from 'ionbase-ui';",
7
+ "status": "stable",
8
+ "summary": "A segmented date field with a calendar attached, taking and returning a YYYY-MM-DD string.",
9
+ "useWhen": [
10
+ "the user picks one calendar date — an event date, a due date, a birthday",
11
+ "the date has to be typed as well as clicked, because the user often already knows it",
12
+ "some dates must be refused individually rather than as a continuous range"
13
+ ],
14
+ "useInstead": [
15
+ {
16
+ "when": "the user picks a start and an end",
17
+ "use": "DateRangePicker",
18
+ "why": "two DatePickers cannot stop the end preceding the start, cannot show the days between them, and give the user two calendars to reconcile from memory"
19
+ },
20
+ {
21
+ "when": "the value is a month or a year rather than a day",
22
+ "use": "Select",
23
+ "why": "a calendar grid to choose 'March' is twelve options rendered as thirty-one"
24
+ },
25
+ {
26
+ "when": "the field is one of a short, known set of dates the product defines",
27
+ "use": "Select or RadioGroup",
28
+ "why": "picking from four delivery slots is a choice from a list, not a date"
29
+ },
30
+ {
31
+ "when": "a time of day is part of the value",
32
+ "use": "Input",
33
+ "why": "this picker is fixed to day granularity; a time picker is not yet in this system"
34
+ }
35
+ ],
36
+ "variants": {
37
+ "size": {
38
+ "sm": {
39
+ "use": "dense forms, table filters"
40
+ },
41
+ "md": {
42
+ "use": "the default"
43
+ },
44
+ "lg": {
45
+ "use": "prominent single-field forms"
46
+ }
47
+ }
48
+ },
49
+ "a11y": {
50
+ "guarantees": [
51
+ "each editable segment is its own tab stop, as in Chrome's native <input type=date>; the locale's separators are not, and arrow keys move between segments as well, so neither convention has to be learned",
52
+ "the segments sit in one labelled group, so the field's name is announced once on entry rather than repeated per segment",
53
+ "each segment announces its own name, value and bounds, so 'month, 4, minimum 1, maximum 12' is heard rather than an opaque number",
54
+ "field order and separators come from Intl, so a German reader edits DD.MM.YYYY and a US reader MM/DD/YYYY from the same component and the same value",
55
+ "the calendar overlay is modal: focus moves into the grid, the rest of the page is hidden from assistive tech, Escape closes it and focus returns to the field",
56
+ "every day cell carries the full date as its accessible name in the reader's own language, so the narrow weekday headers are never load-bearing",
57
+ "unavailable and disabled days are distinguished visually and in the accessible name, not by colour alone",
58
+ "today is marked with a dot rather than a ring, so it cannot be mistaken for a second selected day",
59
+ "the selected fill and the segment focus box are redeclared under forced-colors, where background-color is discarded"
60
+ ],
61
+ "requires": [
62
+ "`label`, or an `aria-label` when no visible label is rendered",
63
+ "`calendarLabel` wording if 'Open calendar' is wrong for the context — it is the button's whole accessible name"
64
+ ]
65
+ },
66
+ "antiPatterns": [
67
+ {
68
+ "dont": "passing a Date object, a timestamp, or a locale-formatted string as `value`",
69
+ "why": "the value is a calendar date with no time and no zone, spelled YYYY-MM-DD. `new Date('2026-04-12')` parses as UTC midnight and displays as the 11th for every reader west of Greenwich. A malformed value throws with the prop name rather than rendering an empty field"
70
+ },
71
+ {
72
+ "dont": "using `isDateUnavailable` to express a lower or upper bound",
73
+ "why": "`minValue` and `maxValue` also disable the navigation arrows past the bound, so the user cannot page into a month with nothing selectable in it. A predicate is for the holes inside the bounds"
74
+ },
75
+ {
76
+ "dont": "hiding the field and leaving only the calendar button",
77
+ "why": "typing a date is faster than clicking one for anyone who already knows it, and paging a calendar back to a birth year is the worst interaction in any form"
78
+ },
79
+ {
80
+ "dont": "styling a day cell with :hover for the keyboard highlight",
81
+ "why": "use [data-selected], [data-today], [data-disabled] and [data-unavailable]. Pointer hover and keyboard focus are different states here and a grid where they disagree has two apparent cursors"
82
+ },
83
+ {
84
+ "dont": "reading the posted form value from the segments",
85
+ "why": "they are spinbuttons with no name. Pass `name` and the component posts the ISO value in a hidden input"
86
+ },
87
+ {
88
+ "dont": "wrapping it in a <label>",
89
+ "why": "the group is a set of spinbuttons rather than one form control, so there is nothing for htmlFor to point at. Pass `label` and react-aria wires the name through aria-labelledby"
90
+ },
91
+ {
92
+ "dont": "adding a roving tabindex so the field becomes one tab stop",
93
+ "why": "it buys back two tabs per field and costs the ability to Tab straight to the year, which is the segment users most often fix on its own. It also diverges from the native date input, which is the convention they already have"
94
+ }
95
+ ],
96
+ "slots": {
97
+ "label": {
98
+ "expects": "what the date is for — 'Event starts', not 'Date'"
99
+ },
100
+ "description": {
101
+ "expects": "helper text under the field; replaced by `errorMessage` when `isInvalid`"
102
+ },
103
+ "errorMessage": {
104
+ "expects": "what is wrong and what would be right, shown only when `isInvalid`"
105
+ }
106
+ },
107
+ "stylesheet": "src/styles/date-picker.css",
108
+ "tokens": [
109
+ "--border-focus",
110
+ "--border-subtle",
111
+ "--border-width-default",
112
+ "--border-width-thick",
113
+ "--font-family-sans",
114
+ "--font-weight-medium",
115
+ "--font-weight-regular",
116
+ "--font-weight-semibold",
117
+ "--icon-disabled",
118
+ "--icon-interactive",
119
+ "--icon-on-color",
120
+ "--icon-primary",
121
+ "--icon-secondary",
122
+ "--icon-size-sm",
123
+ "--ion-calendar-day-size",
124
+ "--ion-input-icon-size",
125
+ "--ion-shadow-shadow-lg",
126
+ "--radius-2xs",
127
+ "--radius-full",
128
+ "--radius-md",
129
+ "--radius-sm",
130
+ "--spacing-12",
131
+ "--spacing-2",
132
+ "--spacing-24",
133
+ "--spacing-32",
134
+ "--spacing-4",
135
+ "--spacing-6",
136
+ "--spacing-8",
137
+ "--surface-hover",
138
+ "--surface-primary",
139
+ "--surface-raised",
140
+ "--text-default",
141
+ "--text-disabled",
142
+ "--text-on-color",
143
+ "--text-secondary",
144
+ "--text-tertiary",
145
+ "--type-body-sm",
146
+ "--type-body-sm-line-height",
147
+ "--type-caption",
148
+ "--type-caption-line-height"
149
+ ],
150
+ "props": {
151
+ "label": {
152
+ "type": "React.ReactNode",
153
+ "required": false,
154
+ "origin": "own",
155
+ "description": "Field label. Required for a usable control — see `a11y.requires`."
156
+ },
157
+ "description": {
158
+ "type": "React.ReactNode",
159
+ "required": false,
160
+ "origin": "own",
161
+ "description": "Helper text below the field."
162
+ },
163
+ "errorMessage": {
164
+ "type": "React.ReactNode",
165
+ "required": false,
166
+ "origin": "own",
167
+ "description": "Replaces the helper text when `isInvalid` is set."
168
+ },
169
+ "isInvalid": {
170
+ "type": "boolean | undefined",
171
+ "required": false,
172
+ "origin": "own"
173
+ },
174
+ "isDisabled": {
175
+ "type": "boolean | undefined",
176
+ "required": false,
177
+ "origin": "own"
178
+ },
179
+ "isReadOnly": {
180
+ "type": "boolean | undefined",
181
+ "required": false,
182
+ "origin": "own"
183
+ },
184
+ "isRequired": {
185
+ "type": "boolean | undefined",
186
+ "required": false,
187
+ "origin": "own"
188
+ },
189
+ "size": {
190
+ "type": "DatePickerSize | undefined",
191
+ "required": false,
192
+ "origin": "own",
193
+ "description": "Matches Input's `Size` variant: Small, Medium, Large.",
194
+ "values": [
195
+ "sm",
196
+ "md",
197
+ "lg"
198
+ ]
199
+ },
200
+ "value": {
201
+ "type": "string | null | undefined",
202
+ "required": false,
203
+ "origin": "own",
204
+ "description": "The selected date as `YYYY-MM-DD`. `null` means nothing is selected.\n\nNot a `Date` and not a timestamp — a calendar date has no time and no\ntimezone, so it cannot shift across midnight the way `new Date('2026-04-12')`\ndoes for every reader west of Greenwich. A malformed value throws rather\nthan rendering an empty field."
205
+ },
206
+ "defaultValue": {
207
+ "type": "string | undefined",
208
+ "required": false,
209
+ "origin": "own",
210
+ "description": "The initial date as `YYYY-MM-DD`, for an uncontrolled picker."
211
+ },
212
+ "onChange": {
213
+ "type": "((value: IsoDate | null) => void) | undefined",
214
+ "required": false,
215
+ "origin": "own",
216
+ "description": "Fires with `YYYY-MM-DD`, or `null` when the field is cleared."
217
+ },
218
+ "minValue": {
219
+ "type": "string | undefined",
220
+ "required": false,
221
+ "origin": "own",
222
+ "description": "Earliest selectable date, `YYYY-MM-DD`. Earlier dates are unreachable."
223
+ },
224
+ "maxValue": {
225
+ "type": "string | undefined",
226
+ "required": false,
227
+ "origin": "own",
228
+ "description": "Latest selectable date, `YYYY-MM-DD`."
229
+ },
230
+ "isDateUnavailable": {
231
+ "type": "((date: IsoDate) => boolean) | undefined",
232
+ "required": false,
233
+ "origin": "own",
234
+ "description": "Individual dates the calendar refuses — weekends, holidays, booked days.\nReceives `YYYY-MM-DD`. Use `minValue`/`maxValue` for a continuous bound;\nthis is for the holes inside it."
235
+ },
236
+ "name": {
237
+ "type": "string | undefined",
238
+ "required": false,
239
+ "origin": "own",
240
+ "description": "Posts the value under this name, for an uncontrolled form."
241
+ },
242
+ "calendarLabel": {
243
+ "type": "string | undefined",
244
+ "required": false,
245
+ "origin": "own",
246
+ "description": "Accessible label for the button that opens the calendar."
247
+ },
248
+ "className": {
249
+ "type": "string | undefined",
250
+ "required": false,
251
+ "origin": "own"
252
+ },
253
+ "wrapperClassName": {
254
+ "type": "string | undefined",
255
+ "required": false,
256
+ "origin": "own"
257
+ },
258
+ "id": {
259
+ "type": "string | undefined",
260
+ "required": false,
261
+ "origin": "own"
262
+ }
263
+ },
264
+ "propCounts": {
265
+ "own": 19,
266
+ "aria": 0,
267
+ "dom": 0,
268
+ "other": 0
269
+ }
270
+ }