@antadesign/anta 0.3.33 → 0.3.35

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 (205) hide show
  1. package/LICENSE.md +14 -1
  2. package/README.md +9 -0
  3. package/dist/anta_helpers.d.ts +46 -30
  4. package/dist/anta_helpers.js +66 -25
  5. package/dist/bundle.css +1 -1
  6. package/dist/bundle.js +2 -1353
  7. package/dist/components/Banner.d.ts +25 -40
  8. package/dist/components/Banner.js +15 -17
  9. package/dist/components/Breadcrumbs.d.ts +0 -1
  10. package/dist/components/Breadcrumbs.js +0 -1
  11. package/dist/components/Button.d.ts +2 -3
  12. package/dist/components/Button.js +1 -0
  13. package/dist/components/ButtonCopy.js +3 -2
  14. package/dist/components/Calendar.js +13 -13
  15. package/dist/components/Card.d.ts +9 -17
  16. package/dist/components/Card.js +50 -46
  17. package/dist/components/Dialog.d.ts +1 -0
  18. package/dist/components/Dialog.js +16 -10
  19. package/dist/components/Expander.d.ts +6 -21
  20. package/dist/components/Expander.js +24 -23
  21. package/dist/components/Icon.d.ts +8 -14
  22. package/dist/components/Input.d.ts +77 -96
  23. package/dist/components/Input.js +23 -27
  24. package/dist/components/InputAutocomplete.d.ts +29 -10
  25. package/dist/components/InputAutocomplete.js +25 -8
  26. package/dist/components/InputDate.d.ts +0 -1
  27. package/dist/components/InputDate.js +2 -3
  28. package/dist/components/InputTime.d.ts +10 -4
  29. package/dist/components/InputTime.js +20 -16
  30. package/dist/components/Loader.d.ts +2 -2
  31. package/dist/components/Menu.d.ts +5 -4
  32. package/dist/components/Menu.js +13 -8
  33. package/dist/components/MenuItem.js +1 -14
  34. package/dist/components/MenuItemCopy.js +2 -2
  35. package/dist/components/Panel.d.ts +3 -1
  36. package/dist/components/Panel.js +17 -11
  37. package/dist/components/Progress.d.ts +21 -23
  38. package/dist/components/Progress.js +13 -9
  39. package/dist/components/RadioGroup.js +6 -8
  40. package/dist/components/{select-parts.css → Select.css} +1 -1
  41. package/dist/components/Select.d.ts +53 -36
  42. package/dist/components/Select.js +53 -98
  43. package/dist/components/SelectFaceted.d.ts +7 -4
  44. package/dist/components/SelectFaceted.js +21 -10
  45. package/dist/components/Slider.d.ts +28 -11
  46. package/dist/components/Slider.js +20 -20
  47. package/dist/components/Steps.css +1 -1
  48. package/dist/components/Steps.d.ts +0 -1
  49. package/dist/components/Steps.js +1 -12
  50. package/dist/components/Tabs.d.ts +17 -20
  51. package/dist/components/Tabs.js +7 -9
  52. package/dist/components/Text.d.ts +55 -36
  53. package/dist/components/Text.js +35 -22
  54. package/dist/components/Title.d.ts +19 -27
  55. package/dist/components/Title.js +2 -1
  56. package/dist/components/Toaster.js +9 -2
  57. package/dist/components/Tooltip.d.ts +10 -15
  58. package/dist/components/copy-props.d.ts +14 -3
  59. package/dist/components/copy-props.js +14 -3
  60. package/dist/components/select-tree.d.ts +24 -0
  61. package/dist/components/select-tree.js +35 -0
  62. package/dist/element-base.d.ts +10 -0
  63. package/dist/element-base.js +15 -0
  64. package/dist/elements/a-avatar.css +1 -1
  65. package/dist/elements/a-avatar.js +2 -28
  66. package/dist/elements/a-banner.css +1 -1
  67. package/dist/elements/a-banner.d.ts +4 -1
  68. package/dist/elements/a-banner.js +22 -112
  69. package/dist/elements/a-breadcrumbs.d.ts +1 -0
  70. package/dist/elements/a-breadcrumbs.js +1 -0
  71. package/dist/elements/a-button.css +1 -1
  72. package/dist/elements/a-button.d.ts +10 -7
  73. package/dist/elements/a-button.js +18 -1
  74. package/dist/elements/a-calendar.d.ts +14 -13
  75. package/dist/elements/a-calendar.js +29 -24
  76. package/dist/elements/a-card.css +1 -1
  77. package/dist/elements/a-card.d.ts +0 -9
  78. package/dist/elements/a-card.js +0 -184
  79. package/dist/elements/a-checkbox.css +1 -1
  80. package/dist/elements/a-copy.css +1 -1
  81. package/dist/elements/a-copy.d.ts +1 -5
  82. package/dist/elements/a-copy.js +129 -108
  83. package/dist/elements/a-dialog.css +1 -1
  84. package/dist/elements/a-dialog.d.ts +1 -1
  85. package/dist/elements/a-dialog.js +68 -194
  86. package/dist/elements/a-expander.css +1 -1
  87. package/dist/elements/a-expander.d.ts +4 -30
  88. package/dist/elements/a-expander.js +85 -344
  89. package/dist/elements/a-icon.d.ts +5 -23
  90. package/dist/elements/a-icon.js +1 -2
  91. package/dist/elements/a-input-date.d.ts +1 -0
  92. package/dist/elements/a-input-date.js +1 -0
  93. package/dist/elements/a-input-time.css +1 -1
  94. package/dist/elements/a-input-time.d.ts +2 -1
  95. package/dist/elements/a-input-time.js +101 -168
  96. package/dist/elements/a-input.css +1 -1
  97. package/dist/elements/a-input.d.ts +10 -8
  98. package/dist/elements/a-input.js +155 -230
  99. package/dist/elements/a-loader.d.ts +5 -8
  100. package/dist/elements/a-loader.js +1 -2
  101. package/dist/elements/a-menu-item.css +1 -1
  102. package/dist/elements/a-menu-item.d.ts +5 -3
  103. package/dist/elements/a-menu-item.js +5 -1
  104. package/dist/elements/a-menu.css +1 -1
  105. package/dist/elements/a-menu.d.ts +5 -7
  106. package/dist/elements/a-menu.js +383 -299
  107. package/dist/elements/a-panel.css +1 -1
  108. package/dist/elements/a-panel.js +19 -37
  109. package/dist/elements/a-progress.css +1 -1
  110. package/dist/elements/a-progress.d.ts +0 -22
  111. package/dist/elements/a-progress.js +0 -100
  112. package/dist/elements/a-radio-group.d.ts +1 -1
  113. package/dist/elements/a-radio-group.js +6 -4
  114. package/dist/elements/a-select-faceted.d.ts +1 -0
  115. package/dist/elements/a-select-faceted.js +1 -0
  116. package/dist/elements/a-select.d.ts +1 -0
  117. package/dist/elements/a-select.js +1 -0
  118. package/dist/elements/a-slider.css +1 -1
  119. package/dist/elements/a-slider.d.ts +11 -3
  120. package/dist/elements/a-slider.js +244 -242
  121. package/dist/elements/a-steps.d.ts +1 -0
  122. package/dist/elements/a-steps.js +1 -0
  123. package/dist/elements/a-switch.css +1 -1
  124. package/dist/elements/a-tab.css +1 -1
  125. package/dist/elements/a-tabpanel.d.ts +2 -2
  126. package/dist/elements/a-tabpanel.js +13 -3
  127. package/dist/elements/a-tabs.css +1 -1
  128. package/dist/elements/a-tabs.d.ts +1 -1
  129. package/dist/elements/a-tabs.js +6 -4
  130. package/dist/elements/a-tag.css +1 -1
  131. package/dist/elements/a-tag.d.ts +1 -0
  132. package/dist/elements/a-tag.js +1 -0
  133. package/dist/elements/a-text.css +1 -1
  134. package/dist/elements/a-text.d.ts +6 -43
  135. package/dist/elements/a-text.js +110 -193
  136. package/dist/elements/a-title.css +1 -1
  137. package/dist/elements/a-title.d.ts +1 -0
  138. package/dist/elements/a-title.js +1 -0
  139. package/dist/elements/a-toast.css +1 -1
  140. package/dist/elements/a-toast.d.ts +10 -45
  141. package/dist/elements/a-toast.js +80 -66
  142. package/dist/elements/a-toaster.css +1 -1
  143. package/dist/elements/a-toaster.js +9 -44
  144. package/dist/elements/a-tooltip.css +1 -1
  145. package/dist/elements/a-tooltip.d.ts +1 -1
  146. package/dist/elements/a-tooltip.js +142 -136
  147. package/dist/elements/copy-behavior.d.ts +1 -1
  148. package/dist/elements/copy-behavior.js +21 -21
  149. package/dist/elements/index.d.ts +9 -3
  150. package/dist/elements/index.js +6 -6
  151. package/dist/elements/input-focus-modality.d.ts +15 -0
  152. package/dist/elements/input-focus-modality.js +44 -0
  153. package/dist/elements/input-trailing-actions.d.ts +5 -0
  154. package/dist/elements/input-trailing-actions.js +38 -0
  155. package/dist/elements/shadow-aria.d.ts +2 -0
  156. package/dist/elements/shadow-aria.js +2 -1
  157. package/dist/elements/value-input.d.ts +3 -0
  158. package/dist/elements/value-input.js +12 -0
  159. package/dist/general_types.d.ts +103 -96
  160. package/dist/index.d.ts +1 -1
  161. package/dist/input-events.d.ts +4 -0
  162. package/dist/input-events.js +15 -0
  163. package/dist/jsx-runtime.d.ts +70 -23
  164. package/dist/jsx-runtime.js +26 -19
  165. package/dist/native-event-props.d.ts +2 -0
  166. package/dist/native-event-props.js +10 -0
  167. package/dist/reset.css +1 -1
  168. package/dist/theme-antithesis.css +10 -1
  169. package/dist/theme-antune.css +1 -1
  170. package/docs/accessibility.md +17 -3
  171. package/docs/changelog.md +384 -2
  172. package/docs/comparison.md +2 -2
  173. package/docs/components/avatar.md +5 -0
  174. package/docs/components/banner.md +117 -187
  175. package/docs/components/breadcrumbs.md +9 -6
  176. package/docs/components/button.md +22 -0
  177. package/docs/components/card.md +74 -28
  178. package/docs/components/checkbox.md +9 -10
  179. package/docs/components/dialog.md +83 -32
  180. package/docs/components/expander.md +198 -435
  181. package/docs/components/icon.md +11 -17
  182. package/docs/components/input-autocomplete.md +152 -52
  183. package/docs/components/input-date.md +175 -148
  184. package/docs/components/input-time.md +81 -38
  185. package/docs/components/input.md +343 -349
  186. package/docs/components/loader.md +2 -1
  187. package/docs/components/menu.md +35 -6
  188. package/docs/components/panel.md +18 -0
  189. package/docs/components/progress.md +83 -49
  190. package/docs/components/radio.md +39 -43
  191. package/docs/components/select-faceted.md +54 -7
  192. package/docs/components/select.md +390 -386
  193. package/docs/components/slider.md +136 -19
  194. package/docs/components/steps.md +6 -3
  195. package/docs/components/switch.md +8 -9
  196. package/docs/components/tabs.md +110 -104
  197. package/docs/components/text.md +110 -86
  198. package/docs/components/title.md +47 -33
  199. package/docs/components/toaster.md +242 -84
  200. package/docs/components/tooltip.md +204 -113
  201. package/docs/install-config.md +461 -96
  202. package/docs/packages/plot.md +228 -143
  203. package/docs/theming.md +29 -13
  204. package/package.json +43 -4
  205. package/theme-antithesis-license.md +66 -0
