@capra/core 1.13.0 → 1.14.1

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.
@@ -105,6 +105,6 @@ Don't:
105
105
 
106
106
  ## Related components
107
107
 
108
- * [Toast](?path=/docs/core-toast--design) — For non-blocking, auto-dismissing notifications.
109
- * [Pill](?path=/docs/core-pill--design) — For inline semantic status indicators without body copy.
110
- * [Modal](?path=/docs/core-modal--design) — When the error or warning requires an immediate blocking decision.
108
+ * [Toast](/llms/core-toast--design.txt) — For non-blocking, auto-dismissing notifications.
109
+ * [Pill](/llms/core-pill--design.txt) — For inline semantic status indicators without body copy.
110
+ * [Modal](/llms/core-modal--design.txt) — When the error or warning requires an immediate blocking decision.
@@ -39,7 +39,7 @@ space is limited.
39
39
 
40
40
  ## Layout and spacing
41
41
 
42
- * Input renders identically to [TextField](?path=/docs/core-textfield--design).
42
+ * Input renders identically to [TextField](/llms/core-textfield--design.txt).
43
43
  * Suggestions are presented using a ListBox.
44
44
 
45
45
  ## Behavior
@@ -70,8 +70,8 @@ Do:
70
70
 
71
71
  Don't:
72
72
 
73
- * Don't use AutocompleteField for small mutually exclusive sets (about seven or fewer visible options) — use [RadioGroup](?path=/docs/core-radiogroup--design) or Select.
74
- * Don't use AutocompleteField for plain freeform text with no list validation — use [TextField](?path=/docs/core-textfield--design).
73
+ * Don't use AutocompleteField for small mutually exclusive sets (about seven or fewer visible options) — use [RadioGroup](/llms/core-radiogroup--design.txt) or Select.
74
+ * Don't use AutocompleteField for plain freeform text with no list validation — use [TextField](/llms/core-textfield--design.txt).
75
75
  * Don't require picking from the list while still allowing arbitrary text without explaining that behavior.
76
76
 
77
77
  ## Accessibility
@@ -87,5 +87,5 @@ Don't:
87
87
 
88
88
  ## Related components
89
89
 
90
- * [TextField](?path=/docs/core-textfield--design) — For freeform text without suggestions.
91
- * [RadioGroup](?path=/docs/core-radiogroup--design) — For small mutually exclusive option sets.
90
+ * [TextField](/llms/core-textfield--design.txt) — For freeform text without suggestions.
91
+ * [RadioGroup](/llms/core-radiogroup--design.txt) — For small mutually exclusive option sets.
@@ -16,6 +16,10 @@ Badges support `md` and `sm` sizes. Use `sm` when composing with smaller control
16
16
 
17
17
  Use a dot to show notifications without a count.
18
18
 
19
+ ### Decorative
20
+
21
+ Use `decorative` when the badge is purely visual and its meaning is already conveyed nearby, such as adjacent text. Decorative badges are removed from the accessibility tree and must not be combined with `aria-label` or `aria-labelledby`.
22
+
19
23
  ### Appearance
20
24
 
21
25
  Badges can be colored based on their status. The `attention` appearance is the general purpose color used for numbered badges and does not necessarily communicate severity.
@@ -49,6 +53,7 @@ Do:
49
53
  * Do cap displayed values at 99 with a "99+" label.
50
54
  * Do use the dot variant for simple presence indicators.
51
55
  * Do associate interactive anchors with badge count context using `aria-describedby`.
56
+ * Do use `decorative` when the badge is redundant with nearby text or another accessible name.
52
57
 
53
58
  Don't:
54
59
 
@@ -57,12 +62,15 @@ Don't:
57
62
  * Don't place Badge on non-interactive decorative elements.
58
63
  * Don't use Badge colors as the only method to communicate status.
59
64
  * Don't include badge count context in the anchor's `aria-label`.
65
+ * Don't combine `decorative` with `aria-label` or `aria-labelledby`.
60
66
 
61
67
  ## Accessibility
62
68
 
