@acronis-platform/ui-react 2.0.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. package/dist/components/ui/accordion-container/accordion-container.js +144 -0
  2. package/dist/components/ui/accordion-container/accordion-container.js.map +1 -0
  3. package/dist/components/ui/app-shell-chat/app-shell-chat.js.map +1 -1
  4. package/dist/components/ui/area-chart/area-chart.js +141 -139
  5. package/dist/components/ui/area-chart/area-chart.js.map +1 -1
  6. package/dist/components/ui/avatar/avatar.js +33 -22
  7. package/dist/components/ui/avatar/avatar.js.map +1 -1
  8. package/dist/components/ui/bar-chart/bar-chart.js +295 -293
  9. package/dist/components/ui/bar-chart/bar-chart.js.map +1 -1
  10. package/dist/components/ui/card/card.js +150 -74
  11. package/dist/components/ui/card/card.js.map +1 -1
  12. package/dist/components/ui/card-section/card-section.js +111 -0
  13. package/dist/components/ui/card-section/card-section.js.map +1 -0
  14. package/dist/components/ui/category-bar/category-bar.js +109 -93
  15. package/dist/components/ui/category-bar/category-bar.js.map +1 -1
  16. package/dist/components/ui/chart/chart-palette.js +113 -0
  17. package/dist/components/ui/chart/chart-palette.js.map +1 -0
  18. package/dist/components/ui/chart/chart.js +176 -129
  19. package/dist/components/ui/chart/chart.js.map +1 -1
  20. package/dist/components/ui/chart-state/chart-state-illustrations.js +310 -0
  21. package/dist/components/ui/chart-state/chart-state-illustrations.js.map +1 -0
  22. package/dist/components/ui/chart-state/chart-state.js +48 -34
  23. package/dist/components/ui/chart-state/chart-state.js.map +1 -1
  24. package/dist/components/ui/chart-widget/chart-widget.js +87 -0
  25. package/dist/components/ui/chart-widget/chart-widget.js.map +1 -0
  26. package/dist/components/ui/composed-chart/composed-chart.js +117 -115
  27. package/dist/components/ui/composed-chart/composed-chart.js.map +1 -1
  28. package/dist/components/ui/confidence-cone/confidence-cone.js +166 -158
  29. package/dist/components/ui/confidence-cone/confidence-cone.js.map +1 -1
  30. package/dist/components/ui/data-table/data-table.js +4 -2
  31. package/dist/components/ui/data-table/data-table.js.map +1 -1
  32. package/dist/components/ui/empty-overlay/empty-overlay.js +49 -0
  33. package/dist/components/ui/empty-overlay/empty-overlay.js.map +1 -0
  34. package/dist/components/ui/funnel-chart/funnel-chart.js +162 -161
  35. package/dist/components/ui/funnel-chart/funnel-chart.js.map +1 -1
  36. package/dist/components/ui/histogram/histogram.js +94 -92
  37. package/dist/components/ui/histogram/histogram.js.map +1 -1
  38. package/dist/components/ui/input/input.js.map +1 -1
  39. package/dist/components/ui/label/label.js +2 -9
  40. package/dist/components/ui/label/label.js.map +1 -1
  41. package/dist/components/ui/line-chart/line-chart.js +143 -141
  42. package/dist/components/ui/line-chart/line-chart.js.map +1 -1
  43. package/dist/components/ui/metric/metric.js +74 -185
  44. package/dist/components/ui/metric/metric.js.map +1 -1
  45. package/dist/components/ui/page-content/page-content.js.map +1 -1
  46. package/dist/components/ui/pie-chart/pie-chart.js +201 -195
  47. package/dist/components/ui/pie-chart/pie-chart.js.map +1 -1
  48. package/dist/components/ui/radar-chart/radar-chart.js +133 -131
  49. package/dist/components/ui/radar-chart/radar-chart.js.map +1 -1
  50. package/dist/components/ui/radial-bar-chart/radial-bar-chart.js +297 -265
  51. package/dist/components/ui/radial-bar-chart/radial-bar-chart.js.map +1 -1
  52. package/dist/components/ui/sankey-chart/sankey-chart.js +123 -101
  53. package/dist/components/ui/sankey-chart/sankey-chart.js.map +1 -1
  54. package/dist/components/ui/scatter-chart/scatter-chart.js +76 -74
  55. package/dist/components/ui/scatter-chart/scatter-chart.js.map +1 -1
  56. package/dist/components/ui/section/section.js +157 -31
  57. package/dist/components/ui/section/section.js.map +1 -1
  58. package/dist/components/ui/stepper/stepper.js +24 -24
  59. package/dist/components/ui/stepper/stepper.js.map +1 -1
  60. package/dist/components/ui/stepper-item/stepper-item.js +43 -24
  61. package/dist/components/ui/stepper-item/stepper-item.js.map +1 -1
  62. package/dist/components/ui/table/table.js +27 -27
  63. package/dist/components/ui/table/table.js.map +1 -1
  64. package/dist/components/ui/treemap/treemap.js +133 -124
  65. package/dist/components/ui/treemap/treemap.js.map +1 -1
  66. package/dist/components/ui/trend-indicator/trend-indicator.js +34 -69
  67. package/dist/components/ui/trend-indicator/trend-indicator.js.map +1 -1
  68. package/dist/components/ui/wizard/wizard.js +59 -0
  69. package/dist/components/ui/wizard/wizard.js.map +1 -0
  70. package/dist/hooks/use-wizard.js +75 -0
  71. package/dist/hooks/use-wizard.js.map +1 -0
  72. package/dist/index.js +540 -525
  73. package/dist/index.js.map +1 -1
  74. package/dist/llms/accordion-container.md +181 -0
  75. package/dist/llms/accordion.md +124 -0
  76. package/dist/llms/alert.md +225 -0
  77. package/dist/llms/app-shell-chat.md +415 -0
  78. package/dist/llms/area-chart.md +355 -0
  79. package/dist/llms/avatar.md +208 -0
  80. package/dist/llms/bar-chart.md +406 -0
  81. package/dist/llms/breadcrumb.md +326 -0
  82. package/dist/llms/button-group.md +298 -0
  83. package/dist/llms/button-icon-input.md +142 -0
  84. package/dist/llms/button-icon-menu.md +209 -0
  85. package/dist/llms/button-icon.md +128 -0
  86. package/dist/llms/button-menu.md +172 -0
  87. package/dist/llms/button.md +185 -0
  88. package/dist/llms/calendar-panel.md +733 -0
  89. package/dist/llms/calendar.md +188 -0
  90. package/dist/llms/card-filter.md +214 -0
  91. package/dist/llms/card-section.md +293 -0
  92. package/dist/llms/card.md +447 -0
  93. package/dist/llms/carousel-dialog.md +156 -0
  94. package/dist/llms/category-bar.md +193 -0
  95. package/dist/llms/chart-state.md +190 -0
  96. package/dist/llms/chart-widget.md +209 -0
  97. package/dist/llms/chart.md +182 -0
  98. package/dist/llms/checkbox.md +257 -0
  99. package/dist/llms/chip.md +195 -0
  100. package/dist/llms/combobox.md +164 -0
  101. package/dist/llms/composed-chart.md +508 -0
  102. package/dist/llms/confidence-cone.md +277 -0
  103. package/dist/llms/data-table.md +474 -0
  104. package/dist/llms/date-range-picker.md +272 -0
  105. package/dist/llms/description-list.md +131 -0
  106. package/dist/llms/dialog-footer-carousel.md +147 -0
  107. package/dist/llms/dialog-footer-default.md +142 -0
  108. package/dist/llms/dialog-root.md +246 -0
  109. package/dist/llms/dialog-welcome.md +223 -0
  110. package/dist/llms/dialog.md +403 -0
  111. package/dist/llms/dropdown-menu.md +193 -0
  112. package/dist/llms/empty-overlay.md +146 -0
  113. package/dist/llms/empty.md +156 -0
  114. package/dist/llms/field.md +185 -0
  115. package/dist/llms/filter-cards.md +157 -0
  116. package/dist/llms/filter-chips.md +323 -0
  117. package/dist/llms/filter-search.md +512 -0
  118. package/dist/llms/form.md +154 -0
  119. package/dist/llms/funnel-chart.md +333 -0
  120. package/dist/llms/grid.md +80 -0
  121. package/dist/llms/histogram.md +190 -0
  122. package/dist/llms/input-date-picker.md +203 -0
  123. package/dist/llms/input-otp.md +173 -0
  124. package/dist/llms/input-password.md +197 -0
  125. package/dist/llms/input-search.md +170 -0
  126. package/dist/llms/input-select.md +278 -0
  127. package/dist/llms/input-text-area.md +271 -0
  128. package/dist/llms/input-text.md +192 -0
  129. package/dist/llms/line-chart.md +369 -0
  130. package/dist/llms/link.md +209 -0
  131. package/dist/llms/loading.md +130 -0
  132. package/dist/llms/meter.md +177 -0
  133. package/dist/llms/metric.md +204 -0
  134. package/dist/llms/number-field.md +122 -0
  135. package/dist/llms/page-content.md +69 -0
  136. package/dist/llms/page-header.md +182 -0
  137. package/dist/llms/pagination.md +133 -0
  138. package/dist/llms/pie-chart.md +307 -0
  139. package/dist/llms/popover.md +252 -0
  140. package/dist/llms/progress-circle.md +126 -0
  141. package/dist/llms/progress.md +111 -0
  142. package/dist/llms/radar-chart.md +316 -0
  143. package/dist/llms/radial-bar-chart.md +319 -0
  144. package/dist/llms/radio.md +255 -0
  145. package/dist/llms/resizable.md +195 -0
  146. package/dist/llms/sankey-chart.md +208 -0
  147. package/dist/llms/scatter-chart.md +239 -0
  148. package/dist/llms/scroll-area.md +183 -0
  149. package/dist/llms/section.md +401 -0
  150. package/dist/llms/select.md +210 -0
  151. package/dist/llms/separator.md +87 -0
  152. package/dist/llms/sheet.md +202 -0
  153. package/dist/llms/sidebar-primary.md +360 -0
  154. package/dist/llms/sidebar-secondary.md +417 -0
  155. package/dist/llms/skeleton.md +81 -0
  156. package/dist/llms/slider.md +97 -0
  157. package/dist/llms/spinner.md +100 -0
  158. package/dist/llms/stack.md +80 -0
  159. package/dist/llms/stepper-item.md +309 -0
  160. package/dist/llms/stepper.md +251 -0
  161. package/dist/llms/switch.md +140 -0
  162. package/dist/llms/table.md +436 -0
  163. package/dist/llms/tabs.md +197 -0
  164. package/dist/llms/tag.md +139 -0
  165. package/dist/llms/timeline.md +361 -0
  166. package/dist/llms/timer.md +259 -0
  167. package/dist/llms/toast.md +241 -0
  168. package/dist/llms/toggle-group.md +121 -0
  169. package/dist/llms/toolbar.md +453 -0
  170. package/dist/llms/tooltip.md +164 -0
  171. package/dist/llms/treemap.md +254 -0
  172. package/dist/llms/trend-indicator.md +132 -0
  173. package/dist/llms/widget-placeholder.md +169 -0
  174. package/dist/llms/wizard.md +412 -0
  175. package/dist/llms.txt +142 -0
  176. package/dist/react.js +540 -525
  177. package/dist/react.js.map +1 -1
  178. package/dist/src/components/ui/accordion-container/accordion-container.d.ts +44 -0
  179. package/dist/src/components/ui/accordion-container/index.d.ts +1 -0
  180. package/dist/src/components/ui/area-chart/area-chart.d.ts +9 -4
  181. package/dist/src/components/ui/avatar/avatar.d.ts +27 -8
  182. package/dist/src/components/ui/bar-chart/bar-chart.d.ts +9 -4
  183. package/dist/src/components/ui/button/button.d.ts +1 -1
  184. package/dist/src/components/ui/card/card.d.ts +69 -8
  185. package/dist/src/components/ui/card/index.d.ts +1 -1
  186. package/dist/src/components/ui/card-section/card-section.d.ts +50 -0
  187. package/dist/src/components/ui/card-section/index.d.ts +1 -0
  188. package/dist/src/components/ui/category-bar/category-bar.d.ts +10 -4
  189. package/dist/src/components/ui/chart/__stories__/palette-control.d.ts +48 -0
  190. package/dist/src/components/ui/chart/__tests__/chart-palette.test.d.ts +1 -0
  191. package/dist/src/components/ui/chart/chart-palette.d.ts +128 -0
  192. package/dist/src/components/ui/chart/chart.d.ts +66 -15
  193. package/dist/src/components/ui/chart/index.d.ts +4 -2
  194. package/dist/src/components/ui/chart-state/chart-state-illustrations.d.ts +6 -0
  195. package/dist/src/components/ui/chart-state/chart-state.d.ts +13 -2
  196. package/dist/src/components/ui/chart-state/index.d.ts +1 -0
  197. package/dist/src/components/ui/chart-widget/chart-widget.d.ts +64 -0
  198. package/dist/src/components/ui/chart-widget/index.d.ts +1 -0
  199. package/dist/src/components/ui/composed-chart/composed-chart.d.ts +12 -7
  200. package/dist/src/components/ui/confidence-cone/confidence-cone.d.ts +10 -5
  201. package/dist/src/components/ui/dialog/dialog.d.ts +1 -1
  202. package/dist/src/components/ui/empty-overlay/empty-overlay.d.ts +20 -0
  203. package/dist/src/components/ui/empty-overlay/index.d.ts +2 -0
  204. package/dist/src/components/ui/funnel-chart/funnel-chart.d.ts +11 -5
  205. package/dist/src/components/ui/grid/grid.d.ts +1 -1
  206. package/dist/src/components/ui/histogram/histogram.d.ts +10 -4
  207. package/dist/src/components/ui/label/index.d.ts +1 -1
  208. package/dist/src/components/ui/label/label.d.ts +1 -5
  209. package/dist/src/components/ui/line-chart/line-chart.d.ts +9 -4
  210. package/dist/src/components/ui/metric/metric.d.ts +13 -14
  211. package/dist/src/components/ui/pie-chart/pie-chart.d.ts +17 -8
  212. package/dist/src/components/ui/radar-chart/radar-chart.d.ts +10 -5
  213. package/dist/src/components/ui/radial-bar-chart/radial-bar-chart.d.ts +17 -6
  214. package/dist/src/components/ui/sankey-chart/sankey-chart.d.ts +10 -5
  215. package/dist/src/components/ui/scatter-chart/scatter-chart.d.ts +10 -5
  216. package/dist/src/components/ui/section/index.d.ts +1 -1
  217. package/dist/src/components/ui/section/section.d.ts +75 -6
  218. package/dist/src/components/ui/sheet/sheet.d.ts +1 -1
  219. package/dist/src/components/ui/stack/stack.d.ts +1 -1
  220. package/dist/src/components/ui/stepper-item/stepper-item.d.ts +6 -5
  221. package/dist/src/components/ui/tag/tag.d.ts +1 -1
  222. package/dist/src/components/ui/treemap/treemap.d.ts +11 -5
  223. package/dist/src/components/ui/trend-indicator/trend-indicator.d.ts +3 -7
  224. package/dist/src/components/ui/wizard/index.d.ts +1 -0
  225. package/dist/src/components/ui/wizard/wizard.d.ts +6 -0
  226. package/dist/src/hooks/index.d.ts +1 -0
  227. package/dist/src/hooks/use-wizard.d.ts +80 -0
  228. package/dist/src/index.d.ts +5 -3
  229. package/dist/ui-react.css +1 -1
  230. package/package.json +12 -6
  231. package/dist/components/ui/app-shell/app-shell.js +0 -75
  232. package/dist/components/ui/app-shell/app-shell.js.map +0 -1
  233. package/dist/components/ui/auth-layout/auth-layout.js +0 -56
  234. package/dist/components/ui/auth-layout/auth-layout.js.map +0 -1
  235. package/dist/components/ui/collapsible/collapsible.js +0 -22
  236. package/dist/components/ui/collapsible/collapsible.js.map +0 -1
  237. package/dist/src/components/ui/app-shell/app-shell.d.ts +0 -8
  238. package/dist/src/components/ui/app-shell/index.d.ts +0 -1
  239. package/dist/src/components/ui/auth-layout/auth-layout.d.ts +0 -6
  240. package/dist/src/components/ui/auth-layout/index.d.ts +0 -1
  241. package/dist/src/components/ui/collapsible/collapsible.d.ts +0 -5
  242. package/dist/src/components/ui/collapsible/index.d.ts +0 -1
  243. package/dist/src/components/ui/label/label.docs.d.ts +0 -10