@@ -1,346 +1,182 @@
1
1
  # Expander
2
2
 
3
- A collapsible disclosure: a header that toggles a region of content open
4
- and closed. The `Expander` JSX wrapper renders an `<a-expander>`, whose
5
- shadow DOM is a `<button>` summary plus a collapsible region — so focus,
6
- keyboard activation (Enter / Space), and the
7
- WAI-ARIA disclosure semantics (`aria-expanded`) come from the platform
8
- button. The expand / collapse animation matches the disclosure sections
9
- used across these docs.
3
+ Expander keeps a section title visible while its content opens and closes. Use
4
+ it for supporting details, settings groups, or sections that readers can reveal
5
+ as needed. Select the header or focus it and press Enter or Space to toggle.
10
6
 
11
- The `title` is the always-visible header; the `children` are the
12
- collapsible body.
7
+ ## Priority and tone
13
8
 
14
- ## Priority
15
-
16
- `priority` sets the surface, reusing `Button`/`Text`'s vocabulary.
17
- `primary` is the most pronounced card; `secondary` (the default) is a
18
- subtle filled box; `tertiary` is transparent — just the header row and
19
- its chevron, for an inline disclosure.
20
-
21
- ```tsx
22
- <Expander title="Primary" priority="primary">…</Expander>
23
- <Expander title="Secondary (default)">…</Expander> {/* secondary is the default */}
24
- <Expander title="Tertiary" priority="tertiary">…</Expander>
25
- ```
26
-
27
- ## Round
28
-
29
- `round` sizes the corners to **half the folded header height** — so a folded
30
- expander reads as a pill, and an open one keeps that *same* corner radius instead
31
- of a bigger stadium. The header is measured (a `ResizeObserver`), so the pill
32
- tracks the header at any `level` or with custom header content. Open the example
33
- to see the corners hold their radius as the body appears.
34
-
35
- ```tsx
36
- <Expander round title="Details" priority="primary">…</Expander>
37
- ```
38
-
39
- ## Tone
40
-
41
- Pick a semantic `tone` to tint the header text and — on the filled
42
- priorities (`secondary` / `primary`) — the surface too. The set matches
43
- `Button` / `Tag`: `neutral` (the default) plus `brand`, `info`,
44
- `success`, `warning`, and `critical`. Color comes from the theme tokens,
45
- so every tone tracks light and dark mode automatically. Every tone is
46
- shown below across all three priorities, so you can compare (rows are
47
- tones, columns are priorities):
48
-
49
- **Primary**
50
-
51
- **Secondary** (default)
52
-
53
- **Tertiary**
54
-
55
- ```tsx
56
- // every tone × every priority — e.g. brand:
57
- <Expander tone="brand" priority="primary" title="Brand" />
58
- <Expander tone="brand" title="Brand" /> {/* secondary — the default */}
59
- <Expander tone="brand" priority="tertiary" title="Brand" />
60
- // …repeat for neutral / info / success / warning / critical
61
- ```
62
-
63
- `tone` also accepts **any literal CSS color** for a one-off tint — the
64
- hue is kept while lightness and chroma are pinned to the named-tone curve
65
- (and re-tuned for dark mode), the same mechanism as `Button` / `Tag`:
66
-
67
- ```tsx
68
- // shown on the `primary` surface — the custom tone tints the fill;
69
- // drop to "secondary" (the default) for a subtler card, or "tertiary" for text only.
70
- <Expander tone="#ff1493" priority="primary" title="Magenta" />
71
- <Expander tone="rebeccapurple" priority="primary" title="Purple" />
72
- <Expander tone="#0d9488" priority="primary" title="Teal" />
73
- ```
74
-
75
- ## Outdent
76
-
77
- By default the chevron sits **inside** the header row. On a transparent
78
- `priority="tertiary"` expander, add `outdent` to hang the chevron in the
79
- left gutter so the **title and body sit flush** with the surrounding
80
- content — the same layout as the section headers in these docs. Under the
81
- hood it zeroes `--expander-gutter`: the title and body lose their inset and
82
- the chevron, positioned in that gutter, auto-hangs to the left. The
83
- transparent border stays (it keeps tertiary content aligned with the filled
84
- priorities), and a small internal correction cancels the border's 1px inset
85
- so the title and body line up exactly with the surrounding content.
9
+ Choose `primary` for a pronounced surface, `secondary` for a subtle filled box,
10
+ or `tertiary` for a transparent disclosure in a content column. `tone` colors
11
+ the header and filled surfaces using the current theme.
86
12
 
