@codecademy/gamut 68.6.2-alpha.f8b396.0 → 68.6.2

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 (36) hide show
  1. package/dist/DatePicker/DatePickerCalendar/Calendar/CalendarBody.js +3 -3
  2. package/dist/DatePicker/DatePickerInput/Segment/index.js +1 -1
  3. package/dist/DatePicker/DatePickerInput/index.js +1 -1
  4. package/package.json +8 -11
  5. package/agent-tools/.claude-plugin/marketplace.json +0 -16
  6. package/agent-tools/.claude-plugin/plugin.json +0 -7
  7. package/agent-tools/.cursor-plugin/plugin.json +0 -7
  8. package/agent-tools/DESIGN.Codecademy.md +0 -643
  9. package/agent-tools/DESIGN.LXStudio.md +0 -437
  10. package/agent-tools/DESIGN.Percipio.md +0 -433
  11. package/agent-tools/DESIGN.md +0 -1
  12. package/agent-tools/agents/.gitkeep +0 -0
  13. package/agent-tools/rules/accessibility.mdc +0 -78
  14. package/agent-tools/skills/gamut-accessibility/SKILL.md +0 -214
  15. package/agent-tools/skills/gamut-buttons/SKILL.md +0 -96
  16. package/agent-tools/skills/gamut-color-mode/SKILL.md +0 -257
  17. package/agent-tools/skills/gamut-forms/SKILL.md +0 -84
  18. package/agent-tools/skills/gamut-layout/SKILL.md +0 -109
  19. package/agent-tools/skills/gamut-list/SKILL.md +0 -273
  20. package/agent-tools/skills/gamut-review/SKILL.md +0 -254
  21. package/agent-tools/skills/gamut-style-utilities/SKILL.md +0 -107
  22. package/agent-tools/skills/gamut-system-props/SKILL.md +0 -203
  23. package/agent-tools/skills/gamut-testing/SKILL.md +0 -221
  24. package/agent-tools/skills/gamut-theming/SKILL.md +0 -115
  25. package/agent-tools/skills/gamut-typography/SKILL.md +0 -98
  26. package/bin/commands/plugin/install.mjs +0 -212
  27. package/bin/commands/plugin/list.mjs +0 -73
  28. package/bin/commands/plugin/remove.mjs +0 -108
  29. package/bin/commands/plugin/update.mjs +0 -59
  30. package/bin/gamut.mjs +0 -96
  31. package/bin/lib/claude.mjs +0 -52
  32. package/bin/lib/cursor.mjs +0 -40
  33. package/bin/lib/design.mjs +0 -71
  34. package/bin/lib/io.mjs +0 -14
  35. package/bin/lib/resolve-plugin-dir.mjs +0 -38
  36. package/bin/lib/run-command.mjs +0 -22
