ionbase-ui 0.84.0 → 0.88.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 (57) hide show
  1. package/dist/components/Checkbox.d.ts +39 -0
  2. package/dist/components/Checkbox.d.ts.map +1 -1
  3. package/dist/components/Checkbox.js +77 -4
  4. package/dist/components/Checkbox.js.map +1 -1
  5. package/dist/components/Fieldset.d.ts +62 -0
  6. package/dist/components/Fieldset.d.ts.map +1 -0
  7. package/dist/components/Fieldset.js +58 -0
  8. package/dist/components/Fieldset.js.map +1 -0
  9. package/dist/components/MultiSelect.d.ts +90 -0
  10. package/dist/components/MultiSelect.d.ts.map +1 -0
  11. package/dist/components/MultiSelect.js +189 -0
  12. package/dist/components/MultiSelect.js.map +1 -0
  13. package/dist/components/Radio.d.ts +14 -0
  14. package/dist/components/Radio.d.ts.map +1 -1
  15. package/dist/components/Radio.js +15 -4
  16. package/dist/components/Radio.js.map +1 -1
  17. package/dist/components/SearchField.d.ts +36 -0
  18. package/dist/components/SearchField.d.ts.map +1 -0
  19. package/dist/components/SearchField.js +76 -0
  20. package/dist/components/SearchField.js.map +1 -0
  21. package/dist/components/Toolbar.d.ts +35 -0
  22. package/dist/components/Toolbar.d.ts.map +1 -0
  23. package/dist/components/Toolbar.js +81 -0
  24. package/dist/components/Toolbar.js.map +1 -0
  25. package/dist/components/index.d.ts +10 -2
  26. package/dist/components/index.d.ts.map +1 -1
  27. package/dist/components/index.js +5 -1
  28. package/dist/components/index.js.map +1 -1
  29. package/dist/figma-descriptions.json +97 -77
  30. package/dist/figma-map.json +228 -4
  31. package/dist/meta/Checkbox.json +8 -6
  32. package/dist/meta/CheckboxGroup.json +268 -0
  33. package/dist/meta/Combobox.json +8 -0
  34. package/dist/meta/Divider.json +2 -2
  35. package/dist/meta/Fieldset.json +162 -0
  36. package/dist/meta/MultiSelect.json +269 -0
  37. package/dist/meta/Radio.json +0 -1
  38. package/dist/meta/RadioGroup.json +59 -7
  39. package/dist/meta/SearchField.json +487 -0
  40. package/dist/meta/Select.json +2 -1
  41. package/dist/meta/Stepper.json +2 -2
  42. package/dist/meta/Tabs.json +3 -3
  43. package/dist/meta/Toolbar.json +130 -0
  44. package/dist/meta/components.json +1616 -238
  45. package/dist/meta/contrast.json +398 -74
  46. package/dist/meta/index.json +84 -8
  47. package/dist/meta/patterns/DataTable.json +33 -6
  48. package/dist/meta/patterns/Form.json +27 -2
  49. package/dist/meta/patterns/index.json +6 -2
  50. package/dist/styles/fieldset.css +65 -0
  51. package/dist/styles/index.css +4 -0
  52. package/dist/styles/multi-select.css +104 -0
  53. package/dist/styles/radio.css +0 -20
  54. package/dist/styles/search-field.css +70 -0
  55. package/dist/styles/toolbar.css +36 -0
  56. package/llms.txt +2 -2
  57. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "package": "ionbase-ui",
3
- "version": "0.84.0",
3
+ "version": "0.88.0",
4
4
  "figmaFile": "gaLbGd0QNb1fUl6BjSpfBA",
5
5
  "figmaExported": "2026-09-25",
6
6
  "usage": "Given a Figma node id or component name, look it up here for the React component, its import, and how each Figma property maps onto a real prop. Every mapping is verified against both the Figma export and the TypeScript API on each build.",
@@ -539,6 +539,41 @@
539
539
  }
540
540
  }
541
541
  },