87
13
  ```tsx
88
- <Expander priority="tertiary" outdent title="…">…</Expander>
14
+ <Expander title="Primary" priority="primary" tone="brand"><Text>A pronounced section surface.</Text></Expander>
15
+ <Expander title="Secondary"><Text>A subtle section surface.</Text></Expander>
16
+ <Expander title="Tertiary" priority="tertiary"><Text>A disclosure in a content column.</Text></Expander>
89
17
  ```
90
18
 
91
- `outdent` only takes effect on `tertiary` — on the filled priorities
92
- (`secondary` / `primary`) the container edge has to bound the chevron, so
93
- it's a no-op there. Use it where the expander sits in a content column
94
- with room to its left (a prose measure); in a full-width / edge-to-edge
95
- container the chevron would overhang the edge.
96
-
97
- ## Indicator
98
-
99
- `indicatorPlacement="end"` puts the disclosure mark after the header actions.
100
- The mark stays inside the summary button, so selecting it toggles the section.
101
- Actions remain separate controls. The default `start` placement keeps the
102
- right-pointing chevron that turns down when open. At the end, it points down
103
- when closed and up when open.
104
-
105
- Choose `indicator="triangle"` for a filled arrow or `indicator="plus"` for a
106
- plus that becomes a minus. The default is `chevron`. The built-in motion follows
107
- the reduced-motion preference. An unrecognized string or number leaves the
108
- indicator empty; omit `indicator` to keep the default chevron.
109
-
110
- ```tsx
111
- <Expander title="Download report" indicatorPlacement="end"
112
- actions={<Button size="small" label="Download" />}>
113
- …
114
- </Expander>
115
- <Expander title="Plus and minus" indicator="plus" indicatorPlacement="end">…</Expander>
116
- ```
117
-
118
- Pass a decorative node as `indicator` to replace the built-in mark. One node
119
- rotates with the section. To switch between two visuals, pass `closed` and
120
- `open` nodes. Neither form adds a focus stop or an accessible name: the title
121
- names the summary button, and its `aria-expanded` reports the state. Put
122
- interactive controls in `actions`.
123
-
124
- ```tsx
125
- <Expander title="Custom mark" indicatorPlacement="end"
126
- indicator={<Icon shape="chevron-down" />}>…</Expander>
127
- <Expander title="Two marks" indicatorPlacement="end"
128
- indicator={{ closed: <Icon shape="plus" />, open: <Icon shape="minus" /> }}>…</Expander>
129
- ```
130
-
131
- On a tertiary end-placement expander, `outdent` still aligns the title and body
132
- with surrounding content. The indicator stays inside the end edge.
133
-
134
19
  ## Title and level
135
20
 