63
- * Badge requires an accessible label via `aria-label` or `aria-labelledby` that describes the count or status (e.g., "76 unread").
69
+ * Named badges require `aria-label` or `aria-labelledby` that describes the count or status (e.g., "76 unread").
70
+ * Counter badges use `role="status"` so naming attributes are valid and the visible count is not presentational. Dot badges use `role="img"` because they have no text.
71
+ * Use `decorative` when the badge is purely presentational and the meaning is already available to assistive technology. Decorative badges omit the role and accessible name and set `aria-hidden`. Do not combine `decorative` with `aria-label` or `aria-labelledby`.
64
72
  * When composing with interactive controls such as `Button` or `IconButton`, set `aria-describedby` on the anchor to the badge's `id`. Keep the anchor's accessible name limited to the control action or label (e.g., "Messages"), not the badge content.
65
- * Use `aria-live="polite"` on the parent for dynamic badge updates.
73
+ * Counter badges are a polite live region via `role="status"`. Use `aria-live="polite"` on a parent only when you need to announce grouped updates beyond the badge itself.
66
74
 
67
75
  ## Content
68
76
 
@@ -71,5 +79,5 @@ Don't:
71
79
 
72
80
  ## Related components
73
81
 
74
- * [Pill](?path=/docs/core-pill--design) — For semantic status labels.
75
- * [Tag](?path=/docs/core-tag--design) — For removable text chips.
82
+ * [Pill](/llms/core-pill--design.txt) — For semantic status labels.
83
+ * [Tag](/llms/core-tag--design.txt) — For removable text chips.
@@ -13,4 +13,10 @@ Badges display an indicator/counter on an associated component.
13
13
  | `className` | `never` | No | `--` | Use `FORCE__className` instead. |
14
14
  | `style` | `never` | No | `--` | Inline styles are not supported; use component props or `FORCE__className`. |
15
15
  | `FORCE__className` | `string` | No | `--` | 🚨 This prop is meant to be an escape hatch. 🚨<br><br>If the desired style cannot be achieved using component props, use this as a last resort. The inner workings of Capra components are implementation details and this escape hatch gives one access to those implementation details. We cannot make any guarantees that styles will applied correctly across version updates. Please use it responsibly.<br><br>Add a CSS class to the component. |
16
- | `aria-live` | `React.AriaAttributes['aria-live']` | No | `--` | `--` |
16
+ | `aria-live` | `React.AriaAttributes['aria-live']` | No | `--` | `--` |
17
+
18
+ ## Accessibility
19
+
20
+ Named badges require `aria-label` or `aria-labelledby`. Counter badges expose `role="status"` so the visible count is not treated as presentational. Dot badges expose `role="img"` because they have no text.
21
+
22
+ Use `decorative` when the badge is purely presentational and the meaning is already conveyed nearby. Decorative badges omit the role and accessible name and set `aria-hidden`. Do not combine `decorative` with `aria-label` or `aria-labelledby`.
@@ -62,4 +62,4 @@ Don't:
62
62
 
63
63
  ## Related components
64
64
 
65
- * [TabNav](?path=/docs/core-tabnav--design) — For in-page section navigation.
65
+ * [TabNav](/llms/core-tabnav--design.txt) — For in-page section navigation.
@@ -76,9 +76,9 @@ Do:
76
76
 
77
77
  Don't:
78
78
 
79
- * Don't use a Button for navigation to another page — use [Link](?path=/docs/core-link--design) or [Anchor](?path=/docs/core-anchor--design).
80
- * Don't use a Button for text-only inline navigation — use [Link](?path=/docs/core-link--design).
81
- * Don't use a Button for icon-only actions — use [IconButton](?path=/docs/core-iconbutton--design).
79
+ * Don't use a Button for navigation to another page — use [Link](/llms/core-link--design.txt) or [Anchor](/llms/core-anchor--design.txt).
80
+ * Don't use a Button for text-only inline navigation — use [Link](/llms/core-link--design.txt).
81
+ * Don't use a Button for icon-only actions — use [IconButton](/llms/core-iconbutton--design.txt).
82
82
  * Don't use two primary buttons side by side.
83
83
  * Don't truncate button labels.