542
+ "1501:475": {
543
+ "figmaComponent": "Checkbox Group",
544
+ "figmaNodeId": "1501:475",
545
+ "figmaPage": "Checkbox",
546
+ "component": "CheckboxGroup",
547
+ "import": "import { CheckboxGroup } from 'ionbase-ui';",
548
+ "docs": "components/checkbox-group/index.html.md",
549
+ "props": {
550
+ "Orientation": {
551
+ "prop": "orientation",
552
+ "values": {
553
+ "Vertical": "vertical",
554
+ "Horizontal": "horizontal"
555
+ }
556
+ },
557
+ "State": {
558
+ "prop": "isInvalid",
559
+ "when": {
560
+ "Error": true
561
+ },
562
+ "note": "Error recolours the helper and shows errorMessage in its place; pass the error copy as errorMessage, not description. Disabled is isDisabled on the group, which cascades to every Checkbox."
563
+ },
564
+ "Label#1501:0": {
565
+ "prop": "label"
566
+ },
567
+ "Helper Text#1501:7": {
568
+ "prop": "description",
569
+ "note": "the help text; while State=Error the same slot holds errorMessage"
570
+ },
571
+ "Show Helper#1501:14": {
572
+ "prop": "description",
573
+ "note": "an absent prop is the switch, so false means omit description"
574
+ }
575
+ }
576
+ },
542
577
  "1167:247": {
543
578
  "figmaComponent": "Citation",
544
579
  "figmaNodeId": "1167:247",
@@ -1225,6 +1260,35 @@
1225
1260
  }
1226
1261
  }
1227
1262
  },
1263
+ "1510:666": {
1264
+ "figmaComponent": "Multi Select",
1265
+ "figmaNodeId": "1510:666",
1266
+ "figmaPage": "Multi Select",
1267
+ "component": "MultiSelect",
1268
+ "import": "import { MultiSelect } from 'ionbase-ui';",
1269
+ "docs": "components/multi-select/index.html.md",
1270
+ "props": {
1271
+ "Size": {
1272
+ "prop": "size",
1273
+ "values": {
1274
+ "Small": "sm",
1275
+ "Medium": "md",
1276
+ "Large": "lg"
1277
+ }
1278
+ },
1279
+ "State": {
1280
+ "prop": "isInvalid",
1281
+ "when": {
1282
+ "Invalid": true
1283
+ },
1284
+ "note": "Hover, Focus and Filled are CSS or content; Disabled is isDisabled and Read-only is isReadOnly, and both hide the tags’ × buttons. The field is Combobox’s, which is Input’s box — the same split both record."
1285
+ },
1286
+ "Show Tags#1510:0": {
1287
+ "prop": "hideTags",
1288
+ "note": "Inverted: Show Tags off is `hideTags`, for a filter bar whose active-filters row already shows the values. With tags shown, they are the chosen `value`, labelled from `options`, and appear whenever something is chosen."
1289
+ }
1290
+ }
1291
+ },
1228
1292
  "53:13": {
1229
1293
  "figmaComponent": "Nav Item",
1230
1294
  "figmaNodeId": "53:13",
@@ -1512,6 +1576,35 @@
1512
1576
  }
1513
1577
  }
1514
1578
  },
1579
+ "1498:242": {
1580
+ "figmaComponent": "Search Field",
1581
+ "figmaNodeId": "1498:242",
1582
+ "figmaPage": "Search Field",
1583
+ "component": "SearchField",
1584
+ "import": "import { SearchField } from 'ionbase-ui';",
1585
+ "docs": "components/search-field/index.html.md",
1586
+ "props": {
1587
+ "Size": {
1588
+ "prop": "size",
1589
+ "values": {
1590
+ "Small": "sm",
1591
+ "Medium": "md",
1592
+ "Large": "lg"
1593
+ }
1594
+ },
1595
+ "State": {
1596
+ "prop": "isInvalid",
1597
+ "when": {
1598
+ "Invalid": true
1599
+ },
1600
+ "note": "Hover, Focus and Filled are CSS or content; Disabled is isDisabled and Read-only is isReadOnly — Input's split, because the box is Input's. The clear button Filled shows is not a prop: it appears whenever there is a query."
1601
+ },
1602
+ "Value#80:0": {
1603
+ "prop": "value",
1604
+ "note": "The query. Figma's text is also the placeholder until one is typed — pass that as placeholder, and name the field with aria-label, not the placeholder."
1605
+ }
1606
+ }
1607
+ },
1515
1608
  "1390:435": {
1516
1609
  "figmaComponent": "Segmented Control",
1517
1610
  "figmaNodeId": "1390:435",
@@ -2252,6 +2345,24 @@
2252
2345
  }