136
- `title` takes a string or any node. For a string, `level` (1–6) applies
137
- the matching heading **type scale** — the same scale as `<Title>` —
138
- without changing the document outline. Only the type scale changes: the
139
- title's left alignment is constant across every level (the chevron + a
140
- fixed 4px edge inset), so swapping `level` never shifts the header
141
- horizontally.
21
+ A string or number title uses Expander's type scale. `level` changes that scale
22
+ without adding heading semantics. Pass a `Title` or your own heading when the
23
+ section belongs in the document outline; that node owns its typography.
142
24
 
143
25
  ```tsx
144
- <Expander title="Level 1" level={1}>…</Expander>
145
- <Expander title="Level 2" level={2}>…</Expander>
146
- <Expander title="Level 3" level={3}>…</Expander>
147
- <Expander title="Level 4" level={4}>…</Expander>
148
- <Expander title="Level 5 (default)">…</Expander> {/* level 5 is the default */}
149
- <Expander title="Level 6" level={6}><Text size="small">…</Text></Expander> {/* pair the smallest level with small body text */}
26
+ <Expander title="Section title" level={2}><Text>A larger title scale.</Text></Expander>
27
+ <Expander title="Supporting details" level={6}><Text>A compact title scale.</Text></Expander>
150
28
  ```
151
29
 
152
- When you need real heading **semantics** — so the title lands in a
153
- page's table of contents (and assistive tech announces it as a heading) —
154
- pass a `<Title>` (or an application `<h2>`–`<h6>`) as the `title` instead of a
155
- string. The heading's `level` sets its type scale, so set it on the heading,
156
- not on `<Expander>`. A slotted heading keeps its block margins, so reset them
157
- in the header:
30
+ Selecting a link, input, or enabled control in the title activates that control
31
+ without toggling the section. Selecting title text keeps the selection. Prefer
32
+ `actions` for independent header controls so they sit outside the toggle button.
158
33
 
159
- ```tsx
160
- {/* A Title supplies its type scale and heading semantics. Set the level on
161
- the Title, not on Expander. (The preview uses element composition
162
- because these docs render statically; the title-prop form is identical.) */}
163
- <Expander title={<Title level={2}>Title level 2 example</Title>}>…</Expander>
164
- <Expander title={<Title level={4}>Title level 4 example</Title>}>…</Expander>
165
- <Expander title={<Title level={6}>Title level 6 example</Title>}>…</Expander>
166
-
167
- {/* a slotted heading keeps its block-rhythm margins — zero them in the header */}
168
- <style>{`
169
- a-expander a-title { margin: 0; }
170
- `}</style>
171
- ```
172
-
173
- ## Controls in the title
34
+ ## Actions and indicators
174
35
 
175
- A control placed **inside** the `title` — a copy button next to the
176
- heading text, an inline link, an inline `Input` — copies or activates
177
- without toggling the section. The header refuses to toggle when the click
178
- landed on any Anta control (`Button` / `ButtonCopy`, `Checkbox`, `Radio`,
179
- `Input`, `Select`, `InputDate`, `Calendar`, a `Menu` item, a `Tab`) or a
180
- native control (`button` / `a[href]` / `input` / `select` / `textarea` /
181
- `label` / editable), so a `ButtonCopy` beside the title works with no
182
- extra wiring:
36
+ <a id="indicator"></a>
183
37
 
184
- ```tsx
185
- {/* A ButtonCopy in the title copies without toggling — no stopPropagation needed. */}
186
- <Expander title={<>Debug timeline <ButtonCopy copy={moment} icon="copy" size="small" priority="tertiary" /></>}>
187
- …
188
- </Expander>
189
- ```
38
+ Actions stay visible when the section is closed. Use them for operations that
39
+ should remain available without opening the content. `indicatorPlacement="end"`
40
+ places the disclosure mark after the actions. At the start, the chevron turns
41
+ from right to down; at the end, it turns from down to up.
190
42
 
191
- The decision is read from the click's composed path synchronously in the
192
- element, so it holds even where event handlers run off the UI thread (a
193
- consumer's `stopPropagation()` can arrive too late to beat the toggle) —
194
- the same contract as `Menu`'s close-on-outside-click.
43
+ Choose `triangle` for a filled arrow or `plus` for a plus that becomes a minus.
44
+ Custom artwork can rotate as one node or switch between `closed` and `open`
45
+ visuals. Indicator artwork stays decorative and adds no focus stop.
195
46
 
196
- A **disabled** control is inert, so its click toggles the section like the
197
- title text does — the exclusion applies only to live controls. For a
198
- control the detection can't recognize (a non-interactive custom widget
199
- with its own handler), mark it `data-expander-ignore` to opt it out of the
200
- toggle:
47
+ The demonstration uses a custom title, two indicator visuals, and an independent
48
+ Download action. Open the section to see the action count.
201
49
 
202
50
  ```tsx
203
- <Expander title={<>Report <MyWidget data-expander-ignore /></>}>…</Expander>
204
- ```
51
+ import { Button, Expander, Icon, Text, Title } from '@antadesign/anta'
52
+ import { useState } from 'react'
205
53
 
206
- ## Actions
54
+ function ExpanderActionsDemo() {
55
+ const [downloads, setDownloads] = useState(0)
56
+ return (
57
+ <Expander
58
+ title={<Title level={4}>Build artifacts</Title>}
59
+ indicatorPlacement="end"
60
+ indicator={{ closed: <Icon shape="plus" />, open: <Icon shape="minus" /> }}
61
+ actions={<Button size="small" label="Download" onClick={() => setDownloads(count => count + 1)} />}
62
+ >
63
+ <Text>Download requests: {downloads}. The action leaves the disclosure state unchanged.</Text>
64
+ </Expander>
65
+ )
66
+ }
67
+ ```
207
68
 
208
- Pass `actions` to render extra controls — buttons, tags — after the title.
209
- They sit **outside** the toggle trigger as its header siblings. Clicking them
210
- never toggles the expander; they're separately focusable and announced as
211
- their own controls.
212
- (Don't put interactive elements *inside* a custom `title` instead —
213
- nesting controls in a button is an accessibility anti-pattern.)
69
+ ## Round and outdent
214
70
 
215
- ```tsx
216
- <Expander title="Build artifacts" actions={<Button size="small" label="Download" />}>
217
- …
218
- </Expander>
219
- ```
71
+ <a id="round"></a>
220
72
 
221
- The header never wraps: actions keep their intrinsic size and the
222
- title ellipsizes first, so keep actions compact (a small button or
223
- two, a tag). Pass several controls as a fragment — they form a row
224
- with a 2px gap:
73
+ Use `round` for a `1rem` radius that stays constant when the section opens.
74
+ Use `outdent` with `priority="tertiary"` to align the title and body with
75
+ surrounding prose. The start indicator hangs in the gutter; an end indicator
76
+ stays inside the end edge.
225
77
 
226
78
  ```tsx