84
84
  * Don't disable a button without explaining why (use `aria-disabled` with a tooltip).
@@ -86,12 +86,12 @@ Don't:
86
86
  ## Accessibility
87
87
 
88
88
  * Always provide a visible text label; if the design requires a short label, add `aria-label` for context.
89
- * For icon-only use cases, use [IconButton](?path=/docs/core-iconbutton--design) instead (which enforces an accessible label).
89
+ * For icon-only use cases, use [IconButton](/llms/core-iconbutton--design.txt) instead (which enforces an accessible label).
90
90
  * Tab / Shift+Tab to focus. Enter or Space to activate.
91
91
  * The pending state must be communicated to assistive technology (e.g., `aria-label="Saving..."` when `pending` is true).
92
92
 
93
93
  ## Related components
94
94
 
95
- * [IconButton](?path=/docs/core-iconbutton--design) — For icon-only actions.
96
- * [Link](?path=/docs/core-link--design) — For inline text navigation.
97
- * [Modal](?path=/docs/core-modal--design) — Buttons typically appear in modal footers for confirm/cancel patterns.
95
+ * [IconButton](/llms/core-iconbutton--design.txt) — For icon-only actions.
96
+ * [Link](/llms/core-link--design.txt) — For inline text navigation.
97
+ * [Modal](/llms/core-modal--design.txt) — Buttons typically appear in modal footers for confirm/cancel patterns.
@@ -2,6 +2,6 @@
2
2
 
3
3
  Primary interactive element displaying text and supporting leading and trailing icons.
4
4
 
5
- `ButtonLink` follows the design guidelines for [`Button`](?path=/docs/core-button--design) and uses [`Link`](?path=/docs/core-link--design) interally. Use it when you need a link styled as a button.
5
+ `ButtonLink` follows the design guidelines for [`Button`](/llms/core-button--design.txt) and uses [`Link`](/llms/core-link--design.txt) interally. Use it when you need a link styled as a button.
6
6
 
7
7
  See those pages for more details on the properties and usage of `ButtonLink`.
@@ -69,7 +69,7 @@ Treat the card as a container: put links and buttons inside slots rather than ma
69
69
  ## Behavior
70
70
 
71
71
  * Cards are static containers by default.
72
- * If the entire card is interactive (e.g., a selectable card), use `role="button"` with keyboard support. Prefer [RadioTile](?path=/docs/core-radiotile--design) for selection scenarios.
72
+ * If the entire card is interactive (e.g., a selectable card), use `role="button"` with keyboard support. Prefer [RadioTile](/llms/core-radiotile--design.txt) for selection scenarios.
73
73
 
74
74
  ## Content
75
75
 
@@ -86,7 +86,7 @@ Do:
86
86
 
87
87
  Don't:
88
88
 
89
- * Don't make the entire card surface the only navigation affordance — put a [Link](?path=/docs/core-link--design) or [Button](?path=/docs/core-button--design) inside the content.
89
+ * Don't make the entire card surface the only navigation affordance — put a [Link](/llms/core-link--design.txt) or [Button](/llms/core-button--design.txt) inside the content.
90
90
  * Don't use Card for tabular data — use a table.
91
91
 
92
92
  ## Accessibility
@@ -96,5 +96,5 @@ Don't:
96
96
 
97
97
  ## Related components
98
98
 
99
- * [RadioTile](?path=/docs/core-radiotile--design) — For selectable card options.
100
- * [Modal](?path=/docs/core-modal--design) — For focused overlay containers.
99
+ * [RadioTile](/llms/core-radiotile--design.txt) — For selectable card options.
100
+ * [Modal](/llms/core-modal--design.txt) — For focused overlay containers.
@@ -62,5 +62,5 @@ Don't:
62
62
 
63
63
  ## Related components
64
64
 