@@ -1,84 +0,0 @@
1
- ---
2
- name: gamut-forms
3
- description: Implementing or auditing Gamut forms — FormGroup, ConnectedForm, ConnectedFormGroup, GridForm, react-hook-form wiring, labels, and accessible error/description regions. Pair with `gamut-accessibility` for non-form widgets and `accessibility.mdc` for universal HTML/ARIA rules.
4
- ---
5
-
6
- # Gamut forms
7
-
8
- Canonical wiring for `FormGroup`, `ConnectedForm`, `ConnectedFormGroup`, `GridForm`, and field renderers. Source: `packages/gamut/src/Form/`, `ConnectedForm/`, `GridForm/`.
9
-
10
- Universal label and primitive guidance: [`accessibility.mdc`](../../rules/accessibility.mdc) · overlay and composite patterns: [`gamut-accessibility`](../gamut-accessibility/SKILL.md).
11
-
12
- ---
13
-
14
- ## Prefer connected layouts
15
-
16
- For typical product forms, prefer `GridForm` (declarative `fields`, `LayoutGrid`, submit/cancel) or `ConnectedForm` with `ConnectedFormGroup` / `useConnectedForm`. Use raw `FormGroup` + atoms only when the layout is simple and you fully own `id`, `htmlFor`, invalid state, and any `aria-describedby` (see below).
17
-
18
- ---
19
-
20
- ## Labels and controls
21
-
22
- `htmlFor` / `id` pairing — universal rule in [`accessibility.mdc`](../../rules/accessibility.mdc) (Form label association). Form-specific notes:
23
-
24
- - `FormGroupLabel` → control `id` (or stable `name` when that is your field’s id convention).
25
- - Checkbox, Radio, Select: same pairing; checkbox/radio use the visually hidden input pattern from `@codecademy/gamut-styles` where applicable.
26
-
27
- ---
28
-
29
- ## `FormGroup` (baseline)
30
-
31
- [`FormGroup.tsx`](https://github.com/Codecademy/gamut/blob/main/packages/gamut/src/Form/elements/FormGroup.tsx)
32
-
33
- - `description` → `FormGroupDescription` with `aria-live="assertive"`.
34
- - `error` (string) → `FormError` with `aria-live="polite"` and `role="alert"`.
35
-
36
- Raw `FormGroup` does not set `aria-describedby` or `aria-invalid` on `children`. If you compose fields outside `ConnectedFormGroup` / `GridForm`, wire those yourself or accept that only the live regions above communicate errors/descriptions.
37
-
38
- ---
39
-
40
- ## `ConnectedFormGroup`
41
-
42
- [`ConnectedFormGroup.tsx`](https://github.com/Codecademy/gamut/blob/main/packages/gamut/src/ConnectedForm/ConnectedFormGroup.tsx)
43
-
44
- - Passes `aria-describedby` (error region id when shown) and `aria-invalid` on the rendered field component.
45
- - `FormError`: `aria-live="assertive"` and `role="alert"` only when `isFirstError`; otherwise `aria-live="off"` and `role="status"` so subsequent errors do not interrupt repeatedly.
46
-
47
- ---
48
-
49
- ## `GridForm`
50
-
51
- [`GridFormInputGroup/index.tsx`](https://github.com/Codecademy/gamut/blob/main/packages/gamut/src/GridForm/GridFormInputGroup/index.tsx) · [`GridFormTextInput`](https://github.com/Codecademy/gamut/blob/main/packages/gamut/src/GridForm/GridFormInputGroup/GridFormTextInput/index.tsx)
52
-
53
- - Composes `ConnectedForm`, `LayoutGrid`, `GridFormButtons`, and field metadata. `FormError` uses the same first-error assertive pattern as `ConnectedFormGroup` (`aria-live` assertive vs off, `role` alert vs status).
54
- - Built-in text inputs set `aria-invalid` and register with react-hook-form via `register`. Custom / `custom` / `custom-group` renderers must still expose correct `id`, `label`, and error surfacing consistent with this pattern.
55
-
56
- ---
57
-
58
- ## Live regions — do not double up
59
-
60
- `FormGroup`, `ConnectedFormGroup`, and `GridForm` already render `FormError` (and base `FormGroup` renders `FormGroupDescription`) with live-region attributes. Do not add a second `aria-live` wrapper for the same message stream.
61
-
62
- ---
63
-
64
- ## Storybook
65
-
66
- - [Organisms / GridForm / About](https://gamut.codecademy.com/?path=/docs-organisms-gridform-about--docs) · [Usage](https://gamut.codecademy.com/?path=/docs-organisms-gridform-usage--docs) · [Validation](https://gamut.codecademy.com/?path=/docs-organisms-gridform-validation--docs) · [Fields](https://gamut.codecademy.com/?path=/docs-organisms-gridform-fields--docs)
67
- - [Organisms / ConnectedForm / ConnectedForm](https://gamut.codecademy.com/?path=/docs-organisms-connectedform-connectedform--docs) · [ConnectedFormGroup](https://gamut.codecademy.com/?path=/docs-organisms-connectedform-connectedformgroup--docs)
68
-
69
- ---
70
-
71
- ## Example — baseline `FormGroup`
72
-
73
- ```tsx
74
- <FormGroup
75
- htmlFor="email-input"
76
- description="Used for login"
77
- error={errors.email}
78
- >
79
- <FormGroupLabel htmlFor="email-input">Email</FormGroupLabel>
80
- <Input id="email-input" type="email" />
81
- </FormGroup>
82
- ```
83
-
84
- When using `ConnectedFormGroup` or `GridForm`, prefer their docs and defaults over hand-rolling the above for every field.
@@ -1,109 +0,0 @@
1
- ---
2
- name: gamut-layout
3
- description: Use this skill when applying Gamut spacing scale, border radii, viewport or container breakpoints, or page layout grid (LayoutGrid vs GridBox) — complements gamut-system-props for system.space and responsive props.
4
- ---
5
-
6
- # Gamut Layout
7
-
8
- Token values match [`packages/gamut-styles/src/variables`](https://github.com/Codecademy/gamut/tree/main/packages/gamut-styles/src/variables) (`spacing.ts`, `borderRadii.ts`, `responsive.ts`). Breakpoints and max-content widths align with Storybook [Foundations / Layout](https://gamut.codecademy.com/?path=/docs-foundations-layout--docs).
9
-
10
- See also: [`gamut-system-props`](../gamut-system-props/SKILL.md) — `system.space`, responsive `Box` / `FlexBox` / `GridBox` props. [Meta / Best practices](https://gamut.codecademy.com/?path=/docs-meta-best-practices--page) — responsive examples.
11
-
12
- ## System props for spacing
13
-
14
- Gamut layout primitives (`Box`, `FlexBox`, `GridBox`, …) expose margin, padding, and gap props backed by `system.space` from `@codecademy/gamut-styles`. Pass spacing scale numbers (`4`, `8`, `16`, …), not raw pixel strings. For custom `styled` components, compose `system.space`.
15
-
16
- Responsive behavior: All those props accept mobile-first object (`{ _: 8, md: 24 }`) or array syntax per [Responsive properties](https://gamut.codecademy.com/?path=/docs-foundations-system-responsive-properties--page). Container queries use keys `c_base`, `c_xs`, … `c_xl`; the parent must set a container (e.g. `containerType="inline-size"` on `FlexBox`). Prefer a media-query fallback when mixing `c_*` with viewport breakpoints.
17
-
18
- ## Two different “grids”
19
-
20
- - **Design / page grid** (12 columns, margins/gutters) — product layout; implement with [`LayoutGrid`](https://gamut.codecademy.com/?path=/docs-layouts-layoutgrid-layoutgrid--docs) and responsive `columnGap` / `rowGap` where appropriate.
21
- - **CSS Grid system props** — `system.grid` on styled components or `GridBox` for local regions; not the same as full-page `LayoutGrid`. See [System props / Grid](https://gamut.codecademy.com/?path=/docs-foundations-system-props-grid--page). `LayoutGrid` is for flexible full-page sections; use `FlexBox` / `GridBox` / `Box` for smaller areas.
22
-
23
- Designer vs code names: Figma / Layout docs often label artboards XL, LG, MD, SM, XS, Base. In code, viewport breakpoints are `xl`, `lg`, `md`, `sm`, `xs` (min-widths below); `_` is the base (no min-width query).
24
-
25
- ## Spacing scale
26
-
27
- All spacing is multiples of 4px on an 8px grid.
28
-
29
- | Token | Value |
30
- | ----- | ----- |
31
- | `0` | 0 |
32
- | `4` | 4px |
33
- | `8` | 8px |
34
- | `12` | 12px |
35
- | `16` | 16px |
36
- | `24` | 24px |
37
- | `32` | 32px |
38
- | `40` | 40px |
39
- | `48` | 48px |
40
- | `64` | 64px |
41
- | `96` | 96px |
42
-
43
- Use multiples of 8px for block-element spacing. Use 4px only for inline or typographic relationships.
44
-
45
- ## Border radius
46
-
47
- | Token | Value | Use |
48
- | ------ | ----- | ---------------------------------- |
49
- | `none` | 0px | Square / non-interactive elements |
50
- | `sm` | 2px | Subtle rounding, tags |
51
- | `md` | 4px | Buttons, inputs, interactive cards |
52
- | `lg` | 8px | Cards, panels |
53
- | `xl` | 16px | Large cards, modals |
54
- | `full` | 999px | Pills, avatars, circular elements |
55
-
56
- ## Breakpoints
57
-
58
- Mobile-first. Styles apply from the named breakpoint and up.
59
-
60
- | Token | Min-width | Max content width |
61
- | -------- | --------- | ----------------- |
62
- | _(base)_ | 0 | 288px |
63
- | `xs` | 480px | 448px |
64
- | `sm` | 768px | 704px |
65
- | `md` | 1024px | 896px |
66
- | `lg` | 1200px | 1072px |
67
- | `xl` | 1440px | 1248px |
68
-
69
- ## Container query breakpoints
70
-
71
- Container keys (`c_*`) use the same min-width numbers as viewport breakpoints, but they apply to the width of a CSS containment context (usually a parent), not the browser viewport.
72
-
73
- | Key | Min container width |
74
- | -------- | ------------------- |
75
- | `c_base` | 1px |
76
- | `c_xs` | 480px |
77
- | `c_sm` | 768px |
78
- | `c_md` | 1024px |
79
- | `c_lg` | 1200px |
80
- | `c_xl` | 1440px |
81
-
82
- Requirements:
83
-
84
- - A descendant of an element that establishes a container — e.g. parent `<FlexBox containerType="inline-size">`. Without that, `c_*` rules never match.
85
- - Prefer a viewport fallback alongside `c_*` (e.g. `display={{ _: 'block', sm: 'flex', c_md: 'grid' }}`) for browsers or trees without container support.
86
-
87
- When to use which:
88
-
89
- - Viewport keys (`_`, `xs`, … `xl`) — page-level layout, full-bleed sections, global nav.
90
- - Container keys (`c_base`, … `c_xl`) — reusable widgets whose width is driven by layout, not the device alone.
91
-
92
- ## Page layout grid (12 columns)
93
-
94
- 12-column grid at all breakpoints.
95
-
96
- | Property | xl/lg | md | sm/xs | base |
97
- | ------------------ | ----- | ---- | ----- | ---- |
98
- | Horizontal margins | 64px | 48px | 32px | 16px |
99
- | Column gutters | 32px | 24px | 16px | 8px |
100
- | Row gaps | 32px | 24px | 16px | 8px |
101
-
102
- Minimum touch target on mobile: 44×44px — see [`gamut-accessibility`](../gamut-accessibility/SKILL.md).
103
-
104
- ## Responsive rules
105
-
106
- - Begin design work at 1440px (XL), then adapt down.
107
- - Multi-column layouts collapse to fewer columns — do not stretch or squish.
108
- - Catalog cards and non-lockup elements should align on one axis (usually left), not fill column widths.
109
- - Avoid dense or small components at the base (mobile) breakpoint.
@@ -1,273 +0,0 @@
1
- ---
2
- name: gamut-list
3
- description: Use this skill when building list or table layouts with List, ListRow, ListCol, and TableHeader — including variant and spacing selection, expandable row patterns, ordered/table layouts, and the rule that a list of disclosure-style items must use List's expandable row pattern (not multiple Disclosure components). See gamut-accessibility for ARIA and focus guidance.
4
- ---
5
-
6
- # Gamut List
7
-
8
- Structured, repeating layouts built from `List`, `ListRow`, `ListCol`, and `TableHeader`. Colors, borders, and spacing are wired through the `variant` and `spacing` props — consumers do not override these with raw CSS values.
9
-
10
- Source: `@codecademy/gamut` — [List.tsx](https://github.com/Codecademy/gamut/blob/main/packages/gamut/src/List/List.tsx)
11
-
12
- See also: [`gamut-accessibility`](../gamut-accessibility/SKILL.md) — ARIA, focus, and keyboard interaction rules. [`gamut-layout`](../gamut-layout/SKILL.md) — spacing tokens and system props.
13
-
14
- Storybook:
15
-
16
- - [Organisms / Lists & Tables / List](https://gamut.codecademy.com/?path=/docs-organisms-lists-tables-list-list--docs)
17
- - [ListRow](https://gamut.codecademy.com/?path=/docs-organisms-lists-tables-list-listrow--docs)
18
- - [ListCol](https://gamut.codecademy.com/?path=/docs-organisms-lists-tables-list-listcol--docs)
19
-
20
- ## Components
21
-
22
- ```tsx
23
- import { List, ListRow, ListCol, TableHeader } from '@codecademy/gamut';
24
- ```
25
-
26
- | Component | Role |
27
- | ------------- | ----------------------------------------------------------------------- |
28
- | `List` | Root wrapper; sets `variant`, `spacing`, `as`, and context for children |
29
- | `ListRow` | Single row; handles expandable content and click interactions |
30
- | `ListCol` | Column cell; controls `type`, `size`, `fill`, and justification |
31
- | `TableHeader` | Sticky header row; use only with `List as="table"` |
32
-
33
- ## When to use List
34
-
35
- - Displaying repetitive content where individual rows may contain interactive elements, metrics, or controls — use List, not Card.
36
- - Comparing data across rows — use `variant="table"` (or `as="table"`) rather than a plain `<table>`.
37
- - Needing numbered rows — use `as="ol"`.
38
- - **Needing multiple expandable/disclosure-style items** — use List's expandable row pattern (see [Expandable rows](#expandable-rows)), not multiple standalone `Disclosure` components.
39
-
40
- ## Variants
41
-
42
- ```tsx
43
- <List variant="default" />
44
- ```
45
-
46
- | `variant` | Use for |
47
- | --------- | ------------------------------------------------------------------------------------------------------- |
48
- | `default` | Rows with abstract content (buttons, custom renders); bordered, no gutter |
49
- | `table` | Metrics or comparable data; alternating row backgrounds |
50
- | `card` | Content that doesn't need to be adjacent (e.g. curriculum progress); bordered rows with vertical gutter |
51
- | `block` | Feature-forward designs or page scaffolding; always on a colored background |
52
- | `plain` | Minimal styling — no borders or backgrounds; apply custom styles per row via Emotion `styled` |
53
-
54
- ## Spacing
55
-
56
- ```tsx
57
- <List spacing="condensed" />
58
- ```
59
-
60
- | `spacing` | Use for |
61
- | ----------- | -------------------------------------------- |
62
- | `normal` | Mixed content that needs room for components |
63
- | `condensed` | Default choice; reduced padding between rows |
64
- | `compact` | Tightest layout; data-dense views |
65
-
66
- ## `as` prop
67
-
68
- Default is `ul`. Pass `as="ol"` for numbered rows or `as="table"` for semantic table output.
69
-
70
- ```tsx
71
- // ordered list — always include one ListCol with type="header" so numbering renders correctly
72
- <List as="ol">
73
- <ListRow>
74
- <ListCol type="header">Step one</ListCol>
75
- <ListCol>Details</ListCol>
76
- </ListRow>
77
- </List>
78
-
79
- // semantic table with sticky header
80
- <List as="table">
81
- <TableHeader>
82
- <ListCol columnHeader>Name</ListCol>
83
- <ListCol columnHeader>Role</ListCol>
84
- </TableHeader>
85
- <ListRow>
86
- <ListCol type="header">Worf</ListCol>
87
- <ListCol>Lieutenant Commander</ListCol>
88
- </ListRow>
89
- </List>
90
- ```
91
-
92
- ## Key props
93
-
94
- ### List
95
-
96
- | Prop | Type | Default | Effect |
97
- | ----------------------- | ------------------------------------------------------ | ----------- | -------------------------------------------------------- |
98
- | `variant` | `'default' \| 'table' \| 'card' \| 'block' \| 'plain'` | `'default'` | Row styling |
99
- | `spacing` | `'normal' \| 'condensed' \| 'compact'` | `'normal'` | Row padding |
100
- | `as` | `'ul' \| 'ol' \| 'table'` | `'ul'` | Rendered element and semantic meaning |
101
- | `header` | `React.ReactNode` | — | Node rendered above the row list |
102
- | `emptyMessage` | `React.ReactNode` | — | Shown when children is empty |
103
- | `loading` | `boolean` | — | Shows placeholder while data loads |
104
- | `scrollable` | `boolean` | `false` | Enables horizontal scroll with sticky first column |
105
- | `shadow` | `boolean` | `false` | Right-side shadow when scrollable content overflows |
106
- | `disableContainerQuery` | `boolean` | `false` | Falls back to media queries instead of container queries |
107
- | `rowBreakpoint` | `'xs' \| 'sm' \| 'md'` | `'xs'` | Breakpoint at which columns stack |
108
-
109
- ### ListRow
110
-
111
- | Prop | Type | Notes |
112
- | -------------------------- | ----------------------- | ----------------------------------------------------------------------- |
113
- | `expanded` | `boolean` | Required when `renderExpanded` is set |
114
- | `renderExpanded` | `() => React.ReactNode` | Content revealed when `expanded` is true; animates in/out |
115
- | `expandedRowAriaLabel` | `string` | `aria-label` for the revealed region |
116
- | `keepSpacingWhileExpanded` | `boolean` | Maintains row spacing while content is expanded |
117
- | `onClick` | mouse event handler | Makes the full row interactive (adds `role="button"`, keyboard support) |
118
-
119
- ### ListCol `type`
120
-
121
- | `type` | Use for |
122
- | --------------- | ------------------------------------------------------ |
123
- | `header` | Primary label column; sticky when `List` is scrollable |
124
- | `content` | Secondary text content |
125
- | `control` | Action controls (buttons, menus) |
126
- | `expand` | Expanded content area |
127
- | `expandControl` | The toggle button column (no right-padding) |
128
- | `select` | Checkbox / selection column |
129
-
130
- ### ListCol `size`
131
-
132
- `'content'` (default, fits content) | `'sm'` | `'md'` | `'lg'` | `'xl'`
133
-
134
- Pass `fill` to grow a column to fill remaining space.
135
-
136
- ## Expandable rows
137
-
138
- List provides two patterns for rows that reveal content. Both animate open/closed via framer-motion.
139
-
140
- ### Expand on button click
141
-
142
- Use `ExpandControl` in a `type="expandControl"` column to toggle a specific row.
143
-
144
- ```tsx
145
- const [isExpanded, setExpanded] = useState(false);
146
-
147
- <ListRow
148
- expanded={isExpanded}
149
- renderExpanded={() => <Text>Revealed content</Text>}
150
- expandedRowAriaLabel="Row details"
151
- >
152
- <ListCol type="header">Row label</ListCol>
153
- <ListCol type="content">Secondary detail</ListCol>
154
- <ListCol type="expandControl">
155
- <ExpandControl
156
- expanded={isExpanded}
157
- onExpand={() => setExpanded(!isExpanded)}
158
- />
159
- </ListCol>
160
- </ListRow>;
161
- ```
162
-
163
- ### Expand on row click
164
-
165
- Pass `onClick` to `ListRow` to make the entire row the toggle target. The row receives `role="button"` and keyboard Enter support automatically.
166
-
167
- ```tsx
168
- const [isExpanded, setExpanded] = useState(false);
169
-
170
- <ListRow
171
- expanded={isExpanded}
172
- onClick={() => setExpanded(!isExpanded)}
173
- renderExpanded={() => <Text>Revealed content</Text>}
174
- >
175
- <ListCol type="header">Row label</ListCol>
176
- <ListCol type="content">Secondary detail</ListCol>
177
- <ListCol type="control">
178
- <Rotation rotated={isExpanded}>
179
- <ArrowChevronDownIcon color="text-disabled" />
180
- </Rotation>
181
- </ListCol>
182
- </ListRow>;
183
- ```
184
-
185
- ## List of disclosure-style items
186
-
187
- When you need multiple expandable items (an FAQ, an accordion, a list of sections), **use List's expandable row pattern above — do not render multiple standalone `Disclosure` components**.
188
-
189
- `Disclosure` is designed for a single, isolated expandable container. For two or more expandable items, the correct pattern is `List` + `ListRow` with `expanded` / `renderExpanded`:
190
-
191
- ```tsx
192
- // correct — list of expandable items
193
- <List variant="default" spacing="normal">
194
- {items.map(({ id, title, body }) => {
195
- const [isExpanded, setExpanded] = useState(false);
196
- return (
197
- <ListRow
198
- key={id}
199
- expanded={isExpanded}
200
- renderExpanded={() => <Box p={16}>{body}</Box>}
201
- expandedRowAriaLabel={`${title} details`}
202
- >
203
- <ListCol type="header" fill>{title}</ListCol>
204
- <ListCol type="expandControl">
205
- <ExpandControl
206
- expanded={isExpanded}
207
- onExpand={() => setExpanded(!isExpanded)}
208
- />
209
- </ListCol>
210
- </ListRow>
211
- );
212
- })}
213
- </List>
214
-
215
- // wrong — multiple Disclosure components
216
- <Disclosure heading="Item 1" body="..." />
217
- <Disclosure heading="Item 2" body="..." />
218
- ```
219
-
220
- ## Empty state and loading
221
-
222
- ```tsx
223
- // custom empty message
224
- <List emptyMessage={<Text>No results found.</Text>}>
225
- {rows}
226
- </List>
227
-
228
- // loading placeholder
229
- <List loading>{rows}</List>
230
- ```
231
-
232
- ## Scrollable layout
233
-
234
- Use `scrollable` when a list has many columns and collapsing would lose information. The first (`type="header"`) column sticks to the left.
235
-
236
- ```tsx
237
- <List scrollable shadow>
238
- <TableHeader>
239
- <ListCol type="header" columnHeader>
240
- Name
241
- </ListCol>
242
- <ListCol size="md" columnHeader>
243
- Score
244
- </ListCol>
245
- <ListCol size="md" columnHeader>
246
- Progress
247
- </ListCol>
248
- </TableHeader>
249
- {rows}
250
- </List>
251
- ```
252
-
253
- ## Container queries
254
-
255
- By default `List` uses CSS container queries for responsive column stacking. Disable only when:
256
-
257
- - The `List` lives in a container narrower than its breakpoint
258
- - You are managing your own responsive logic
259
- - The list has very few rows and container query overhead is unwanted
260
-
261
- ```tsx
262
- <List disableContainerQuery spacing="condensed">
263
- {rows}
264
- </List>
265
- ```
266
-
267
- ## Accessibility
268
-
269
- - `ListRow` sets `aria-live="polite"` automatically when `renderExpanded` is present — do not add a duplicate live region.
270
- - Pass `expandedRowAriaLabel` to label the revealed `role="region"`.
271
- - `onClick` on `ListRow` adds `role="button"` and keyboard Enter support automatically.
272
- - For sortable columns pass `aria-sort` on the relevant `ListCol`.
273
- - Ordered lists (`as="ol"`) must include at least one `ListCol type="header"` per row for numbering to render correctly.