227
- <Expander
228
- title="A very long expandable section title that shows how the actions would push content to ellipsize when space runs out"
229
- actions={<><Button size="small" label="Expand" /><Button size="small" label="Share" /><Button size="small" label="Download" /></>}
230
- >
231
- …
232
- </Expander>
79
+ <Expander title="Rounded section" round><Text>The corners keep their radius when expanded.</Text></Expander>
80
+ <Expander title="Aligned section" priority="tertiary" outdent defaultOpen><Text>The title and body align with the surrounding content.</Text></Expander>
233
81
  ```
234
82
 
235
- Actions stay visible in the folded state — the header represents the
236
- section whether it's open or not (the same convention as GitHub's file
237
- headers). If an action only makes sense while open, drive the expander
238
- in controlled mode and swap the `actions` node on toggle.
239
-
240
83
  ## Disabled
241
84
 
242
- `disabled` freezes the expander: the header is no longer clickable or
243
- focusable, the hover affordance is off, and the text dims. The open
244
- state stays as-is — disabling an open expander keeps it open. Header
245
- `actions` stay live; disable them separately if needed.
85
+ `disabled` removes the toggle button from focus navigation and prevents toggling.
86
+ An open section stays open. Header actions remain available; disable those
87
+ controls separately when needed.
246
88
 
247
89
  ```tsx
248
- <Expander title="Disabled, closed" disabled>…</Expander>
249
- <Expander title="Disabled, open" disabled defaultOpen>…</Expander>
90
+ <Expander title="Disabled, closed" disabled><Text>Collapsed content.</Text></Expander>
91
+ <Expander title="Disabled, open" disabled defaultOpen><Text>Content remains visible.</Text></Expander>
250
92
  ```
251
93
 
252
94
  ## Open state
253
95
 
254
- For uncontrolled use, pass `defaultOpen` for the initial state. The element
255
- then updates its open state after interaction. For controlled use, pass `open`
256
- and handle `onStateChange`. The expander follows the `open` prop and reports a
257
- requested change. Update application state with `detail.next` to accept it, or
258
- leave the state unchanged to reject it.
96
+ Without `open`, Expander owns its state. `defaultOpen` chooses its initial state.
97
+ Closing keeps the content mounted and removes it from focus navigation and the
98
+ accessibility tree. Reopening restores its visibility and interaction.
259
99
 
260
- `onStateChange` is event-first: `(event, { next, prev })`, where `next`/`prev`
261
- are booleans (the requested / previous open state). `statechange` fires
262
- **before** the element applies anything and is **cancelable**, so even
263
- uncontrolled you can veto a toggle synchronously with `event.preventDefault()`
264
- (e.g. a section that confirms before closing) — no need to lift state.
100
+ Call `event.preventDefault()` in `onStateChange` to reject an uncontrolled
101
+ request synchronously. The demonstration keeps the section open until you clear
102
+ the checkbox.
265
103
 
266
104
  ```tsx
267
- // uncontrolled
268
- <Expander title="Details" defaultOpen>…</Expander>
269
-
270
- // uncontrolled, with a veto
271
- <Expander title="Details" onStateChange={(e, { next }) => {
272
- if (!next && unsaved) e.preventDefault() // refuse to close
273
- }}>…</Expander>
105
+ import { Box, Checkbox, Expander, Text } from '@antadesign/anta'
106
+ import { useState } from 'react'
274
107
 
275
- // controlled
276
- const [open, setOpen] = useState(false)
277
- <Expander title="Details" open={open} onStateChange={(e, { next }) => setOpen(next)}>…</Expander>
108
+ function CancelableExample() {
109
+ const [keepOpen, setKeepOpen] = useState(true)
110
+ return (
111
+ <Box display="grid" gap={12} style={{ width: '100%' }}>
112
+ <Checkbox checked={keepOpen} onStateChange={(_, { next }) => setKeepOpen(next === true)}>Keep details open</Checkbox>
113
+ <Expander title="Unsaved changes" defaultOpen onStateChange={(event, { next }) => {
114
+ if (!next && keepOpen) event.preventDefault()
115
+ }}>
116
+ <Text>Clear the checkbox to allow this section to close.</Text>
117
+ </Expander>
118
+ </Box>
119
+ )
120
+ }
278
121
  ```
279
122
 
280
- ## Controlled vs Uncontrolled
123
+ Pass `open` to control the section. Update it with `detail.next` in
124
+ `onStateChange` to accept a request, or leave it unchanged to reject. The
125
+ following demonstration counts requests even when they are rejected.
281
126
 
282
- Open state follows Anta's shared **state contract**. In uncontrolled use, the
283
- element updates open state. In controlled use, the application supplies it. The
284
- `<Expander>` wrapper uses booleans; the `<a-expander>` element uses a `state`
285
- string. The wrapper maps between them:
286
-
287
- | | `<Expander>` (JSX) | `<a-expander>` (element) |
288
- |---|---|---|
289
- | **Uncontrolled** | `defaultOpen` | `default-state="open" \| "closed"` |
290
- | **Controlled** | `open` + `onStateChange` | `state="open" \| "closed"` + `statechange` |
291
- | **Change event** | `onStateChange(event, { next, prev })` — `next` / `prev` are **booleans** | `statechange`, a `CustomEvent<{ next, prev }>` — `'open'` / `'closed'` |
127
+ ```tsx
128
+ import { Box, Button, Checkbox, Expander, Text } from '@antadesign/anta'
129
+ import { useState } from 'react'
292
130
 