65
- * [Switch](?path=/docs/core-switch--design) — For immediate binary settings.
66
- * [RadioGroup](?path=/docs/core-radiogroup--design) — For mutually exclusive selections.
65
+ * [Switch](/llms/core-switch--design.txt) — For immediate binary settings.
66
+ * [RadioGroup](/llms/core-radiogroup--design.txt) — For mutually exclusive selections.
@@ -8,6 +8,7 @@ Checkbox component for capturing boolean user input. Supports controlled and unc
8
8
  | `checked` | `boolean` | No | `--` | Controlled checked state. Use with onChange for controlled component. |
9
9
  | `defaultChecked` | `boolean` | No | `--` | Default checked state for uncontrolled component. |
10
10
  | `children` | `string \| React.ReactNode` | No | `--` | The label for the checkbox. |
11
+ | `slot` | `string \| null` | No | `--` | Slot name for React Aria context wiring (e.g. "selection" in Table). |
11
12
  | `className` | `never` | No | `--` | Use `FORCE__className` instead. |
12
13
  | `style` | `never` | No | `--` | Inline styles are not supported; use component props or `FORCE__className`. |
13
14
  | `FORCE__className` | `string` | No | `--` | 🚨 This prop is meant to be an escape hatch. 🚨<br><br>If the desired style cannot be achieved using component props, use this as a last resort. The inner workings of Capra components are implementation details and this escape hatch gives one access to those implementation details. We cannot make any guarantees that styles will applied correctly across version updates. Please use it responsibly.<br><br>Add a CSS class to the component. |
@@ -24,5 +24,5 @@ Use Collapse to structure readable content, separate regions, or group related i
24
24
 
25
25
  ## Related components
26
26
 
27
- * [Card](?path=/docs/core-card--design)
28
- * [Divider](?path=/docs/core-divider--design)
27
+ * [Card](/llms/core-card--design.txt)
28
+ * [Divider](/llms/core-divider--design.txt)
@@ -32,4 +32,4 @@ Don't:
32
32
 
33
33
  ## Related components
34
34
 
35
- * [Collapse](?path=/docs/core-collapse--design)
35
+ * [Collapse](/llms/core-collapse--design.txt)
@@ -54,5 +54,5 @@ Links inherit the standard focus indicator standard to all focusable elements.
54
54
 
55
55
  ## Related components
56
56
 
57
- * [Button](?path=/docs/core-button--design)
58
- * [Anchor](?path=/docs/core-anchor--design)
57
+ * [Button](/llms/core-button--design.txt)
58
+ * [Anchor](/llms/core-anchor--design.txt)
@@ -5,7 +5,7 @@
5
5
  1. **Label**: describes the field and carries the required marker when needed.
6
6
  2. **Trigger**: matches Capra text-input sizing and state treatments.
7
7
  3. **Popover**: contains the option list and optional search field.
8
- 4. **ListBox items**: support single-select rows or multi-select rows with checkmarks.
8
+ 4. **ListBox items**: selected rows are marked with a checkmark in both single- and multiple-select modes.
9
9
  5. **Sections**: optional grouped options with a non-selectable header label.
10
10
 
11
11
  ## Properties