2253
2346
  }
2254
2347
  },
2348
+ "1517:583": {
2349
+ "figmaComponent": "Toolbar",
2350
+ "figmaNodeId": "1517:583",
2351
+ "figmaPage": "Toolbar",
2352
+ "component": "Toolbar",
2353
+ "import": "import { Toolbar } from 'ionbase-ui';",
2354
+ "docs": "components/toolbar/index.html.md",
2355
+ "props": {
2356
+ "Orientation": {
2357
+ "prop": "orientation",
2358
+ "values": {
2359
+ "Horizontal": "horizontal",
2360
+ "Vertical": "vertical"
2361
+ },
2362
+ "note": "Horizontal moves with ← →, vertical with ↑ ↓. The children are the caller's: Buttons, a Divider between groups (vertical in a horizontal toolbar), and a MenuTrigger with an icon-only Button for the overflow. Name it with aria-label for what it acts on."
2363
+ }
2364
+ }
2365
+ },
2255
2366
  "801:68": {
2256
2367
  "figmaComponent": "Tooltip",
2257
2368
  "figmaNodeId": "801:68",
@@ -2892,6 +3003,41 @@
2892
3003
  }
2893
3004
  }
2894
3005
  },
3006
+ "Checkbox Group": {
3007
+ "figmaComponent": "Checkbox Group",
3008
+ "figmaNodeId": "1501:475",
3009
+ "figmaPage": "Checkbox",
3010
+ "component": "CheckboxGroup",
3011
+ "import": "import { CheckboxGroup } from 'ionbase-ui';",
3012
+ "docs": "components/checkbox-group/index.html.md",
3013
+ "props": {
3014
+ "Orientation": {
3015
+ "prop": "orientation",
3016
+ "values": {
3017
+ "Vertical": "vertical",
3018
+ "Horizontal": "horizontal"
3019
+ }
3020
+ },
3021
+ "State": {
3022
+ "prop": "isInvalid",
3023
+ "when": {
3024
+ "Error": true
3025
+ },
3026
+ "note": "Error recolours the helper and shows errorMessage in its place; pass the error copy as errorMessage, not description. Disabled is isDisabled on the group, which cascades to every Checkbox."
3027
+ },
3028
+ "Label#1501:0": {
3029
+ "prop": "label"
3030
+ },
3031
+ "Helper Text#1501:7": {
3032
+ "prop": "description",
3033
+ "note": "the help text; while State=Error the same slot holds errorMessage"
3034
+ },
3035
+ "Show Helper#1501:14": {
3036
+ "prop": "description",
3037
+ "note": "an absent prop is the switch, so false means omit description"
3038
+ }
3039
+ }
3040
+ },
2895
3041
  "Citation": {
2896
3042
  "figmaComponent": "Citation",
2897
3043
  "figmaNodeId": "1167:247",
@@ -3578,6 +3724,35 @@
3578
3724
  }
3579
3725
  }
3580
3726
  },
3727
+ "Multi Select": {
3728
+ "figmaComponent": "Multi Select",
3729
+ "figmaNodeId": "1510:666",
3730
+ "figmaPage": "Multi Select",
3731
+ "component": "MultiSelect",
3732
+ "import": "import { MultiSelect } from 'ionbase-ui';",
3733
+ "docs": "components/multi-select/index.html.md",
3734
+ "props": {
3735
+ "Size": {
3736
+ "prop": "size",
3737
+ "values": {
3738
+ "Small": "sm",
3739
+ "Medium": "md",
3740
+ "Large": "lg"
3741
+ }
3742
+ },
3743
+ "State": {
3744
+ "prop": "isInvalid",
3745
+ "when": {
3746
+ "Invalid": true
3747
+ },
3748
+ "note": "Hover, Focus and Filled are CSS or content; Disabled is isDisabled and Read-only is isReadOnly, and both hide the tags’ × buttons. The field is Combobox’s, which is Input’s box — the same split both record."
3749
+ },
3750
+ "Show Tags#1510:0": {
3751
+ "prop": "hideTags",
3752
+ "note": "Inverted: Show Tags off is `hideTags`, for a filter bar whose active-filters row already shows the values. With tags shown, they are the chosen `value`, labelled from `options`, and appear whenever something is chosen."
3753
+ }
3754
+ }
3755
+ },
3581
3756
  "Nav Item": {
3582
3757
  "figmaComponent": "Nav Item",
3583
3758
  "figmaNodeId": "53:13",
@@ -3865,6 +4040,35 @@
3865
4040
  }