293
- `statechange` is **cancelable** and fires *before* the element applies the
294
- change. Uncontrolled, call `event.preventDefault()` to veto a toggle (e.g.
295
- confirm before closing). Controlled, the element never self-applies — apply
296
- `detail.next` to `open` to accept, or do nothing to reject.
131
+ function ControlledExample() {
132
+ const [open, setOpen] = useState(false)
133
+ const [accept, setAccept] = useState(true)
134
+ const [requests, setRequests] = useState(0)
135
+ return (
136
+ <Box display="grid" gap={12} style={{ width: '100%' }}>
137
+ <Checkbox checked={accept} onStateChange={(_, { next }) => setAccept(next === true)}>Accept state requests</Checkbox>
138
+ <Expander title="Release details" open={open} onStateChange={(_, { next }) => {
139
+ setRequests(count => count + 1)
140
+ if (accept) setOpen(next)
141
+ }}>
142
+ <Text>The content stays mounted when closed.</Text>
143
+ </Expander>
144
+ <Text>State requests: {requests}</Text>
145
+ <Button label="Show details" onClick={() => setOpen(true)} />
146
+ </Box>
147
+ )
148
+ }
149
+ ```
297
150
 
298
151
  ### Accordion
299
152
 
300
- An accordion is the controlled pattern applied to a stack of expanders that share one
301
- open value: hold the open item's id in state, point each `open` at it, and on
302
- `onStateChange` set that id (or `null`) — so opening one folds the rest. Wrapping them
303
- in a `.accordion` lets you square the inner corners and overlap the borders by 1px, so
304
- the stack reads as one panel.
153
+ Share one open item identifier across a group of controlled Expanders when only
154
+ one section should be open at a time.
305
155
 
306
156
  ```tsx