@@ -0,0 +1,251 @@
1
+ # Stepper
2
+
3
+ > The root of a step sequence. It renders the sequence two ways and lets CSS pick one: on wide viewports a start-aligned, wrapping row of `StepperItem` children; on narrow ones a two-line text summary — "Step 3 of 5: <current step>" and "Next: <next step>" — with no items at all. The switch is a real viewport media query (Tailwind's `lg:`, `min-width: 1024px`), not a JS measurement, so both subtrees are always in the DOM and only one is ever displayed. It owns that layout switch and the summary copy only: the steps, their order, which one is current, and all navigation belong to the application.
4
+
5
+ **Status:** ready · **Category:** navigation · **Since:** v0.63.0
6
+
7
+ ## Import
8
+
9
+ ```tsx
10
+ import { Stepper } from '@acronis-platform/ui-react'
11
+ ```
12
+
13
+ ## Quick example
14
+
15
+ ```tsx
16
+ <Stepper
17
+ currentStep={2}
18
+ totalSteps={3}
19
+ current="Choose a plan"
20
+ next="Confirm and pay"
21
+ >
22
+ <StepperItem variant="completed" label="Create an account" avatar={…} />
23
+ <StepperItem variant="current" label="Choose a plan" avatar={…} />
24
+ <StepperItem variant="future" label="Confirm and pay" avatar={…} />
25
+ </Stepper>
26
+ ```
27
+
28
+ The root of a step sequence. It renders the sequence **two ways** and lets CSS
29
+ pick one:
30
+
31
+ - **At 1024px and above** — the `StepperItem` children you pass, in a
32
+ start-aligned row that wraps.
33
+ - **Below 1024px** — a two-line text summary instead: "Step 3 of 5: Choose a
34
+ plan" and "Next: Confirm and pay". No step items at all.
35
+
36
+ Both subtrees are always in the DOM; a real viewport media query (Tailwind's
37
+ `lg:`) displays one and hides the other. So you supply **both** the children and
38
+ the summary props — the component cannot derive one from the other.
39
+
40
+ ## When to use
41
+
42
+ - Any wizard / checkout / onboarding flow that has to stay usable on a narrow
43
+ viewport, where a row of steps would not fit.
44
+ - Whenever you would otherwise lay `StepperItem`s out by hand: this is that row,
45
+ plus the compact fallback.
46
+
47
+ ## When not to use
48
+
49
+ - For a single step in isolation — that is `StepperItem`.
50
+ - To show progress as one quantity — use `Progress` or `ProgressCircle`.
51
+ - For a chronological log of things that already happened — use `Timeline`.
52
+
53
+ ## Usage
54
+
55
+ ```tsx
56
+ <Stepper
57
+ currentStep={2}
58
+ totalSteps={3}
59
+ current="Choose a plan"
60
+ next="Confirm and pay"
61
+ >
62
+ <StepperItem
63
+ variant="completed"
64
+ label="Create an account"
65
+ avatar={
66
+ <Avatar color="green" className="[box-shadow:none]">
67
+ <CheckIcon size={16} />
68
+ </Avatar>
69
+ }
70
+ />
71
+ <StepperItem
72
+ variant="current"
73
+ label="Choose a plan"
74
+ avatar={
75
+ <Avatar color="blue" className="[box-shadow:none]">
76
+ <AvatarFallback>2</AvatarFallback>
77
+ </Avatar>
78
+ }
79
+ />
80
+ <StepperItem
81
+ variant="future"
82
+ label="Confirm and pay"
83
+ avatar={
84
+ <Avatar color="gray" className="[box-shadow:none]">
85
+ <AvatarFallback>3</AvatarFallback>
86
+ </Avatar>
87
+ }
88
+ />
89
+ </Stepper>
90
+ ```
91
+
92
+ `Avatar`'s 2px outset ring (meant to separate overlapping avatars in an
93
+ `AvatarGroup`) is switched off with `className="[box-shadow:none]"` on each
94
+ composed avatar — Figma's Stepper avatars carry no stroke, and left on, the
95
+ ring shows as an unwanted halo on a filled step container. See `StepperItem`'s
96
+ own spec `README.md` for the full note.
97
+
98
+ On the **last** step, omit `next` — the whole "Next: …" line is then left out
99
+ rather than rendered empty.
100
+
101
+ Every string the component generates itself is a prop so it can be translated —
102
+ `stepLabel` ("Step"), `ofLabel` ("of"), `nextLabel` ("Next:"), and
103
+ `separatorLabel` (": ", the punctuation before the step's name). Everything else
104
+ it renders is yours.
105
+
106
+ ## Parts
107
+
108
+ | Part | Element | Notes |
109
+ | -------------- | ------- | ------------------------------------------------------ |
110
+ | root | `div` | Full width; holds both layouts |
111
+ | `summary` | `div` | The compact layout, displayed below 1024px |
112
+ | `current-line` | `p` | "Step N of M: {current}", two-tone |
113
+ | `next-line` | `p` | "Next: {next}"; absent when `next` is omitted |
114
+ | `items-row` | `div` | The `StepperItem` children, displayed at 1024px and up |
115
+
116
+ ## Breakpoint boundary
117
+
118
+ Figma names the two variants `0-1024` and `>1025`, which leaves 1024px itself
119
+ ambiguous. The implementation uses the kit's pinned `lg` breakpoint — exactly
120
+ 1024px (64rem) — so **1024px renders the wide row**, one pixel lower than the
121
+ Figma label implies. That is a deliberate choice to stay on the shared
122
+ breakpoint scale rather than mint a bespoke `1025px` query for one component;
123
+ the repo documents equivalent roundings elsewhere (see
124
+ `src/stories/breakpoints-demo.stories.tsx`).
125
+
126
+ ## Design status
127
+
128
+ As of the 2026-08-24 Figma sync, this component consumes a dedicated
129
+ `--ui-stepper-breakpoint-*` token tier (see `tokens.yaml`) — the summary's line
130
+ gap, the item row's gap, and both summary text colors are Stepper-owned
131
+ tokens rather than borrowed from the semantic scale, the same treatment
132
+ `StepperItem` already carries.
133
+
134
+ ## Props
135
+
136
+ | Prop | Type | Default | Required | Description |
137
+ |------|------|---------|----------|-------------|
138
+ | `currentStep` | `ReactNode` | — | yes | 1-based index of the step the user is on, e.g. `3`. Appears in the compact summary only; above the breakpoint the highlighted `StepperItem` carries the same information. |
139
+ | `totalSteps` | `ReactNode` | — | yes | How many steps the sequence has, e.g. `5`. Compact summary only. |
140
+ | `current` | `ReactNode` | — | yes | The current step's name. Compact summary only. |
141
+ | `next` | `ReactNode` | — | no | The next step's name. Omit it — on the last step — and the whole "Next: …" line is left out rather than rendered empty. Compact summary only. |
142
+ | `stepLabel` | `string` | `Step` | no | The word introducing the step counter. A prop rather than an inlined literal so the component ships no untranslatable copy; the default is English, and a localized application overrides it. |
143
+ | `ofLabel` | `string` | `of` | no | The word joining the current step number to the total. Translatable — see `stepLabel`. |
144
+ | `nextLabel` | `string` | `Next:` | no | The label introducing the next step's name. Translatable — see `stepLabel`. |
145
+ | `separatorLabel` | `string` | `: ` | no | The punctuation joining the step counter to the current step's name. Translatable — see `stepLabel`; not every locale writes it as `": "` (French spaces before the colon, CJK uses a full-width one). |
146
+
147
+ ## Content slots
148
+
149
+ | Slot | Description |
150
+ |------|-------------|
151
+ | `default` | The `StepperItem` elements making up the sequence, laid out in the wide row. They are not rendered in the compact summary, which replaces the row with text. |
152
+
153
+ ## Behavior
154
+
155
+ ### Two layouts, chosen by the viewport
156
+
157
+ - **Given** a viewport **at or above 1024px**
158
+ **Then** the `StepperItem` children render as a start-aligned row that wraps, and
159
+ the compact summary is `display: none`.
160
+ - **Given** a viewport **below 1024px**
161
+ **Then** the two-line text summary renders, and the item row is
162
+ `display: none` — no step items are shown at all.
163
+ - **Given** any viewport
164
+ **Then** _both_ subtrees are in the DOM. The switch is a CSS media query
165
+ (Tailwind's `lg:`, i.e. `@media (min-width: 1024px)`), not a JS measurement,
166
+ so nothing is mounted or unmounted as the window resizes and there is no
167
+ hydration mismatch, no `ResizeObserver`, and no measuring pass.
168
+ - **Given** a viewport of exactly **1024px**
169
+ **Then** the wide row renders. Figma names the variants `0-1024` and `>1025`,
170
+ which makes 1024 itself ambiguous; the implementation follows the kit's pinned
171
+ `lg` breakpoint (1024px = 64rem) rather than inventing a one-off `1025px`
172
+ query, so the boundary sits one pixel lower than the Figma label suggests.
173
+ - **Given** a `dir="rtl"` document
174
+ **Then** both layouts mirror without extra work: neither uses a physical
175
+ directional utility.
176
+
177
+ ### The compact summary
178
+
179
+ - **Given** `currentStep`, `totalSteps`, and `current`
180
+ **Then** the first line reads "Step {currentStep} of {totalSteps}: {current}",
181
+ with the generated prefix in `--ui-stepper-breakpoint-default-label-color` and
182
+ the step's name in `--ui-stepper-breakpoint-default-value-color`.
183
+ - **Given** a `next` step name
184
+ **Then** a second line reads "Next: {next}", with the same two-tone treatment.
185
+ - **Given** no `next`
186
+ **Then** the second line is **not rendered at all** — no empty paragraph, no
187
+ dangling "Next:". This is how the last step in a sequence renders.
188
+ - **Given** `stepLabel`, `ofLabel`, `nextLabel`, or `separatorLabel`
189
+ **Then** the corresponding generated string is replaced. The component ships
190
+ English defaults and no other copy of its own, so a localized application
191
+ overrides exactly these four. `separatorLabel` (default `": "`) is the
192
+ punctuation joining the counter to the step name — a prop rather than an inlined
193
+ literal because not every locale writes it that way.
194
+
195
+ ### The wide row
196
+
197
+ - **Given** `children`
198
+ **Then** they are laid out in a flex row, start-aligned, wrapping onto further
199
+ lines when the sequence is too wide. Each wrapped line packs to the start
200
+ (left in LTR) rather than centering independently, so a lone trailing item on
201
+ the last line stays visually connected to the row above it instead of
202
+ floating under the container's midpoint.
203
+ - **Given** more steps than fit
204
+ **Then** the row wraps rather than scrolling or truncating; below the
205
+ breakpoint the summary takes over instead.
206
+
207
+ ### Not owned here
208
+
209
+ - The steps themselves — their number, order, labels, markers, and which one is
210
+ `current` are the application's, passed as `StepperItem` children.
211
+ - Navigation: the component has no events. A completed step becomes clickable by
212
+ composing that `StepperItem` with its own `render` prop.
213
+ - Keeping `currentStep` / `current` / `next` consistent with the children. The
214
+ compact summary and the wide row are two renderings of the same state, but the
215
+ component cannot inspect its children to derive one from the other — the
216
+ caller supplies both.
217
+
218
+ ## Accessibility
219
+
220
+ - **Only one layout is ever announced.** Both subtrees are in the DOM at all
221
+ times, but the inactive one is `display: none`, which removes it from the
222
+ accessibility tree. A screen-reader user below 1024px hears the text summary;
223
+ above it, the step items. Nothing is duplicated. Do not "fix" this with
224
+ `aria-hidden` — which layout is live is a CSS decision the component cannot
225
+ observe, so any JS-set flag would be wrong half the time.
226
+ - **The compact summary is the accessible progress statement.** "Step 3 of 5:
227
+ Choose a plan" is plain text in a `<p>`, so it is read verbatim, in order,
228
+ with no ARIA required. That is deliberately the same sentence a sighted user
229
+ reads.
230
+ - **The wide row is not itself a list.** The root is a plain `<div>` with no
231
+ `role`. If the sequence should announce as an ordered list, or the current
232
+ step as `aria-current="step"`, the application composes that on the
233
+ `StepperItem` children — which are polymorphic via their own `render` prop.
234
+ - **No tab stops of its own.** The root and both layouts are non-interactive.
235
+ Focusability comes only from whatever a `StepperItem` child is rendered as.
236
+ - **Ships four English strings** — `stepLabel`, `ofLabel`, `nextLabel`,
237
+ `separatorLabel` — and nothing else. They are props, so a localized application replaces them; the
238
+ step names themselves are always consumer-supplied.
239
+ - **RTL**: the summary is a plain block flow and the row uses logical flex
240
+ layout with a gap, so both mirror under `dir="rtl"` with no extra work and no
241
+ physical directional utility to correct.
242
+
243
+ ## Contrast
244
+
245
+ The generated prefix uses `--ui-stepper-breakpoint-default-label-color` and the
246
+ step names use `--ui-stepper-breakpoint-default-value-color` (see
247
+ `tokens.yaml`) — both Stepper-owned tokens as of the 2026-08-24 tier sync.
248
+ Contrast has been verified against the tier's resolved values in light and
249
+ dark; the label-color/prefix pairing is the one to re-check first if a brand
250
+ later defines a distinct value for it, as it is the lowest-contrast text the
251
+ component renders.
@@ -0,0 +1,140 @@
1
+ # Switch
2
+
3
+ > A binary on/off toggle. Wraps the Base UI Switch primitive; the change takes effect immediately (no separate submit).
4
+
5
+ **Status:** ready · **Category:** forms · **Since:** v0.4.0
6
+
7
+ ## Import
8
+
9
+ ```tsx
10
+ import { Switch } from '@acronis-platform/ui-react'
11
+ ```
12
+
13
+ ## Quick example
14
+
15
+ ```tsx
16
+ <Switch
17
+ aria-label="Wireless"
18
+ checked={enabled}
19
+ onCheckedChange={setEnabled}
20
+ />
21
+ ```
22
+
23
+ A binary on/off toggle whose change takes effect immediately.
24
+
25
+ ## When to use
26
+
27
+ - Toggling a single setting that applies instantly (e.g. "Enable notifications").
28
+
29
+ ## When not to use
30
+
31
+ - A choice that only applies after submitting a form — use a checkbox.
32
+ - Selecting one of several options — use radios or a segmented control.
33
+
34
+ ## Example (React — implemented)
35
+
36
+ ```tsx
37
+ import { Switch } from '@acronis-platform/ui-react';
38
+
39
+ const [enabled, setEnabled] = useState(false);
40
+
41
+ {
42
+ /* Bare toggle — name it with aria-label */
43
+ }
44
+ <Switch aria-label="Wireless" checked={enabled} onCheckedChange={setEnabled} />;
45
+
46
+ {
47
+ /* With a label — the whole row toggles */
48
+ }
49
+ <Switch
50
+ label="Enable notifications"
51
+ checked={enabled}
52
+ onCheckedChange={setEnabled}
53
+ />;
54
+ ```
55
+
56
+ Pass `label` to compose the full field (the row becomes clickable and the toggle
57
+ is named via aria-labelledby); omit it for a bare toggle named with `aria-label`.
58
+ Vue and Web Component implementations are planned against the same contract.
59
+
60
+ ## Parts
61
+
62
+ | Part | Element | Notes |
63
+ | ----------- | -------- | ------------------------------------------------------- |
64
+ | `root` | `button` | The track (role `switch`); fill per off/on state |
65
+ | `thumb` | `span` | The sliding knob |
66
+ | `container` | `label` | Wraps toggle + label, forwards clicks (only with label) |
67
+ | `label` | `span` | Label text, names the control |
68
+
69
+ ## Props
70
+
71
+ | Prop | Type | Default | Required | Description |
72
+ |------|------|---------|----------|-------------|
73
+ | `checked` | `boolean` | — | no | Controlled checked state. Pair with the change event. |
74
+ | `default-checked` | `boolean` | `false` | no | Initial checked state when uncontrolled. |
75
+ | `disabled` | `boolean` | `false` | no | Disables interaction. |
76
+ | `required` | `boolean` | `false` | no | Marks the control as required in a form. |
77
+ | `name` | `string` | — | no | Form field name submitted with the form. |
78
+ | `value` | `string` | — | no | Form field value when checked. |
79
+ | `label` | `ReactNode` | — | no | Optional label rendered beside the toggle. When provided, the whole row becomes a clickable `<label>` and the toggle is named via aria-labelledby. |
80
+
81
+ ## Events
82
+
83
+ | Event | Payload | Description |
84
+ |-------|---------|-------------|
85
+ | `checked-change` | `boolean` | Fired when the checked state changes (React: `onCheckedChange`). Payload is the new checked value. |
86
+
87
+ ## Behavior
88
+
89
+ ### Toggling
90
+
91
+ #### Toggles on activation
92
+
93
+ **Given** an enabled Switch
94
+ **When** the user clicks it or presses Space/Enter while focused
95
+ **Then** the checked state flips
96
+ **And** a `checked-change` event fires with the new boolean value
97
+ **And** the thumb slides to the new position.
98
+
99
+ ### Controlled vs uncontrolled
100
+
101
+ #### Uncontrolled
102
+
103
+ **Given** a Switch with `default-checked` and no `checked`
104
+ **When** it renders
105
+ **Then** it manages its own state, starting from `default-checked`.
106
+
107
+ #### Controlled
108
+
109
+ **Given** a Switch with `checked`
110
+ **When** the user toggles it
111
+ **Then** it emits `checked-change` but does not change visually until the owner
112
+ updates `checked`.
113
+
114
+ ### Disabled
115
+
116
+ **Given** a Switch with `disabled`
117
+ **When** the user attempts to toggle it
118
+ **Then** the state does not change and no event fires.
119
+
120
+ ### Known gap
121
+
122
+ The current React implementation renders correctly but is **not yet wired to the
123
+ `--ui-switch-*` tokens** (see `tokens.yaml`). It uses the shared semantic bridge
124
+ and `opacity-50` for disabled. This spec is the target contract; aligning the
125
+ implementation is tracked follow-up.
126
+
127
+ ## Accessibility
128
+
129
+ - **Role:** `switch` (Base UI Switch.Root), with `aria-checked` reflecting the
130
+ on/off state.
131
+ - **Accessible name:** associate a visible label (e.g. wrap with a `<label>` or
132
+ use `aria-labelledby`); when there is no visible label, provide `aria-label`.
133
+ - **Keyboard:** Space (and Enter) toggle the switch while focused; Tab moves
134
+ focus. A `disabled` switch is not focusable.
135
+ - **Focus visible:** 2px `--ui-focus-brand` ring with 2px offset via
136
+ `:focus-visible`.
137
+ - **State, not color alone:** the on/off state is exposed via `aria-checked` and
138
+ the thumb position, not color alone.
139
+ - **WCAG:** 2.1.1 (keyboard), 2.4.7 (focus visible), 4.1.2 (name/role/value),
140
+ 1.4.11 (non-text contrast).