3866
4041
  }
3867
4042
  },
4043
+ "Search Field": {
4044
+ "figmaComponent": "Search Field",
4045
+ "figmaNodeId": "1498:242",
4046
+ "figmaPage": "Search Field",
4047
+ "component": "SearchField",
4048
+ "import": "import { SearchField } from 'ionbase-ui';",
4049
+ "docs": "components/search-field/index.html.md",
4050
+ "props": {
4051
+ "Size": {
4052
+ "prop": "size",
4053
+ "values": {
4054
+ "Small": "sm",
4055
+ "Medium": "md",
4056
+ "Large": "lg"
4057
+ }
4058
+ },
4059
+ "State": {
4060
+ "prop": "isInvalid",
4061
+ "when": {
4062
+ "Invalid": true
4063
+ },
4064
+ "note": "Hover, Focus and Filled are CSS or content; Disabled is isDisabled and Read-only is isReadOnly — Input's split, because the box is Input's. The clear button Filled shows is not a prop: it appears whenever there is a query."
4065
+ },
4066
+ "Value#80:0": {
4067
+ "prop": "value",
4068
+ "note": "The query. Figma's text is also the placeholder until one is typed — pass that as placeholder, and name the field with aria-label, not the placeholder."
4069
+ }
4070
+ }
4071
+ },
3868
4072
  "Segmented Control": {
3869
4073
  "figmaComponent": "Segmented Control",
3870
4074
  "figmaNodeId": "1390:435",
@@ -4605,6 +4809,24 @@
4605
4809
  }
4606
4810
  }
4607
4811
  },
4812
+ "Toolbar": {
4813
+ "figmaComponent": "Toolbar",
4814
+ "figmaNodeId": "1517:583",
4815
+ "figmaPage": "Toolbar",
4816
+ "component": "Toolbar",
4817
+ "import": "import { Toolbar } from 'ionbase-ui';",
4818
+ "docs": "components/toolbar/index.html.md",
4819
+ "props": {
4820
+ "Orientation": {
4821
+ "prop": "orientation",
4822
+ "values": {
4823
+ "Horizontal": "horizontal",
4824
+ "Vertical": "vertical"
4825
+ },
4826
+ "note": "Horizontal moves with ← →, vertical with ↑ ↓. The children are the caller's: Buttons, a Divider between groups (vertical in a horizontal toolbar), and a MenuTrigger with an icon-only Button for the overflow. Name it with aria-label for what it acts on."
4827
+ }
4828
+ }
4829
+ },
4608
4830
  "Tooltip": {
4609
4831
  "figmaComponent": "Tooltip",
4610
4832
  "figmaNodeId": "801:68",
@@ -4727,18 +4949,20 @@
4727
4949
  "Screen Frame": "The frame FullCard draws around its media slot. The component owns it, so there is nothing for a caller to place.",
4728
4950
  "Side Menu Item": "A 32-tall side-navigation row on the Side Menu page, which predates Sidebar. It shared the name Menu Item with the real one (82:217) until 25 Sep 2026, and because the export keyed on the name it silently replaced it — the mapping, the gates and the Dev Mode block all pointed here. Sidebar Item is the side-navigation row code ships.",
4729
4951
  "Side Menu Section Title": "The section heading of the same pre-Sidebar side menu. Menu Section Title (1468:271) is the menu's own, drawn on the Menu page; Sidebar Section is the side navigation's.",
4730
- "Table/Cell Text": "TableCell covers Figma's Table Cell and Cell Text together; the two are never used apart in the design. Recorded in TableCell's contract."
4952
+ "Table/Cell Text": "TableCell covers Figma's Table Cell and Cell Text together; the two are never used apart in the design. Recorded in TableCell's contract.",
4953
+ "Multi Select Menu": "The list MultiSelect opens, with a check on each row. Not a separate export, for Combobox Menu's reason: the list and the field are one control, and a listbox with nothing to own it is not a thing a caller should be able to mount. Drawn separately because a frame cannot both hug a field and overlay a list."
4731
4954
  },