@@ -0,0 +1,17 @@
1
+ # Core/Table - Design
2
+
3
+ A component for rendering tabular data
4
+
5
+ ## Properties
6
+
7
+ ### Density
8
+
9
+ Tables have 3 different row sizes: `default`, `compact`, and `tight`.
10
+
11
+ ### Appearance
12
+
13
+ Striped rows (aka: Zebra striping) provide contrast between rows by alternating the background color. This can be disabled with `appearance="flat"`.
14
+
15
+ ### Loading
16
+
17
+ When tables are in a loading state the content is replaced with 5 rows of skeleton content that represents the general shape of the data.
@@ -0,0 +1,198 @@
1
+ # Core/Table - Usage
2
+
3
+ A component for rendering tabular data
4
+
5
+ ## Basic usage
6
+
7
+ ```tsx
8
+ import { Table } from '@capra/core';
9
+
10
+ type User = {
11
+ id: string;
12
+ name: string;
13
+ };
14
+
15
+ const userColumns = defineColumns<User>([
16
+ { id: 'name', label: 'Name', allowsSorting: true },
17
+ ])
18
+
19
+ const users = [
20
+ {
21
+ id: 1,
22
+ name: 'Alice Johnson',
23
+ },
24
+ {
25
+ id: 2,
26
+ name: 'Bob Williams',
27
+ },
28
+ ];
29
+
30
+ <Table
31
+ columns={userColumns}
32
+ visibleColumns={['name']}
33
+ items={users}
34
+ />
35
+ ```
36
+
37
+ ## Props
38
+
39
+ | Prop | Type | Required | Default | Description |
40
+ | --- | --- | --- | --- | --- |
41
+ | `items` | `ItemRecord[]` | Yes | `--` | The data to render as rows in the Table |
42
+ | `density` | `'default' \| 'compact' \| 'tight'` | No | 'default' | The density of the table, affects row height |
43
+ | `appearance` | `'zebra' \| 'flat'` | No | 'zebra' | Whether to apply zebra-striping to rows |
44
+ | `sortDescriptor` | `SortDescriptor` | No | `--` | Key and direction for sorting the Table |
45
+ | `onSortChange` | `(sortDescriptor: SortDescriptor) => void` | No | `--` | Callback function fired when a the sorting configuration is changed |
46
+ | `columns` | `ColumnConfig<ColumnKey, ItemRecord>[]` | Yes | `--` | The columns to render in the Table |
47
+ | `visibleColumns` | `ColumnKey[]` | Yes | `--` | Which columns should be visible |
48
+ | `isLoading` | `boolean` | No | false | Whether the table is in a loading state |
49
+ | `enableDragAndDrop` | `boolean` | No | false | Whether drag-and-dropping rows is enabled |
50
+ | `renderActionColumn` | `(item: ItemRecord) => React.ReactNode` | No | `--` | todo: is this prop needed? |
51
+ | `selectionMode` | `SelectionMode` | No | `--` | Mode of row selection<br>@default 'none' |
52
+ | `selectedKeys` | `Selection` | No | `--` | The currently selected keys (controlled) |
53
+ | `onSelectionChange` | `(keys: Selection) => void` | No | `--` | Callback fired when selection changes |
54
+ | `className` | `never` | No | `--` | Use `FORCE__className` instead. |
55
+ | `style` | `never` | No | `--` | Inline styles are not supported; use component props or `FORCE__className`. |
56
+ | `FORCE__className` | `string` | No | `--` | 🚨 This prop is meant to be an escape hatch. 🚨<br><br>If the desired style cannot be achieved using component props, use this as a last resort. The inner workings of Capra components are implementation details and this escape hatch gives one access to those implementation details. We cannot make any guarantees that styles will applied correctly across version updates. Please use it responsibly.<br><br>Add a CSS class to the component. |
57
+
58
+ ## Examples
59
+
60
+ ### Sorting
61
+
62
+ ```tsx
63
+ import { Table } from '@capra/core';
64
+
65
+ const userColumns = defineColumns<User>([
66
+ { id: 'name', label: 'Name', allowsSorting: true },
67
+ // ...
68
+ ])
69
+
70
+ function SortableTable() {
71
+ const [sortDescriptor, setSortDescriptor] = useState<SortDescriptor | undefined>({
72
+ column: 'name',
73
+ direction: 'ascending',
74
+ });
75
+ const sortedItems = useMemo(() => sortItems(items, sortDescriptor), [sortDescriptor, items]);
76
+ return (
77
+ <Table
78
+ {...props}
79
+ items={sortedItems}
80
+ sortDescriptor={sortDescriptor}
81
+ onSortChange={setSortDescriptor}
82
+ visibleColumns={reverseColumns ? visibleColumns.toReversed() : visibleColumns}
83
+ />
84
+ );
85
+ }
86
+ ```
87
+
88
+ ### Selection
89
+
90
+ Use the `selectionMode` prop to enable `single` or `multiple` selection of rows.
91
+
92
+ ```tsx
93
+ <Table
94
+ columns={userColumns}
95
+ visibleColumns={['name', 'email', 'balance', 'enabled', 'description', 'trend']}
96
+ items={sortedItems}
97
+ selectionMode="multiple"
98
+ />
99
+ ```
100
+
101
+ ### Filter toolbar
102
+
103
+ Use the `FilterToolbar` component to support filtering and/or bulk editing.
104
+
105
+ Note: `FilterToolbar` is only supported instances of `Table` with controlled selection.
106
+
107
+ ```tsx
108
+ import { FilterToolbar, Table } from '@capra/core';
109
+
110
+ function TableWithToolbar() {
111
+ const [filterString, setFilterString] = useState('');
112
+ const [selectedKeys, setSelectedKeys] = useState<Selection>(new Set());
113
+ const sortedItems = useMemo(() => sortItems(users, sortDescriptor), [sortDescriptor] );
114
+ const filteredItems = filterUsers(sortedItems, filterString)
115
+ const selectableCount = sortedItems.filter((item) => !item.isDisabled).length;
116
+ const selectedCount = selectedKeys === 'all' ? selectableCount : selectedKeys.size;
117
+
118
+ return (
119
+ <div style={{ display: 'flex', gap: '8px', flexDirection: 'column' }}>
120
+ <TableToolbar
121
+ filterInputProps={{
122
+ onChange: (value: string) => setFilterString(value)
123
+ }}
124
+ renderActions={() => (
125
+ <Button size="sm" variant="primary">Add user</Button>
126
+ )}
127
+ renderBulkEditText={() => (
128
+ <Text>
129
+ {selectedCount} of {sortedItems.length} users selected
130
+ </Text>
131
+ )}
132
+ renderBulkEditActions={() => (
133
+ <div style={{ display: 'flex', gap: '8px' }}>
134
+ <Button appearance="neutral" size="sm">
135
+ Delete
136
+ </Button>
137
+ <Button appearance="neutral" variant="tertiary" size="sm">
138
+ Cancel
139
+ </Button>
140
+ </div>
141
+ )}
142
+ selectedKeys={selectedKeys}
143
+ />
144
+ <Table
145
+ columns={userColumns}
146
+ visibleColumns={['name', 'email', 'balance']}
147
+ items={filteredItems}
148
+ selectionMode="multiple"
149
+ selectedKeys={selectedKeys}
150
+ onSelectionChange={setSelectedKeys}
151
+ />
152
+ </div>
153
+ );
154
+ }
155
+ ```
156
+
157
+ ### Custom cell rendering
158
+
159
+ ```tsx
160
+ import { Table } from '@capra/core';
161
+
162
+ type User = {
163
+ id: string;
164
+ name: string;
165
+ email: string;
166
+ };
167
+
168
+ const userColumns = defineColumns<User>([
169
+ { id: 'name', label: 'Name', allowsSorting: true },
170
+ {
171
+ id: 'email',
172
+ label: 'Email',
173
+ allowsSorting: true,
174
+ render(value) {
175
+ return <Link href={`mailto:${value as string}`}>{value as string}</Link>;
176
+ },
177
+ },
178
+ ])
179
+
180
+ const users = [
181
+ {
182
+ id: 1,
183
+ name: 'Alice Johnson',
184
+ email: 'alice@example.com',
185
+ },
186
+ {
187
+ id: 2,
188
+ name: 'Bob Williams',
189
+ email: 'bob@example.com',
190
+ },
191
+ ];
192
+
193
+ <Table
194
+ columns={userColumns}
195
+ visibleColumns={['name', 'email']}
196
+ items={users}
197
+ />
198
+ ```
@@ -4,7 +4,7 @@ TextInput component for single-line text entry. Backwards compatible with Ant De
4
4
 