307
- const ITEMS = [
308
- { id: 'shipping', title: 'Shipping & delivery', body: '…' },
309
- { id: 'returns', title: 'Returns', body: '…' },
310
- { id: 'warranty', title: 'Warranty', body: '…' },
157
+ import { Box, Expander, Text } from '@antadesign/anta'
158
+ import { useState } from 'react'
159
+
160
+ const items = [
161
+ { id: 'shipping', title: 'Shipping', body: 'Orders ship within two business days.' },
162
+ { id: 'returns', title: 'Returns', body: 'Returns are accepted within 30 days.' },
311
163
  ]
312
164
 
313
165
  function Accordion() {
314
- const [openId, setOpenId] = useState('shipping') // null = all closed
166
+ const [openId, setOpenId] = useState('shipping')
315
167
  return (
316
- <div className="accordion">
317
- {ITEMS.map((item) => (
318
- <Expander
319
- key={item.id}
320
- title={item.title}
321
- open={openId === item.id}
322
- onStateChange={(_e, { next }) => setOpenId(next ? item.id : null)}
323
- >
168
+ <Box display="grid" gap={1}>
169
+ {items.map(item => (
170
+ <Expander key={item.id} title={item.title} open={openId === item.id}
171
+ onStateChange={(_, { next }) => setOpenId(next ? item.id : null)}>
324
172
  <Text>{item.body}</Text>
325
173
  </Expander>
326
174
  ))}
327
- </div>
175
+ </Box>
328
176
  )
329
177
  }
330
178
  ```
331
179
 
332
- ```css
333
- .accordion { display: flex; flex-direction: column; }
334
-
335
- /* Overlap adjacent borders so they share one 1px line. */
336
- .accordion a-expander:not(:first-child) { margin-top: -1px; }
337
-
338
- /* Square the inner corners so the stack reads as one panel. */
339
- .accordion a-expander:first-child { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
340
- .accordion a-expander:last-child { border-top-left-radius: 0; border-top-right-radius: 0; }
341
- .accordion a-expander:not(:first-child):not(:last-child) { border-radius: 0; }
342
- ```
343
-
344
180
  ## Component props
345
181
 
346
182
  | Prop | Type | Default | Description |
@@ -356,187 +192,114 @@ function Accordion() {
356
192
  | `open?` | boolean | — | Controlled open state. When provided, the consumer owns open/close. Selecting the summary requests a change through `onStateChange`; omit `open` for uncontrolled use. |
357
193
  | `outdent?` | boolean | — | Align the title and body with surrounding content on a tertiary expander. With the default start indicator, the mark hangs in the gutter; an end indicator stays inside its edge. |
358
194
  | `priority?` | 'primary' \| 'secondary' \| 'tertiary' | 'secondary' | Surface emphasis. `secondary` (the default) is a subtle fill; `primary` is a more pronounced card; `tertiary` is transparent (the bare disclosure). |
359
- | `round?` | boolean \| number \| string | — | Round corners sized to half the folded (header) height: a pill when folded, and — when expanded — the same corner radius rather than a bigger stadium. The element measures the header, so it tracks custom header content. Pass a `number` (px) or CSS length string for a *fixed* radius that overrides the measurement (and skips it) in both states. |
195
+ | `round?` | boolean \| number \| string | — | Use a `1rem` corner radius in both folded and expanded states, independent of title level or header height. Pass a number (px) or CSS length string for a custom radius. |
360
196
  | `tone?` | 'neutral' \| 'brand' \| 'info' \| 'success' \| 'warning' \| 'critical' \| (string & {}) | 'neutral' | Semantic tone, or any literal CSS color (`'#ff1493'`, `'rebeccapurple'`) for a one-off custom tone. Named tones re-point the text and (on filled priorities) the surface to the matching palette; a custom color keeps its hue while lightness/chroma are pinned. `'neutral'` (the default) is the same as omitting it. |
361
197
 
362
198
  ## Web Component
363
199
 
364
- Use the web component directly when you are not using React or Preact and a native control does not fit.
365
-
366
- Slots hold the title, actions, and an optional custom indicator. The details
367
- element contains the collapsible body.
200
+ Supply direct named regions. Use `a-expander-summary` for the built-in title
201
+ scale, with `level` on that region. Use boxless `a-expander-title` for custom
202
+ title markup. Put controls in `a-expander-actions` and body content in
203
+ `a-expander-details`. Place `a-expander-indicator` before the title or after the
204
+ actions to choose its position. Set its `shape` to `chevron`, `triangle`, or
205
+ `plus`, or omit `shape` and supply custom artwork as children.
368
206
 
369
207
  ```html
370
208
  <a-expander default-state="open">
371
- <a-expander-summary slot="title">Build artifacts</a-expander-summary>
372
- <a-button slot="actions" role="button" tabindex="0" size="small">
373
- <a-button-label>Share</a-button-label>
374
- </a-button>
375
- <a-expander-details>Everything without a slot attribute is the body.</a-expander-details>
376
- </a-expander>
377
- ```
378
-
379
- Use the `indicator` and `indicator-placement` attributes for built-in marks.
380
- For custom artwork, assign one element to `slot="indicator"`. Assign two
381
- elements with `data-when="closed"` and `data-when="open"` when the artwork
382
- changes between states. Indicator content is decorative and cannot receive focus
383
- or pointer events.
384
-
385
- ```html
386
- <a-expander indicator="triangle" indicator-placement="end">…</a-expander>
387
-
388
- <a-expander indicator-placement="end">
389
- <a-expander-summary slot="title">Custom mark</a-expander-summary>
390
- <a-icon slot="indicator" shape="chevron-down" aria-hidden="true" inert></a-icon>
391
- <a-expander-details>Content</a-expander-details>
392
- </a-expander>
393
-
394
- <a-expander indicator-placement="end">
395
- <a-expander-summary slot="title">Two marks</a-expander-summary>
396
- <a-icon slot="indicator" data-when="closed" shape="plus" aria-hidden="true" inert></a-icon>
397
- <a-icon slot="indicator" data-when="open" shape="minus" aria-hidden="true" inert></a-icon>
398
- <a-expander-details>Content</a-expander-details>
209
+ <a-expander-summary level="4">Build artifacts</a-expander-summary>
210
+ <a-expander-actions>
211
+ <a-button role="button" tabindex="0" size="small">
212
+ <a-button-label>Download</a-button-label>
213
+ </a-button>
214
+ </a-expander-actions>
215
+ <a-expander-indicator shape="chevron" aria-hidden="true" inert></a-expander-indicator>
216
+ <a-expander-details>The title and indicator toggle this section. Download is an independent action.</a-expander-details>
399
217
  </a-expander>
400
218
  ```
401
219
 
402
- ### Native HTML details
403
-
404
- For a simple disclosure, add `data-anta` to native `<details>`. It keeps the
405
- browser's opening, keyboard, and accessibility behavior while taking on the
406
- neutral tertiary Expander treatment. Add `priority="secondary"` or `"primary"`
407
- for a filled surface. `round`, `tone`, tertiary `outdent`, and `level="1"`–`"6"`
408
- use the matching Expander visual treatments. `level` changes visual type only;
409
- use a heading inside the summary when you need heading semantics.
410
-
411
- ```html
412
- <details data-anta priority="secondary" tone="info" round level="3" open>
413
- <summary>Build artifacts</summary>
414
- <div>Everything inside &lt;details&gt; is the collapsible body.</div>
415
- </details>
416
- ```
220
+ The element retains a native button and inert collapse region in shadow DOM.
221
+ It projects authored regions without changing their attributes, styles, ARIA,
222
+ or children. The default styling loads from external CSS. Import
223
+ `@antadesign/anta/elements/a-expander` for browser behavior; import the entries
224
+ for any nested Anta controls separately. The full bundle includes them.
225
+
226
+ `state="open"` or `state="closed"` controls the section. Without `state`,
227
+ `default-state` seeds it once; the omitted default is closed. Reconnection
228
+ retains applied state. `statechange` is a cancelable, non-bubbling request with
229
+ `{ next, prev }` in the `open`/`closed` vocabulary. Call `preventDefault()` to
230
+ reject an uncontrolled request. For controlled use, update `state` to accept
231
+ or leave it unchanged to reject. The element owns the native button's expanded
232
+ and disabled states and the collapsed region's inertness.
233
+
234
+ The same indicator element renders before and after registration. An omitted
235
+ indicator renders no artwork. JSX creates the built-in chevron automatically;
236
+ `indicatorPlacement` changes the region order.
237
+
238
+ One custom `a-expander-indicator` rotates with state. Two regions with
239
+ `data-when="closed"` and `data-when="open"` switch instead. Raw authors mark
240
+ all indicator artwork `aria-hidden="true"` and `inert`. The owned indicator container
241
+ also excludes its projected artwork from interaction and accessibility.
242
+
243
+ Omit unused regions. CSS hides empty actions regions. Whitespace counts as
244
+ content; JSX omits whitespace-only string actions. Mark an otherwise
245
+ unrecognized interactive title widget `data-expander-ignore` to prevent toggling.
246
+ A disabled title control behaves like title text; actions never toggle.
247
+
248
+ Named regions replace public slot attributes, and raw `level` moves from
249
+ `a-expander` to `a-expander-summary`. Raw indicator shape and placement now
250
+ belong to its region and DOM order. Existing shadow part overrides remain
251
+ supported. JSX props stay the same.
252
+
253
+ Named tones and boolean `round` need no inline styles. Custom tones and radii
254
+ use JSX-owned CSS variables. Under a CSP that blocks style attributes, supply
255
+ `--expander-tone-source` or `--expander-round` through external CSS. Raw authors
256
+ also use those variables on browsers without typed CSS `attr()`.
257
+
258
+ Native `details[data-anta]` shares this component's stylesheet and entry. Its
259
+ `summary` and `open` attribute retain native behavior; it does not use Anta's
260
+ cancelable state contract. Keep its `level` attribute on `details`.
417
261
 
418
262
  ## Styling
419
263
 
420
- Reach for the props first: **`tone`** sets the color (any CSS color for a custom
421
- tone — it derives the surface in oklch), **`priority`** the fill, **`level`** the
422
- heading. Two knobs are worth keeping: **`--expander-tone-source`** (the color a
423
- custom `tone` derives from — set it to drive the palette from your own variable) and
424
- **`--expander-gutter`** (the shared title and body inset; it also positions the
425
- start indicator). It is 24px at the start, 12px at the end, and `0` with tertiary
426
- `outdent`.
427
-
428
- ```tsx
429
- <Expander tone="#e0457b" title="Custom" />
430
- ```
431
-
432
- For everything else, `<a-expander>` exposes shadow **parts** — `::part(summary)` (the
433
- header button), `::part(indicator)` (the decorative mark), `::part(content)` (the
434
- body), and `::part(actions)` (the actions row) — and the light-DOM
435
- `<a-expander-summary>` / `<a-expander-details>` children, which you style directly.
264
+ Use `priority`, `tone`, `round`, and `outdent` for the built-in appearance.
265
+ `--expander-tone-source` connects a custom tone to an application color, and
266
+ `--expander-gutter` sets the shared title/body inset. Style authored regions
267
+ directly and the owned button, actions row, indicator, or content through
268
+ `::part()`. Unlayered rules override Anta's defaults without `!important`.
436
269
 
437
- Each example keeps its CSS in a `<style>` **inside** its own preview, targeting a demo
438
- class on that preview's expander (e.g. `.hide-chevron`) — so the rule you read is
439
- literally in the preview's DOM, next to the element it styles, never a page-wide
440
- stylesheet. The folded recipe under each preview is that same CSS; in your app, swap the
441
- demo class for your own selector.
270
+ The classes below identify demonstrations. Substitute your own selectors.
442
271
 
443
- **Hide the indicator.** A disclosure needs a visible affordance. If the title
444
- provides one, hide the indicator part and tighten the unused gutter:
272
+ Hide the indicator and reduce the title/body inset.
445
273
 
446
274
  ```css
447
- a-expander.hide-chevron::part(indicator) { display: none; }
448
- a-expander.hide-chevron { --expander-gutter: 12px; } /* tighten the inset (default 24px) */
275
+ a-expander.hide-chevron > a-expander-indicator { display: none; }
276
+ a-expander.hide-chevron { --expander-gutter: 12px; }
449
277
  ```
450
278
 
451
- **Change the indicator.** Use the built-in `triangle` or `plus` preset, or
452
- provide a custom node through `indicator` / `slot="indicator"`:
453
-
454
- ```tsx
455
- <Expander title="Triangle indicator" indicator="triangle" priority="tertiary">…</Expander>
456
- ```
457
-
458
- **Tint the whole header** on hover / press by styling the summary part (the header
459
- `<button>`):
279
+ Change header height and hover appearance while keeping its focus ring.
460
280
 
461
281
  ```css
462
- a-expander.hover-tint::part(summary):hover { background: var(--bg-3); }
463
- a-expander.hover-tint::part(summary):active { background: var(--bg-2); }
282
+ a-expander.header-tint::part(summary) { min-height: 40px; }
283
+ a-expander.header-tint::part(summary):enabled:hover { background: var(--bg-3); }
464
284
  ```
465
285
 
466
- **Highlight the title on hover.** The title inherits its `color` from `::part(summary)`,
467
- so recolor the part on hover and the title (and the `currentColor` chevron) follow — a
468
- custom title node that sets its own `color` opts out. Here we also drop the tertiary
469
- hover underline on the light-DOM `a-expander-summary`:
286
+ Respond to applied state without managing CSS classes.
470
287
 
471
288
  ```css
472
- a-expander.title-highlight::part(summary):hover { color: #e5484d; } /* title color on hover */
473
- a-expander.title-highlight a-expander-summary { text-decoration: none; } /* drop the hover underline */
289
+ a-expander.state-indicator > a-expander-indicator { color: var(--text-4); }
290
+ a-expander.state-indicator:state(open) > a-expander-indicator { color: var(--text-1); }
474
291
  ```
475
292
 
476
- **React to open / closed state** with the **`:state(open)`** custom state — your CSS can
477
- respond to it in controlled and uncontrolled mode alike, no JS. Two worked examples.
478
-
479
- *A custom code block* — pointed at the code-block surface tokens so it reads like the
480
- foldable code blocks on this site: a darker header bar over a lighter content surface, a
481
- `border-5` frame, and a tertiary-button copy that fades in once open.
293
+ ## Example
482
294
 
483
295
  ```tsx
484
- {/* An Expander pointed at the code-block surface tokens so it reads like the
485
- foldable code blocks on this site: a darker header bar over a lighter
486
- content surface, a border-5 frame, and a tertiary-button copy that fades
487
- in once open. */}
488
- <Expander
489
- title="Example.tsx"
490
- actions={<Button priority="tertiary"><Icon shape="copy" /></Button>}
491
- className="code-like"
492
- defaultOpen
493
- >
494
- <pre>{`import { Button } from '@antadesign/anta'
495
-
496
- export function SaveButton() {
497
- return <Button priority="primary">Save</Button>
498
- }`}</pre>
499
- </Expander>
500
-
501
- <style>{`
502
- a-expander.code-like {
503
- background: var(--bg-pane); /* header — a bit darker than the content */
504
- border-color: var(--border-5); /* the code-block frame outline */
505
- color: var(--text-2); /* title + code */
506
-
507
- /* With custom corner radius don't reach for overflow:hidden (it would clip
508
- the focus ring); instead give the host the radius and match it on the
509
- parts: ::part(summary) the top corners, the body the bottom corners. */
510
- border-radius: 0;
511
- }
512
- a-expander.code-like::part(summary):hover { color: var(--text-1); } /* title brightens on hover */
513
- a-expander.code-like::part(summary) { min-height: 36px; }
514
- a-expander.code-like::part(actions) { gap: 6px; } /* roomier action row */
515
- a-expander.code-like a-expander-summary {
516
- font-size: 14px; /* the title, a touch bigger */
517
- letter-spacing: 0.02em;
518
- }
519
- a-expander.code-like::part(content) { background: var(--bg-canvas); } /* content — lighter than the header */
520
- a-expander.code-like a-expander-details {
521
- padding: 12px 16px 14px; /* roomier top padding above the code */
522
- font-family: var(--monospace);
523
- font-size: 13px;
524
- font-weight: 440;
525
- line-height: 20px;
526
- }
527
- a-expander.code-like a-expander-details pre { margin: 0; font: inherit; }
528
- a-expander.code-like [slot="actions"] { opacity: 0; transition: opacity 150ms ease; } /* copy appears once open */
529
- a-expander.code-like:state(open) [slot="actions"] { opacity: 1; }
530
- `}</style>
531
- ```
296
+ import { Expander, Text } from '@antadesign/anta'
532
297
 
533
- *Style the indicator by state* with `:state(open)` and `::part(indicator)`. The
534
- example changes its color without changing the built-in glyph or motion:
535
-
536
- ```tsx
537
- <Expander title="Build log" indicatorPlacement="end" className="indicator" defaultOpen>…</Expander>
538
- <style>{`
539
- a-expander.indicator::part(indicator) { color: var(--text-4); }
540
- a-expander.indicator:state(open)::part(indicator) { color: var(--text-1); }
541
- `}</style>
298
+ function Demo() {
299
+ return (
300
+ <Expander title="What is automated testing?" defaultOpen>
301
+ <Text>Automated testing runs checks against your software and reports failures you can investigate and reproduce.</Text>
302
+ </Expander>
303
+ )
304
+ }
542
305
  ```