4732
4955
  "codeUnmapped": {
4733
4956
  "DateRangePicker": "Drawn, but not as a set of its own: its field is Date Picker Type=Range and its calendar is Calendar Mode=Range. A Figma set maps to exactly one code component, and splitting one field into two sets to satisfy that would draw the same box twice. Read Date Picker’s Type note for the handoff.",
4734
4957
  "Icon": "Figma has no Icon component; icons are vector assets dropped into slots. Icon is the React wrapper that gives any of them a size rung and an accessible label, so there is nothing on the Figma side to point at.",
4735
4958
  "LogoMark": "Logo-Ionbase maps to Logo. Figma spells the no-wordmark case as that component's Type axis rather than a separate component; LogoMark is how code spells the same thing.",
4736
- "RadioGroup": "Figma draws Radio only. The group is the thing that owns the selected value, which a static frame cannot express — Figma's Radio State axis is ignored for exactly this reason.",
4959
+ "RadioGroup": "Figma draws Radio only. The group is the thing that owns the selected value, which a static frame cannot express — Figma's Radio State axis is ignored for exactly this reason. Its label, help, error and orientation are the shell drawn as Checkbox Group; the two groups share it in code as well.",
4737
4960
  "ScrollProgress": "Drawn in Figma as its three parts — Line, Progress, Progress Heading — all of which the component owns internally. There is no whole-component frame to map.",
4738
4961
  "Table": "Figma draws Table Row and Table Cell; the table itself is the caller assembling them. Density, the one property Table owns, is mapped on Table Cell.",
4739
4962
  "TableBody": "As TableHead.",
4740
4963
  "TableHead": "As Table — a code-side grouping element. Figma expresses a header row as a Table Row variant, not a separate component.",
4741
4964
  "ToastProvider": "Figma draws Toast, which is mapped. The provider is placement, queueing and the live region — runtime behaviour with nothing to draw.",
4742
- "MenuTrigger": "Composition, not a drawing: a Button followed by a Menu, both already drawn and mapped. What MenuTrigger adds — open state, positioning, aria-haspopup and aria-expanded, focus on open and return on close — is runtime behaviour a static frame cannot hold, the same reason RadioGroup is here."
4965
+ "MenuTrigger": "Composition, not a drawing: a Button followed by a Menu, both already drawn and mapped. What MenuTrigger adds — open state, positioning, aria-haspopup and aria-expanded, focus on open and return on close — is runtime behaviour a static frame cannot hold, the same reason RadioGroup is here.",
4966
+ "Fieldset": "Drawn as the shell of Checkbox Group: the legend, the helper and the Error recolour are Form Field's type, laid around a set of options. A Fieldset of Inputs is the caller composing Form Fields under that legend, so there is no separate frame to point at."
4743
4967
  }
4744
4968
  }
@@ -8,8 +8,7 @@
8
8
  "summary": "A native checkbox with the system's styling, supporting an indeterminate state.",
9
9
  "useWhen": [
10
10
  "a single on/off choice inside a form that is submitted",
11
- "selecting rows in a Table",
12
- "several independent options where more than one may be picked"
11
+ "selecting rows in a Table"
13
12
  ],
14
13
  "useInstead": [
15
14
  {
@@ -17,6 +16,11 @@
17
16
  "use": "Toggle",
18
17
  "why": "a switch reads as a setting taking effect now; a checkbox reads as a value being collected"
19
18
  },
19
+ {
20
+ "when": "several options answer one question",
21
+ "use": "CheckboxGroup",
22
+ "why": "it announces the question with each option, carries the group's error, and can require at least one"
23
+ },
20
24
  {
21
25
  "when": "the options are mutually exclusive",
22
26
  "use": "RadioGroup"
@@ -148,8 +152,7 @@
148
152
  "sm",
149
153
  "md",
150
154
  "lg"
151
- ],
152
- "default": "md"
155
+ ]
153
156
  },