5
5
  :::warning[Internal only]
6
6
 
7
- `TextInput` is not exported from the public `@capra/core` API. For single-line text entry in product code, use [`TextField`](?path=/docs/core-textfield--design).
7
+ `TextInput` is not exported from the public `@capra/core` API. For single-line text entry in product code, use [`TextField`](/llms/core-textfield--design.txt).
8
8
 
9
9
  :::
10
10
 
@@ -0,0 +1,89 @@
1
+ # Core/ToggleButtonGroup - Design
2
+
3
+ A group of toggle buttons for choosing one or more options from a set.
4
+
5
+ ## When to use
6
+
7
+ Toggle button groups behave similarly to radio buttons, providing selection between multiple mutually exclusive options. They are also used to switch views, such as between list and grid view.
8
+
9
+ Toggle button groups affect section-level views and are not a replacement for tabs.
10
+
11
+ ## Properties
12
+
13
+ ### Size
14
+
15
+ The default size for toggle button groups is medium, which should be used in most cases. A small size is also available when the group appears inline with another component.
16
+
17
+ ### Label
18
+
19
+ Labels should be short and succinct. For longer labels, consider a [Radio](/llms/core-radio--design.txt) or [SelectField](/llms/core-selectfield--design.txt).
20
+
21
+ ### Icon
22
+
23
+ Icons may appear with a label as part of each button. All buttons within a group should have an icon, or none should.
24
+
25
+ ## Behavior
26
+
27
+ ### Control count
28
+
29
+ Toggle button groups can have between 2 and 4 buttons.
30
+
31
+ ### View switching
32
+
33
+ Toggle button groups can be used to switch the view of data at the section level, such as between a list view and grid view.
34
+
35
+ Views should be presented in a consistent order:
36
+
37
+ * List
38
+ * Grid
39
+ * Graph / Chart
40
+ * Metrics
41
+
42
+ ### Filtering
43
+
44
+ Toggle button groups can be used to filter data at the section level, such as a data range within a graph.
45
+
46
+ ## Best practices
47
+
48
+ ### Count
49
+
50
+ **Do**
51
+
52
+ Do consider a toggle button group if you have 2 to 4 succinct choices.
53
+
54
+ **Don't**
55
+
56
+ Don't use more than 4 buttons in a single group. Consider a [SelectField](/llms/core-selectfield--design.txt) if more than 4 choices are needed.
57
+
58
+ ### Wording
59
+
60
+ **Do**
61
+
62
+ Do keep labels short.
63
+
64
+ **Don't**
65
+
66
+ Don't use long, multi-word labels.
67
+
68
+ ### Consistent layout
69
+
70
+ **Do**
71
+
72
+ Do keep icons consistent and unchanged when a button becomes active.
73
+
74
+ **Don't**
75
+
76
+ Don't replace icons when in an active state.
77
+
78
+ ## Accessibility
79
+
80
+ ### Keyboard interaction
81
+
82
+ | Key | Function |
83
+ | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
84
+ | `Tab`<br />`Shift + Tab` | Move focus into and out of the button group. When focus moves into a button group:<br />- if a button is checked, focus is set on the checked button;<br />- if no buttons are checked, focus is set on the first button in the group. |
85
+ | `Enter`<br />`Space` | Checks the focused button if not already selected. |
86
+ | `Right Arrow`<br />`Down Arrow` | Move focus to the next button in the group. |
87
+ | `Left Arrow`<br />`Up Arrow` | Move focus to the previous button in the group. |
88
+
89
+ Icon-only items require an `aria-label` because there is no visible text label. The group itself requires an `aria-label` (or `aria-labelledby`) so assistive technologies can identify the control set.
@@ -0,0 +1,113 @@
1
+ # Core/ToggleButtonGroup - Usage
2
+
3
+ A group of toggle buttons for choosing one or more options from a set.
4
+
5
+ ## Basic usage
6
+
7
+ ```tsx
8
+ import { useState } from 'react';
9
+ import { ToggleButtonGroup, type Key } from '@capra/core';
10
+
11
+ function Example() {
12
+ const [selectedKeys, setSelectedKeys] = useState<Set<Key>>(new Set(['day']));
13
+ return (
14
+ <ToggleButtonGroup
15
+ aria-label="Time period"
16
+ selectedKeys={selectedKeys}
17
+ onSelectionChange={setSelectedKeys}
18
+ items={[
19
+ { key: 'day', text: 'Day' },
20
+ { key: 'week', text: 'Week' },
21
+ { key: 'month', text: 'Month' },
22
+ ]}
23
+ />
24
+ )
25
+ }
26
+ ```
27
+
28
+ ## Props
29
+
30
+ | Prop | Type | Required | Default | Description |
31
+ | --- | --- | --- | --- | --- |
32
+ | `items` | `ToggleButtonGroupItem[]` | Yes | `--` | Items to render as toggle buttons. |
33
+ | `size` | `Extract<ButtonSize, 'sm' \| 'md'>` | No | 'md' | Size forwarded to every rendered button. Defaults to `md`. |
34
+ | `disabled` | `boolean` | No | false | Whether the entire group is disabled. |
35
+ | `selectionMode` | `'single' \| 'multiple'` | No | 'single' | Whether single or multiple selection is enabled. Defaults to `single`. |
36
+ | `selectedKeys` | `Iterable<Key>` | No | `--` | Controlled selected keys. |
37
+ | `defaultSelectedKeys` | `Iterable<Key>` | No | `--` | Uncontrolled initial selected keys. |
38
+ | `onSelectionChange` | `(keys: Set<Key>) => void` | No | `--` | Handler called when the selection changes. |
39
+ | `disallowEmptySelection` | `boolean` | No | `--` | Whether the group must always have at least one selected item. |
40
+ | `className` | `never` | No | `--` | Use `FORCE__className` instead. |
41
+ | `style` | `never` | No | `--` | Inline styles are not supported; use component props or `FORCE__className`. |
42
+ | `FORCE__className` | `string` | No | `--` | 🚨 This prop is meant to be an escape hatch. 🚨<br><br>If the desired style cannot be achieved using component props, use this as a last resort. The inner workings of Capra components are implementation details and this escape hatch gives one access to those implementation details. We cannot make any guarantees that styles will applied correctly across version updates. Please use it responsibly.<br><br>Add a CSS class to the component. |
43
+
44
+ ## Examples
45
+
46
+ ### Icons
47
+
48
+ Buttons can display icons in addition to text, or replace text entirely.
49
+
50
+ ```tsx
51
+ <ToggleButtonGroup
52
+ aria-label="Time period"
53
+ items={[
54
+ { key: 'align-left', text: 'Align left', icon: AlignLeft },
55
+ { key: 'align-center', text: 'Align center', icon: AlignCenter },
56
+ { key: 'align-right', text: 'Align right', icon: AlignRight },
57
+ ]}
58
+ ```
59
+
60
+ When using icons only, `aria-label` is required.
61
+
62
+ ```tsx
63
+ <ToggleButtonGroup
64
+ aria-label="Time period"
65
+ items={[
66
+ { key: 'align-left', icon: AlignLeft, 'aria-label': 'Align left' },
67
+ { key: 'align-center', icon: AlignCenter, 'aria-label': 'Align center' },
68
+ { key: 'align-right', icon: AlignRight, 'aria-label': 'Align right' },
69
+ ]}
70
+ ```
71
+
72
+ ### Multi-select
73
+
74
+ ```tsx
75
+ <ToggleButtonGroup
76
+ aria-label="Time period"
77
+ selectionMode="multiple"
78
+ items={[
79
+ { key: 'day', text: 'Day' },
80
+ { key: 'week', text: 'Week' },
81
+ { key: 'month', text: 'Month' },
82
+ ]}
83
+ />
84
+ ```
85
+
86
+ ### Disabled state
87
+
88
+ The entire `ToggleButtonGroup` can be disabled by setting the `disabled` prop.
89
+
90
+ ```tsx
91
+ <ToggleButtonGroup
92
+ aria-label="Time period"
93
+ disabled
94
+ items={[
95
+ { key: 'day', text: 'Day' },
96
+ { key: 'week', text: 'Week' },
97
+ { key: 'month', text: 'Month' },
98
+ ]}
99
+ />
100
+ ```
101
+
102
+ Alternatively, individual buttons can be disabled by setting the `disabled` prop on the individual item.
103
+
104
+ ```tsx
105
+ <ToggleButtonGroup
106
+ aria-label="Time period"
107
+ items={[
108
+ { key: 'day', text: 'Day', disabled: true },
109
+ { key: 'week', text: 'Week' },
110
+ { key: 'month', text: 'Month' },
111
+ ]}
112
+ />
113
+ ```