154
157
  "intent": {
155
158
  "type": "CheckboxIntent | undefined",
@@ -160,8 +163,7 @@
160
163
  "neutral",
161
164
  "danger",
162
165
  "brand"
163
- ],
164
- "default": "brand"
166
+ ]
165
167
  },
166
168
  "isIndeterminate": {
167
169
  "type": "boolean | undefined",
@@ -0,0 +1,268 @@
1
+ {
2
+ "name": "CheckboxGroup",
3
+ "source": "src/components/Checkbox.tsx",
4
+ "propsType": "CheckboxGroupProps",
5
+ "description": "A set of checkboxes that answers one question — \"notify me when…\", \"which\nregions\". Owns the selected values, the group's label, help text and error,\nand \"select at least one\".\n\nA row of loose Checkboxes can do none of that accessibly: the question is\nnot announced with the options, an error has nothing to attach to, and\n\"at least one\" has no native expression at all. See the `required` note in\nCheckbox for how that last one is done.",
6
+ "import": "import { CheckboxGroup } from 'ionbase-ui';",
7
+ "status": "stable",
8
+ "summary": "A set of checkboxes answering one question. Owns the selected values, the group's label, help and error, and \"select at least one\".",
9
+ "useWhen": [
10
+ "several options may be picked and they answer one question — \"notify me when\", \"regions\", \"columns to show\"",
11
+ "the group needs a rule of its own — at least one, or an error that belongs to the set rather than to an option"
12
+ ],
13
+ "useInstead": [
14
+ {
15
+ "when": "only one option may be picked",
16
+ "use": "RadioGroup"
17
+ },
18
+ {
19
+ "when": "the options are many, or space is tight",
20
+ "use": "MultiSelect",
21
+ "why": "past about seven options a list of boxes is longer than the form around it; MultiSelect filters them and shows the chosen ones as tags"
22
+ },
23
+ {
24
+ "when": "there is one on/off choice",
25
+ "use": "Checkbox",
26
+ "why": "a group of one announces a question and then its only answer"
27
+ },
28
+ {
29
+ "when": "each option takes effect immediately",
30
+ "use": "Toggle"
31
+ }
32
+ ],
33
+ "composition": {
34
+ "order": [
35
+ "CheckboxGroup",
36
+ "Checkbox"
37
+ ],
38
+ "note": "Give every Checkbox a `value`; the group's `onChange` receives the array of ticked values. Selection lives on the group — do not also set `isSelected` on the boxes.",
39
+ "example": "<CheckboxGroup label=\"Notify the team when\" isRequired value={notifyOn} onChange={setNotifyOn} isInvalid={!!error} errorMessage={error}><Checkbox value=\"failed\">A run fails</Checkbox><Checkbox value=\"approval\">A run needs approval</Checkbox></CheckboxGroup>"
40
+ },
41
+ "variants": {
42
+ "size": {
43
+ "sm": {
44
+ "use": "dense forms and filter panels"
45
+ },
46
+ "md": {
47
+ "use": "the default"
48
+ },
49
+ "lg": {
50
+ "use": "touch targets and prominent choices"
51
+ }
52
+ },
53
+ "intent": {
54
+ "brand": {
55
+ "use": "the default"
56
+ },
57
+ "neutral": {
58
+ "use": "facts rather than approvals — the columns shown, the filters applied"
59
+ },
60
+ "danger": {
61
+ "use": "a set of destructive consents"
62
+ }
63
+ },
64
+ "orientation": {
65
+ "vertical": {
66
+ "use": "the default, and always when a label runs past a few words"
67
+ },
68
+ "horizontal": {
69
+ "use": "two to four one-word options — weekdays, regions. Wraps rather than overflows"
70
+ }
71
+ }
72
+ },
73
+ "slots": {
74
+ "label": {
75
+ "accepts": "text",
76
+ "note": "renders as the <legend> — the question the options answer"
77
+ },
78
+ "description": {
79
+ "accepts": "text",
80
+ "note": "help beneath the options; replaced by `errorMessage` while `isInvalid` is set"
81
+ },
82
+ "errorMessage": {
83
+ "accepts": "text",
84
+ "note": "shown only while `isInvalid` is set"
85
+ },
86
+ "children": {
87
+ "accepts": "Checkbox"
88
+ }
89
+ },
90
+ "a11y": {
91
+ "role": "group, from <fieldset> and <legend>",
92
+ "guarantees": [
93
+ "the legend is announced as the group's name when focus enters any box",
94
+ "the description or error is on every box's `aria-describedby` as well as the fieldset's, so it is read on the box that takes focus",
95
+ "`isInvalid` sets `aria-invalid` on every box",
96
+ "`isRequired` sets native `required` on every box while none is ticked and on none once one is: the browser blocks the submit in its own language, and each box announces \"required\" exactly while the rule is unmet",
97
+ "`isDisabled` cascades to every Checkbox that has not set its own"
98
+ ],
99
+ "requires": [
100
+ "`label`, or `aria-label` when the question is already visible elsewhere",
101
+ "a `value` on every Checkbox — the group tracks the selection by it"
102
+ ]
103
+ },
104
+ "antiPatterns": [
105
+ {
106
+ "dont": "loose Checkboxes under a text label",
107
+ "why": "the question is not announced with the options, an error has nothing to attach to, and \"at least one\" cannot be expressed"
108
+ },
109
+ {
110
+ "dont": "`isSelected` or `checked` on a Checkbox inside a group",
111
+ "why": "the group owns the selection; the box's own value is ignored"
112
+ },
113
+ {
114
+ "dont": "`isRequired` on every Checkbox in the group",
115
+ "why": "that means every box must be ticked; `isRequired` on the group means at least one"
116
+ }
117
+ ],
118
+ "stylesheet": "src/styles/checkbox.css",
119
+ "tokens": [
120
+ "--border-disabled",
121
+ "--border-error-strong",
122
+ "--border-focus",
123
+ "--border-inverse",
124
+ "--border-primary-strong",
125
+ "--border-stronger",
126
+ "--border-width-default",
127
+ "--border-width-thick",
128
+ "--font-family-sans",
129
+ "--icon-disabled",
130
+ "--icon-on-color",
131
+ "--ion-checkbox-border",
132
+ "--ion-checkbox-fill",
133
+ "--ion-checkbox-font-size",
134
+ "--ion-checkbox-gap",
135
+ "--ion-checkbox-line-height",
136
+ "--ion-checkbox-mark",
137
+ "--ion-checkbox-radius",
138
+ "--ion-checkbox-raised",
139
+ "--ion-checkbox-size",
140
+ "--ion-duration-base",
141
+ "--ion-ease-out",
142
+ "--ion-shadow-raised-flush-lg",
143
+ "--ion-shadow-raised-flush-sm",
144
+ "--ion-shadow-raised-flush-xs",
145
+ "--radius-sm",
146
+ "--radius-xs",
147
+ "--spacing-12",
148
+ "--spacing-16",
149
+ "--spacing-20",
150
+ "--spacing-24",
151
+ "--spacing-4",
152
+ "--spacing-8",
153
+ "--surface-default",
154
+ "--surface-disabled",
155
+ "--surface-error",
156
+ "--surface-inverse",
157
+ "--surface-primary",
158
+ "--text-disabled",
159
+ "--text-secondary",
160
+ "--type-body",
161
+ "--type-body-line-height",
162
+ "--type-body-sm",
163
+ "--type-body-sm-line-height"
164
+ ],
165
+ "props": {
166
+ "label": {
167
+ "type": "React.ReactNode",
168
+ "required": false,
169
+ "origin": "own",
170
+ "description": "The question the options answer. Renders as the `<legend>`."
171
+ },
172
+ "description": {
173
+ "type": "React.ReactNode",
174
+ "required": false,
175
+ "origin": "own",
176
+ "description": "Help text beneath the options. Replaced by `errorMessage` while invalid."
177
+ },
178
+ "errorMessage": {
179
+ "type": "React.ReactNode",
180
+ "required": false,
181
+ "origin": "own",
182
+ "description": "Shown in the description's place while `isInvalid` is set."
183
+ },
184
+ "isInvalid": {
185
+ "type": "boolean | undefined",
186
+ "required": false,
187
+ "origin": "own",
188
+ "description": "Marks every box invalid and shows `errorMessage`."
189
+ },
190
+ "isRequired": {
191
+ "type": "boolean | undefined",
192
+ "required": false,
193
+ "origin": "own",
194
+ "description": "At least one option must be selected — enforced by native validation."
195
+ },
196
+ "value": {
197
+ "type": "readonly string[] | undefined",
198
+ "required": false,
199
+ "origin": "own",
200
+ "description": "The selected values (controlled)."
201
+ },
202
+ "defaultValue": {
203
+ "type": "readonly string[] | undefined",
204
+ "required": false,
205
+ "origin": "own",
206
+ "description": "The initially selected values (uncontrolled)."
207
+ },
208
+ "onChange": {
209
+ "type": "((value: string[]) => void) | undefined",
210
+ "required": false,
211
+ "origin": "own",
212
+ "description": "Receives the whole new selection, in the order the options were ticked."
213
+ },
214
+ "name": {
215
+ "type": "string | undefined",
216
+ "required": false,
217
+ "origin": "own",
218
+ "description": "Shared input name, so a form submits every ticked value under it."
219
+ },
220
+ "size": {
221
+ "type": "CheckboxSize | undefined",
222
+ "required": false,
223
+ "origin": "own",
224
+ "values": [
225
+ "sm",
226
+ "md",
227
+ "lg"
228
+ ]
229
+ },
230
+ "intent": {
231
+ "type": "CheckboxIntent | undefined",
232
+ "required": false,
233
+ "origin": "own",
234
+ "values": [
235
+ "neutral",
236
+ "danger",
237
+ "brand"
238
+ ]
239
+ },
240
+ "isDisabled": {
241
+ "type": "boolean | undefined",
242
+ "required": false,
243
+ "origin": "own",
244
+ "description": "Whether every checkbox in the group is disabled."
245
+ },
246
+ "orientation": {
247
+ "type": "FieldsetOrientation | undefined",
248
+ "required": false,
249
+ "origin": "own",
250
+ "values": [
251
+ "vertical",
252
+ "horizontal"
253
+ ]
254
+ },
255
+ "children": {
256
+ "type": "React.ReactNode",
257
+ "required": false,
258
+ "origin": "own",
259
+ "description": "Checkboxes, each with a `value`."
260
+ }
261
+ },
262
+ "propCounts": {
263
+ "own": 14,
264
+ "aria": 0,
265
+ "dom": 276,
266
+ "other": 0
267
+ }
268
+ }
@@ -17,6 +17,11 @@
17
17
  "use": "Select",
18
18
  "why": "the native control brings keyboard handling and the mobile platform picker for free; this rebuilds all of it"
19
19
  },
20
+ {
21
+ "when": "more than one value may be picked",
22
+ "use": "MultiSelect",
23
+ "why": "its input holds only the filter text, the list stays open between picks, and the chosen values show as tags"
24
+ },
20
25
  {
21
26
  "when": "there are two or three mutually exclusive choices worth showing at once",
22
27
  "use": "RadioGroup"
@@ -58,6 +63,9 @@
58
63
  "requires": [
59
64
  "`label`, or an `aria-label` when no visible label is rendered",
60
65
  "`emptyLabel` wording that says why nothing is listed, if the default is too terse"
66
+ ],
67
+ "notes": [
68
+ "While the list is open, React Aria hides everything outside the field and the list from assistive technology. That is the platform pattern for a combobox; a whole-page accessibility scan taken with the list open reports the hidden page, not this control."
61
69
  ]
62
70
  },
63
71
  "antiPatterns": [
@@ -55,8 +55,8 @@
55
55
  "origin": "own",
56
56
  "description": "Matches Figma's `Style` variant.",
57
57
  "values": [
58
- "horizontal",
59
- "vertical"
58
+ "vertical",
59
+ "horizontal"
60
60
  ]
61
61
  }
62
62
  },