@misoto22/design 0.3.1 → 0.6.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 (266) hide show
  1. package/README.md +124 -0
  2. package/bin/misoto22-design.mjs +205 -0
  3. package/dist/agent/Accordion.md +41 -0
  4. package/dist/agent/Alert.md +42 -0
  5. package/dist/agent/AppShell.md +40 -0
  6. package/dist/agent/AreaChart.md +68 -0
  7. package/dist/agent/Article.md +41 -0
  8. package/dist/agent/Avatar.md +32 -0
  9. package/dist/agent/Badge.md +40 -0
  10. package/dist/agent/BarChart.md +65 -0
  11. package/dist/agent/BarList.md +40 -0
  12. package/dist/agent/BigNumber.md +45 -0
  13. package/dist/agent/BoxPlot.md +58 -0
  14. package/dist/agent/Breadcrumb.md +32 -0
  15. package/dist/agent/BulletChart.md +48 -0
  16. package/dist/agent/Button.md +56 -0
  17. package/dist/agent/Calendar.md +42 -0
  18. package/dist/agent/Card.md +49 -0
  19. package/dist/agent/Checkbox.md +35 -0
  20. package/dist/agent/Collapsible.md +46 -0
  21. package/dist/agent/Combobox.md +50 -0
  22. package/dist/agent/Command.md +93 -0
  23. package/dist/agent/ComposedChart.md +57 -0
  24. package/dist/agent/ContextMenu.md +59 -0
  25. package/dist/agent/DatePicker.md +75 -0
  26. package/dist/agent/Diagram.md +46 -0
  27. package/dist/agent/Dialog.md +52 -0
  28. package/dist/agent/DropdownMenu.md +70 -0
  29. package/dist/agent/EmptyState.md +37 -0
  30. package/dist/agent/ErrorState.md +41 -0
  31. package/dist/agent/Facet.md +82 -0
  32. package/dist/agent/Field.md +37 -0
  33. package/dist/agent/FigureBand.md +37 -0
  34. package/dist/agent/FloatingIconButton.md +38 -0
  35. package/dist/agent/FunnelChart.md +52 -0
  36. package/dist/agent/Heatmap.md +53 -0
  37. package/dist/agent/Histogram.md +63 -0
  38. package/dist/agent/Input.md +30 -0
  39. package/dist/agent/Kbd.md +29 -0
  40. package/dist/agent/LineChart.md +60 -0
  41. package/dist/agent/LinkArrow.md +29 -0
  42. package/dist/agent/NativeSelect.md +40 -0
  43. package/dist/agent/NavItem.md +35 -0
  44. package/dist/agent/Pagination.md +43 -0
  45. package/dist/agent/PieChart.md +55 -0
  46. package/dist/agent/Popover.md +57 -0
  47. package/dist/agent/Progress.md +33 -0
  48. package/dist/agent/RadarChart.md +54 -0
  49. package/dist/agent/RadialChart.md +57 -0
  50. package/dist/agent/RadioGroup.md +44 -0
  51. package/dist/agent/SankeyChart.md +61 -0
  52. package/dist/agent/ScatterChart.md +54 -0
  53. package/dist/agent/ScrollArea.md +40 -0
  54. package/dist/agent/SearchableMenu.md +46 -0
  55. package/dist/agent/Select.md +75 -0
  56. package/dist/agent/Separator.md +35 -0
  57. package/dist/agent/Sheet.md +57 -0
  58. package/dist/agent/Skeleton.md +73 -0
  59. package/dist/agent/Slider.md +39 -0
  60. package/dist/agent/Sparkline.md +45 -0
  61. package/dist/agent/Spinner.md +39 -0
  62. package/dist/agent/StatusDot.md +36 -0
  63. package/dist/agent/StatusPill.md +28 -0
  64. package/dist/agent/Steps.md +44 -0
  65. package/dist/agent/Switch.md +29 -0
  66. package/dist/agent/Table.md +85 -0
  67. package/dist/agent/Tabs.md +48 -0
  68. package/dist/agent/Tag.md +35 -0
  69. package/dist/agent/Textarea.md +26 -0
  70. package/dist/agent/Toast.md +26 -0
  71. package/dist/agent/ToggleGroup.md +44 -0
  72. package/dist/agent/Tooltip.md +51 -0
  73. package/dist/agent/TreemapChart.md +52 -0
  74. package/dist/agent/WaterfallChart.md +62 -0
  75. package/dist/agent/catalog.json +1791 -0
  76. package/dist/agent/index.md +131 -0
  77. package/dist/article.css +393 -0
  78. package/dist/charts/AreaChart/AreaChart.d.ts +191 -0
  79. package/dist/charts/AreaChart/AreaChart.js +480 -0
  80. package/dist/charts/AreaChart/AreaChart.js.map +1 -0
  81. package/dist/charts/BarChart/BarChart.d.ts +179 -0
  82. package/dist/charts/BarChart/BarChart.js +591 -0
  83. package/dist/charts/BarChart/BarChart.js.map +1 -0
  84. package/dist/charts/BarList/BarList.d.ts +57 -0
  85. package/dist/charts/BarList/BarList.js +71 -0
  86. package/dist/charts/BarList/BarList.js.map +1 -0
  87. package/dist/charts/BigNumber/BigNumber.d.ts +56 -0
  88. package/dist/charts/BigNumber/BigNumber.js +31 -0
  89. package/dist/charts/BigNumber/BigNumber.js.map +1 -0
  90. package/dist/charts/BoxPlot/BoxPlot.d.ts +177 -0
  91. package/dist/charts/BoxPlot/BoxPlot.js +495 -0
  92. package/dist/charts/BoxPlot/BoxPlot.js.map +1 -0
  93. package/dist/charts/BulletChart/BulletChart.d.ts +100 -0
  94. package/dist/charts/BulletChart/BulletChart.js +162 -0
  95. package/dist/charts/BulletChart/BulletChart.js.map +1 -0
  96. package/dist/charts/ComposedChart/ComposedChart.d.ts +171 -0
  97. package/dist/charts/ComposedChart/ComposedChart.js +667 -0
  98. package/dist/charts/ComposedChart/ComposedChart.js.map +1 -0
  99. package/dist/charts/Facet/Facet.d.ts +240 -0
  100. package/dist/charts/Facet/Facet.js +133 -0
  101. package/dist/charts/Facet/Facet.js.map +1 -0
  102. package/dist/charts/Facet/panels.d.ts +187 -0
  103. package/dist/charts/Facet/panels.js +203 -0
  104. package/dist/charts/Facet/panels.js.map +1 -0
  105. package/dist/charts/FunnelChart/FunnelChart.d.ts +101 -0
  106. package/dist/charts/FunnelChart/FunnelChart.js +147 -0
  107. package/dist/charts/FunnelChart/FunnelChart.js.map +1 -0
  108. package/dist/charts/Heatmap/Heatmap.d.ts +78 -0
  109. package/dist/charts/Heatmap/Heatmap.js +108 -0
  110. package/dist/charts/Heatmap/Heatmap.js.map +1 -0
  111. package/dist/charts/Histogram/Histogram.d.ts +165 -0
  112. package/dist/charts/Histogram/Histogram.js +350 -0
  113. package/dist/charts/Histogram/Histogram.js.map +1 -0
  114. package/dist/charts/LineChart/LineChart.d.ts +167 -0
  115. package/dist/charts/LineChart/LineChart.js +486 -0
  116. package/dist/charts/LineChart/LineChart.js.map +1 -0
  117. package/dist/charts/PieChart/PieChart.d.ts +128 -0
  118. package/dist/charts/PieChart/PieChart.js +309 -0
  119. package/dist/charts/PieChart/PieChart.js.map +1 -0
  120. package/dist/charts/RadarChart/RadarChart.d.ts +123 -0
  121. package/dist/charts/RadarChart/RadarChart.js +299 -0
  122. package/dist/charts/RadarChart/RadarChart.js.map +1 -0
  123. package/dist/charts/RadialChart/RadialChart.d.ts +124 -0
  124. package/dist/charts/RadialChart/RadialChart.js +257 -0
  125. package/dist/charts/RadialChart/RadialChart.js.map +1 -0
  126. package/dist/charts/SankeyChart/SankeyChart.d.ts +137 -0
  127. package/dist/charts/SankeyChart/SankeyChart.js +501 -0
  128. package/dist/charts/SankeyChart/SankeyChart.js.map +1 -0
  129. package/dist/charts/ScatterChart/ScatterChart.d.ts +128 -0
  130. package/dist/charts/ScatterChart/ScatterChart.js +269 -0
  131. package/dist/charts/ScatterChart/ScatterChart.js.map +1 -0
  132. package/dist/charts/Sparkline/Sparkline.d.ts +53 -0
  133. package/dist/charts/Sparkline/Sparkline.js +93 -0
  134. package/dist/charts/Sparkline/Sparkline.js.map +1 -0
  135. package/dist/charts/TreemapChart/TreemapChart.d.ts +93 -0
  136. package/dist/charts/TreemapChart/TreemapChart.js +118 -0
  137. package/dist/charts/TreemapChart/TreemapChart.js.map +1 -0
  138. package/dist/charts/WaterfallChart/WaterfallChart.d.ts +164 -0
  139. package/dist/charts/WaterfallChart/WaterfallChart.js +319 -0
  140. package/dist/charts/WaterfallChart/WaterfallChart.js.map +1 -0
  141. package/dist/charts/index.d.ts +41 -0
  142. package/dist/charts/index.js +128 -0
  143. package/dist/charts/index.js.map +1 -0
  144. package/dist/charts/lib/annotations.d.ts +103 -0
  145. package/dist/charts/lib/annotations.js +99 -0
  146. package/dist/charts/lib/annotations.js.map +1 -0
  147. package/dist/charts/lib/axis.d.ts +18 -0
  148. package/dist/charts/lib/axis.js +21 -0
  149. package/dist/charts/lib/axis.js.map +1 -0
  150. package/dist/charts/lib/background.d.ts +33 -0
  151. package/dist/charts/lib/background.js +173 -0
  152. package/dist/charts/lib/background.js.map +1 -0
  153. package/dist/charts/lib/brush.d.ts +94 -0
  154. package/dist/charts/lib/brush.js +535 -0
  155. package/dist/charts/lib/brush.js.map +1 -0
  156. package/dist/charts/lib/chart.d.ts +94 -0
  157. package/dist/charts/lib/chart.js +134 -0
  158. package/dist/charts/lib/chart.js.map +1 -0
  159. package/dist/charts/lib/dot.d.ts +29 -0
  160. package/dist/charts/lib/dot.js +78 -0
  161. package/dist/charts/lib/dot.js.map +1 -0
  162. package/dist/charts/lib/empty.d.ts +31 -0
  163. package/dist/charts/lib/empty.js +13 -0
  164. package/dist/charts/lib/empty.js.map +1 -0
  165. package/dist/charts/lib/export.d.ts +103 -0
  166. package/dist/charts/lib/export.js +204 -0
  167. package/dist/charts/lib/export.js.map +1 -0
  168. package/dist/charts/lib/figure.d.ts +61 -0
  169. package/dist/charts/lib/figure.js +59 -0
  170. package/dist/charts/lib/figure.js.map +1 -0
  171. package/dist/charts/lib/format.d.ts +46 -0
  172. package/dist/charts/lib/format.js +56 -0
  173. package/dist/charts/lib/format.js.map +1 -0
  174. package/dist/charts/lib/legend.d.ts +45 -0
  175. package/dist/charts/lib/legend.js +117 -0
  176. package/dist/charts/lib/legend.js.map +1 -0
  177. package/dist/charts/lib/loading.d.ts +48 -0
  178. package/dist/charts/lib/loading.js +105 -0
  179. package/dist/charts/lib/loading.js.map +1 -0
  180. package/dist/charts/lib/paint.d.ts +154 -0
  181. package/dist/charts/lib/paint.js +551 -0
  182. package/dist/charts/lib/paint.js.map +1 -0
  183. package/dist/charts/lib/sonify-control.d.ts +163 -0
  184. package/dist/charts/lib/sonify-control.js +172 -0
  185. package/dist/charts/lib/sonify-control.js.map +1 -0
  186. package/dist/charts/lib/sonify.d.ts +248 -0
  187. package/dist/charts/lib/sonify.js +262 -0
  188. package/dist/charts/lib/sonify.js.map +1 -0
  189. package/dist/charts/lib/toolbar.d.ts +143 -0
  190. package/dist/charts/lib/toolbar.js +156 -0
  191. package/dist/charts/lib/toolbar.js.map +1 -0
  192. package/dist/charts/lib/tooltip.d.ts +59 -0
  193. package/dist/charts/lib/tooltip.js +135 -0
  194. package/dist/charts/lib/tooltip.js.map +1 -0
  195. package/dist/charts/lib/values.d.ts +56 -0
  196. package/dist/charts/lib/values.js +71 -0
  197. package/dist/charts/lib/values.js.map +1 -0
  198. package/dist/charts/lib/zoom.d.ts +139 -0
  199. package/dist/charts/lib/zoom.js +314 -0
  200. package/dist/charts/lib/zoom.js.map +1 -0
  201. package/dist/components/AppShell/AppShell.d.ts +15 -1
  202. package/dist/components/AppShell/AppShell.js +8 -4
  203. package/dist/components/AppShell/AppShell.js.map +1 -1
  204. package/dist/components/Article/Article.d.ts +51 -0
  205. package/dist/components/Article/Article.js +21 -0
  206. package/dist/components/Article/Article.js.map +1 -0
  207. package/dist/components/Button/Button.js +2 -2
  208. package/dist/components/Button/Button.js.map +1 -1
  209. package/dist/components/Calendar/Calendar.d.ts +1 -1
  210. package/dist/components/Calendar/Calendar.js +259 -24
  211. package/dist/components/Calendar/Calendar.js.map +1 -1
  212. package/dist/components/Checkbox/Checkbox.js +1 -1
  213. package/dist/components/Checkbox/Checkbox.js.map +1 -1
  214. package/dist/components/Collapsible/Collapsible.d.ts +15 -1
  215. package/dist/components/Collapsible/Collapsible.js +18 -0
  216. package/dist/components/Collapsible/Collapsible.js.map +1 -1
  217. package/dist/components/Combobox/Combobox.js +1 -1
  218. package/dist/components/Combobox/Combobox.js.map +1 -1
  219. package/dist/components/Command/Command.js +7 -7
  220. package/dist/components/Command/Command.js.map +1 -1
  221. package/dist/components/ContextMenu/ContextMenu.js +2 -2
  222. package/dist/components/ContextMenu/ContextMenu.js.map +1 -1
  223. package/dist/components/Diagram/Diagram.d.ts +81 -0
  224. package/dist/components/Diagram/Diagram.js +139 -0
  225. package/dist/components/Diagram/Diagram.js.map +1 -0
  226. package/dist/components/Dialog/Dialog.js +1 -1
  227. package/dist/components/Dialog/Dialog.js.map +1 -1
  228. package/dist/components/DropdownMenu/DropdownMenu.js +2 -2
  229. package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -1
  230. package/dist/components/FigureBand/FigureBand.js +41 -30
  231. package/dist/components/FigureBand/FigureBand.js.map +1 -1
  232. package/dist/components/Popover/Popover.js +1 -1
  233. package/dist/components/Popover/Popover.js.map +1 -1
  234. package/dist/components/Select/Select.js +2 -2
  235. package/dist/components/Select/Select.js.map +1 -1
  236. package/dist/components/Sheet/Sheet.js +1 -1
  237. package/dist/components/Sheet/Sheet.js.map +1 -1
  238. package/dist/components/Slider/Slider.js +1 -1
  239. package/dist/components/Slider/Slider.js.map +1 -1
  240. package/dist/components/Steps/Steps.d.ts +65 -0
  241. package/dist/components/Steps/Steps.js +55 -0
  242. package/dist/components/Steps/Steps.js.map +1 -0
  243. package/dist/components/Switch/Switch.js +1 -1
  244. package/dist/components/Switch/Switch.js.map +1 -1
  245. package/dist/components/Tooltip/Tooltip.js +1 -1
  246. package/dist/components/Tooltip/Tooltip.js.map +1 -1
  247. package/dist/index.d.ts +4 -1
  248. package/dist/index.js +3 -0
  249. package/dist/index.js.map +1 -1
  250. package/dist/lib/useSelectionIndicator.js +14 -10
  251. package/dist/lib/useSelectionIndicator.js.map +1 -1
  252. package/dist/semantic.css +19 -0
  253. package/dist/styles.css +1 -1
  254. package/dist/themes.css +103 -7
  255. package/dist/tokens.css +193 -10
  256. package/dist/tokens.d.ts +25 -0
  257. package/dist/tokens.js +154 -8
  258. package/dist/tokens.json +154 -8
  259. package/package.json +45 -20
  260. package/skills/misoto22-design/SKILL.md +171 -0
  261. package/skills/misoto22-design/evals/claims.json +95 -0
  262. package/skills/misoto22-design/rules/a11y.md +115 -0
  263. package/skills/misoto22-design/rules/composition.md +108 -0
  264. package/skills/misoto22-design/rules/forms.md +128 -0
  265. package/skills/misoto22-design/rules/naming.md +155 -0
  266. package/skills/misoto22-design/rules/tokens.md +131 -0
@@ -0,0 +1,73 @@
1
+ # Skeleton
2
+
3
+ The shape of the page, before the page.
4
+
5
+ - Group: Feedback
6
+ - Import: `import { Skeleton } from '@misoto22/design'`
7
+ - Version: 0.6.0
8
+ - Docs: https://ui.misoto22.com/components/skeleton/
9
+ - Related: spinner, empty-state
10
+
11
+ ## When to reach for it
12
+
13
+ A wait the reader would otherwise think was a broken page. A shape that describes what is coming beats a dot that describes nothing.
14
+
15
+ ## Accessibility
16
+
17
+ - One live region on the wrapper; every shape inside it is aria-hidden.
18
+ - One pulse on the wrapper, not one per bar, so the page breathes together.
19
+
20
+ ## Skeleton
21
+
22
+ The fill every skeleton part is drawn from: `--stone`, and nothing else. Deliberately NOT a shimmer. A shimmer sweeps a highlight across the shape, which implies a light source; the White Reset has none, so the sweep reads as a bug rather than as loading. The whole page breathes together instead — see `SkeletonPage`, which owns the single pulse.
23
+
24
+ Also accepts: `HTMLAttributes<HTMLDivElement>`.
25
+
26
+ ## SkeletonLine
27
+
28
+ A line of type. Height is the line's; width is how far it runs.
29
+
30
+ Also accepts: `HTMLAttributes<HTMLDivElement>`.
31
+
32
+ ## SkeletonBlock
33
+
34
+ A block: an image mount, a plate, a chart.
35
+
36
+ Also accepts: `HTMLAttributes<HTMLDivElement>`.
37
+
38
+ ## SkeletonCircle
39
+
40
+ A circle: an avatar, a marker.
41
+
42
+ Also accepts: `HTMLAttributes<HTMLDivElement>`.
43
+
44
+ ## SkeletonText
45
+
46
+ A paragraph. The last line is short, because real prose ends mid-measure. A block of equal full-width bars reads as a table, and the reader then flinches when a paragraph arrives instead.
47
+
48
+ ### Props
49
+
50
+ - `lines` — `number` default `3`. How many lines of prose to stand in for.
51
+
52
+ Also accepts: `HTMLAttributes<HTMLDivElement>`.
53
+
54
+ ## SkeletonPage
55
+
56
+ The frame a skeleton sits in: the live region, and the single pulse. One `animate-pulse` on the wrapper rather than one per part, so the page breathes together instead of shimmering out of phase — twenty independently animated bars is a visual noise floor, not a loading state. A skeleton whose shape does not match what replaces it is worse than none: it promises a layout and then the page jumps out from under the reader. Compose the parts to match the real thing, and change both together.
57
+
58
+ ### Props
59
+
60
+ - `label` (required) — `string`. What is loading, in words. Announced once; the shapes themselves are `aria-hidden`, so this sentence is the entire experience for a screen reader and "Loading" alone tells it nothing.
61
+ - `children` (required) — `ReactNode`.
62
+
63
+ Also accepts: `HTMLAttributes<HTMLDivElement>`.
64
+
65
+ ## Examples
66
+
67
+ ```tsx
68
+ <Skeleton className="h-3 w-40 rounded-(--radius-pill)" />
69
+ <SkeletonPage label="Loading projects">
70
+ <SkeletonLine className="h-2.5 w-28" />
71
+ <SkeletonBlock className="mt-6 h-40" />
72
+ </SkeletonPage>
73
+ ```
@@ -0,0 +1,39 @@
1
+ # Slider
2
+
3
+ A value chosen along a range.
4
+
5
+ - Group: Forms
6
+ - Import: `import { Slider } from '@misoto22/design'`
7
+ - Version: 0.6.0
8
+ - Docs: https://ui.misoto22.com/components/slider/
9
+ - Related: progress
10
+
11
+ ## Accessibility
12
+
13
+ - label is required. A thumb that announces "42" and nothing else leaves a screen reader user with a number and no idea what it measures.
14
+ - A 44px hit area sits invisibly around the 16px thumb.
15
+ - Arrows step, Page keys jump, Home and End reach the ends.
16
+
17
+ ## Keyboard
18
+
19
+ - ← / → — Moves by one step.
20
+ - Page Up / Page Down — Moves by a larger step.
21
+ - Home / End — Jumps to the minimum or maximum.
22
+
23
+ ## Slider
24
+
25
+ A value chosen along a range. Radix owns the keyboard contract — arrows step, Page keys jump, Home and End reach the ends — and the ARIA that reports the value. What is here is the look, and the labelling, which is the part a slider most often gets wrong: a thumb that announces "42" and nothing else leaves a screen reader user with a number and no idea what it measures. A 44px hit area sits invisibly around the 16px thumb, because a thumb sized for the design is well under any pointer-target guideline.
26
+
27
+ ### Props
28
+
29
+ - `label` (required) — `string | [string, string]`. Names the control. Required: a slider with no name announces only a number, and a number with no noun is not information. A range slider (two thumbs) needs one name per thumb — pass an array.
30
+ - `showValue` — `boolean` default `false`. Prints the current value beside the label.
31
+ - `format` — `(value: number) => string` default `String`. Renders the value with a unit or a currency, e.g. `(n) => n + '%'`.
32
+
33
+ Also accepts: `ComponentProps<typeof SliderPrimitive.Root>`.
34
+
35
+ ## Examples
36
+
37
+ ```tsx
38
+ <Slider label="Quality" defaultValue={[80]} max={100} step={5} showValue format={(n) => `${n}%`} />
39
+ ```
@@ -0,0 +1,45 @@
1
+ # Sparkline
2
+
3
+ A run of numbers at the size of a word.
4
+
5
+ - Group: Data
6
+ - Import: `import { Sparkline } from '@misoto22/design'`
7
+ - Version: 0.6.0
8
+ - Docs: https://ui.misoto22.com/components/sparkline/
9
+ - Related: line-chart, table
10
+
11
+ ## When to reach for it
12
+
13
+ In a table cell, beside a figure, at the end of a row. When the trend needs reading precisely it wants a LineChart and its own space.
14
+
15
+ ## Accessibility
16
+
17
+ - label is required and is the whole accessible name: a sparkline has no axes and no legend, so nothing else describes it.
18
+ - Axis-less by design. Every piece of chrome that would let it answer “what value exactly” also makes it too big to sit inline, which was the only reason to reach for it.
19
+ - Pin domain for a column of them: on independent domains every row peaks and troughs identically, which is how a table of sparklines becomes actively misleading.
20
+ - One path, no rendering engine — so a hundred of them in a table cost nothing.
21
+
22
+ ## Sparkline
23
+
24
+ A run of numbers at the size of a word — in a table cell, beside a figure, at the end of a row. Deliberately axis-less, gridless and label-less: a sparkline answers "what shape has this been", and every piece of chrome that would make it answer "what value exactly" also makes it too big to sit inline, which was the only reason to reach for it. When the exact value matters, print the number beside it — `value` does — and when the trend needs reading precisely, it wants a `<LineChart>` and its own space. No rendering engine: it is one `<path>` over a normalised viewBox, so it costs nothing to put a hundred of them in a table.
25
+
26
+ ### Props
27
+
28
+ - `data` (required) — `number[]`. The numbers, in order. Anything shorter than two points draws nothing.
29
+ - `label` (required) — `string`. What the run is, in a sentence. Required, and it is the whole accessible name: a sparkline has no axes and no legend, so nothing else describes it.
30
+ - `variant` — `SparklineVariant` default `'line'`.
31
+ - `height` — `number` default `28`. Height in pixels. The width comes from the container.
32
+ - `domain` — `[number, number]`. The domain, as `[min, max]`. Derived from the data when omitted. Pin it whenever a column of sparklines is meant to be compared: on independent domains every row peaks and troughs identically, which is the one way a table of sparklines can be actively misleading.
33
+ - `showLast` — `boolean` default `true`. Marks the last point, which is usually the one being asked about.
34
+ - `value` — `ReactNode`. The value announced alongside the label — the current reading, formatted. Falls back to the last number.
35
+ - `className` — `string`.
36
+
37
+ ## Types
38
+
39
+ - `SparklineVariant` = `'line' | 'area' | 'bars'`
40
+
41
+ ## Examples
42
+
43
+ ```tsx
44
+ <Sparkline label="Weekly signups" data={[12, 18, 9, 24, 30, 22, 41]} value="41" />
45
+ ```
@@ -0,0 +1,39 @@
1
+ # Spinner
2
+
3
+ The one “working” indicator — a ring, never a shimmer.
4
+
5
+ - Group: Feedback
6
+ - Import: `import { Spinner } from '@misoto22/design'`
7
+ - Version: 0.6.0
8
+ - Docs: https://ui.misoto22.com/components/spinner/
9
+ - Related: skeleton, progress
10
+
11
+ ## When to reach for it
12
+
13
+ A wait short enough that the shape of what is coming does not matter. Longer than that, use a Skeleton.
14
+
15
+ ## Accessibility
16
+
17
+ - label names the specific thing being waited on; three spinners all saying “Loading” tell a screen reader nothing.
18
+ - label={null} silences it for use inside a control that already announces the operation.
19
+ - Spins under motion-safe only; the static ring still reads as unfinished because the leading quarter is darker.
20
+
21
+ ## Spinner
22
+
23
+ The system's one "working" indicator. A ring, not a blurred glow and not a shimmer: the White Reset has no light source, so depth and softness are not available to it. The leading quarter is the only thing that distinguishes the ring from a plain circle, which is why the track stays a hairline — a spinner is a hint that time is passing, not a feature of the page. It spins under `motion-safe` only. A reader who has asked for less motion gets a static ring, which still reads as "not finished" because the leading quarter is darker than the track. For a wait long enough that the reader would otherwise wonder whether the page is broken, prefer `Skeleton` — a shape that describes what is coming beats a dot that describes nothing.
24
+
25
+ ### Props
26
+
27
+ - `size` — `'sm' | 'md' | 'lg'` default `'md'`. 14px / 18px / 26px. Match the size of the type it sits beside.
28
+ - `tone` — `'default' | 'current'` default `'default'`. `default` draws the ring against the page — a hairline track in the rule colour with the leading quarter in ink. `current` draws it in the inherited text colour, which is what a spinner inside a filled button needs: on an ink ground an ink ring is invisible.
29
+ - `label` — `string | null` default `'Loading'`. Announced to assistive tech. Pass the specific thing being waited on ("Loading projects"), not the generic word — a screen reader user hearing "Loading" three times cannot tell which three things. Pass `null` for a spinner that sits inside a control which already names the operation (a button whose own label changes to "Saving…"), so the two are not read out twice.
30
+
31
+ Also accepts: `HTMLAttributes<HTMLSpanElement>`.
32
+
33
+ ## Examples
34
+
35
+ ```tsx
36
+ <Spinner size="lg" label="Loading projects" />
37
+ // Inside a filled button, where the ground is ink:
38
+ <Spinner size="sm" tone="current" label={null} />
39
+ ```
@@ -0,0 +1,36 @@
1
+ # StatusDot
2
+
3
+ The dot beside a status word.
4
+
5
+ - Group: Display
6
+ - Import: `import { StatusDot } from '@misoto22/design'`
7
+ - Version: 0.6.0
8
+ - Docs: https://ui.misoto22.com/components/status-dot/
9
+ - Related: status-pill
10
+
11
+ ## Accessibility
12
+
13
+ - aria-hidden without exception: it repeats a state the adjacent label already names.
14
+ - The halo is motion-safe, so a reader who asked for less motion gets a still dot.
15
+
16
+ ## StatusDot
17
+
18
+ The dot beside a status word. `aria-hidden` without exception: the dot repeats a state the adjacent label already names, and a screen reader announcing "available" twice is worse than not announcing the decoration at all. If a call site has no visible label, the fix is a label, not an `aria-label` on the dot. The halo is a separate absolutely-positioned ring rather than a box-shadow, because Law 2 of this system is that a shadow is never blurred — and it is motion-safe, so a reader who asked for less motion gets a static dot.
19
+
20
+ ### Props
21
+
22
+ - `size` — `'sm' | 'md'` default `'md'`. 7px / 8px. `md` is the default.
23
+ - `tone` — `StatusTone` default `'success'`.
24
+ - `pulse` — `boolean` default `true`. A halo that grows and dissolves, for "live right now". Off for a settled state.
25
+
26
+ Also accepts: `HTMLAttributes<HTMLSpanElement>`.
27
+
28
+ ## Types
29
+
30
+ - `StatusTone` = `'success' | 'warning' | 'danger' | 'neutral'`
31
+
32
+ ## Examples
33
+
34
+ ```tsx
35
+ <span className="inline-flex items-center gap-2"><StatusDot /> Available</span>
36
+ ```
@@ -0,0 +1,28 @@
1
+ # StatusPill
2
+
3
+ A live state, named: a dot plus an uppercase mono label.
4
+
5
+ - Group: Display
6
+ - Import: `import { StatusPill } from '@misoto22/design'`
7
+ - Version: 0.6.0
8
+ - Docs: https://ui.misoto22.com/components/status-pill/
9
+ - Related: status-dot, badge
10
+
11
+ ## StatusPill
12
+
13
+ A live state, named: a dot plus an uppercase mono label in an outlined pill. One component rather than a dot and a pill assembled per call site, which is how the same "available for work" chip ended up with three different dot sizes and two different pulse timings on one site.
14
+
15
+ ### Props
16
+
17
+ - `children` (required) — `ReactNode`.
18
+ - `tone` — `StatusTone` default `'success'`.
19
+ - `pulse` — `boolean` default `true`. Pulsing live dot (default) vs a settled one.
20
+
21
+ Also accepts: `HTMLAttributes<HTMLSpanElement>`.
22
+
23
+ ## Examples
24
+
25
+ ```tsx
26
+ <StatusPill>Available for work</StatusPill>
27
+ <StatusPill tone="warning" pulse={false}>Degraded</StatusPill>
28
+ ```
@@ -0,0 +1,44 @@
1
+ # Steps
2
+
3
+ A numbered sequence, as a rail — one thing after another, with a rule through them.
4
+
5
+ - Group: Display
6
+ - Import: `import { Steps } from '@misoto22/design'`
7
+ - Version: 0.6.0
8
+ - Docs: https://ui.misoto22.com/components/steps/
9
+ - Related: diagram, article
10
+
11
+ ## When to reach for it
12
+
13
+ A pipeline, a migration, a recipe: an order with no branches. The moment something forks or points at something else it is a Diagram, and drawing a fork as a list hides it.
14
+
15
+ ## Accessibility
16
+
17
+ - An <ol>, because the order IS the content — a stack of divs says nothing about sequence.
18
+ - aria-current="step" marks the filled one, which is the only thing here a reader could not infer from the reading order.
19
+ - The markers and the connector are aria-hidden: the number is the list position, and screen readers already announce that.
20
+
21
+ ## Steps
22
+
23
+ A numbered sequence, as a rail. The shape a pipeline actually has: one thing after another, each with a name and a line of detail, and a rule running through them so the eye reads them as one process rather than as five unrelated rows. It is the figure a technical post reaches for most often after a diagram, and it is NOT a diagram — nothing branches, nothing points at anything, and drawing it with boxes and arrows says otherwise. The connector is drawn on the ITEM rather than as a full-height line behind the markers, so it starts under one and stops above the next instead of running through both — and so the last step has no tail hanging off it. That is the detail that separates a rail from a list with a border on it. An `<ol>`, because the order is the content. `aria-current="step"` marks the filled one, which is the only thing here a screen reader could not otherwise infer from the order it is read in.
24
+
25
+ ### Props
26
+
27
+ - `steps` (required) — `Step[]`.
28
+ - `label` — `string`. Names the sequence for assistive tech when no heading does.
29
+ - `marker` — `'number' | 'rule'` default `'number'`. How the marker is drawn. `number` counts from one and is right for a pipeline, a recipe, a migration. `rule` drops the digit for a plain hairline node, which is what a sequence of states wants — "queued, running, done" is an order, not a numbered list.
30
+
31
+ Also accepts: `Omit<HTMLAttributes<HTMLOListElement>, 'children'>`.
32
+
33
+ ## Examples
34
+
35
+ ```tsx
36
+ <Steps
37
+ label="How an answer is built"
38
+ steps={[
39
+ { title: 'Corpus', note: 'Blog MDX · project database' },
40
+ { title: 'Chunking', note: 'By heading · 300–800 tokens' },
41
+ { title: 'Answer', note: 'Live citation panel', current: true },
42
+ ]}
43
+ />
44
+ ```
@@ -0,0 +1,29 @@
1
+ # Switch
2
+
3
+ A setting that takes effect immediately.
4
+
5
+ - Group: Forms
6
+ - Import: `import { Switch } from '@misoto22/design'`
7
+ - Version: 0.6.0
8
+ - Docs: https://ui.misoto22.com/components/switch/
9
+ - Related: checkbox
10
+
11
+ ## When to reach for it
12
+
13
+ Inside a form with a Save button, a switch is a lie about when the change happened. Use a Checkbox.
14
+
15
+ ## Keyboard
16
+
17
+ - Space / Enter — Toggles it, and the change applies immediately.
18
+
19
+ ## Switch
20
+
21
+ A setting that takes effect immediately. Distinct from `Checkbox`, and the distinction is not cosmetic: a switch applies on flip, a checkbox applies on submit. A switch inside a form with a Save button is a lie about when the change happened. The track fills with ink when on and the thumb is paper with a hairline — rather than a white thumb floating on a drop shadow, which this system does not have. The off state is a filled rule-coloured track, so the control still reads as a control on a white page.
22
+
23
+ Also accepts: `ComponentProps<typeof SwitchPrimitive.Root>`.
24
+
25
+ ## Examples
26
+
27
+ ```tsx
28
+ <Field label="Email notifications"><Switch defaultChecked /></Field>
29
+ ```
@@ -0,0 +1,85 @@
1
+ # Table
2
+
3
+ A ruled data table — alignment, sorting and rules all per column.
4
+
5
+ - Group: Data
6
+ - Import: `import { Table } from '@misoto22/design'`
7
+ - Version: 0.6.0
8
+ - Docs: https://ui.misoto22.com/components/table/
9
+ - Related: card, figure-band
10
+
11
+ ## When to reach for it
12
+
13
+ Alignment is per column and numbers belong at the end edge, so digits line up. Sorting is opt-in per column: a table where every header is a button invites sorting a column the data cannot be ordered by.
14
+
15
+ ## Accessibility
16
+
17
+ - caption is required: an unnamed table on a page with three tables is unnavigable.
18
+ - Column labels are <th scope="col">, so a cell can be traced back to its heading.
19
+ - A sortable header is a button INSIDE the th, not a click handler on the cell — a cell with an onClick is not focusable and not announced, so the sort would exist only for a mouse.
20
+ - aria-sort is set from sortDirection, which is the only way a screen reader learns the table is ordered at all.
21
+ - No zebra striping at any border setting: in a monochrome system a striped row is a second surface competing with the page ground.
22
+
23
+ ## Keyboard
24
+
25
+ - Tab — Reaches the scroll region, and each sortable column header.
26
+ - ← / → — Scrolls the table sideways once the region has focus.
27
+
28
+ ## Table
29
+
30
+ A ruled data table. Wrapped in its own horizontally scrolling container, because a table is the one block that legitimately exceeds the measure — and a page that scrolls sideways as a whole is a layout bug, while a table that does is a table. That container is focusable, and it has to be. A scrollable region whose contents are not themselves focusable is unreachable by keyboard: there is no element to Tab to and therefore no way to press an arrow key at it, so the columns past the fold simply do not exist for anyone not using a mouse. No zebra striping at any border setting. In a monochrome system a striped row is a second surface competing with the page ground, and the hairline between rows is already enough to track a line across.
31
+
32
+ ### Props
33
+
34
+ - `caption` (required) — `string`. Describes the table for a screen reader, which cannot see the heading above it. Rendered as a visually-hidden `<caption>` — required, because an unnamed table in a page with three tables is unnavigable.
35
+ - `showCaption` — `boolean` default `false`. Prints the caption instead of hiding it.
36
+ - `stickyHeader` — `boolean` default `false`. Pins the header row while the body scrolls. Needs a bounded height on the container — otherwise the page scrolls, not the table, and nothing sticks.
37
+ - `borders` — `TableBorders` default `'rows'`. Which rules to draw. See .
38
+ - `density` — `'comfortable' | 'compact'` default `'comfortable'`. Tightens the row padding, for a table that is mostly numbers.
39
+
40
+ Also accepts: `TableHTMLAttributes<HTMLTableElement>`.
41
+
42
+ ## THead
43
+
44
+ Header group. The rule under it is drawn by the table's border setting.
45
+
46
+ ## TBody
47
+
48
+ ## TR
49
+
50
+ ## TH
51
+
52
+ A column label. Mono and uppercase, so it never reads as data. When sortable, the label becomes a `<button>` INSIDE the `<th>` rather than the `<th>` becoming clickable: a cell with a click handler is not focusable and not announced as a control, so the sort exists only for a mouse. Sorting is per column and opt-in. A table where every header is a button invites the reader to try sorting a column the data cannot be ordered by.
53
+
54
+ ### Props
55
+
56
+ - `align` — `TableAlign` default `'start'`. Which edge the column's contents sit against. Numbers belong at `end`.
57
+ - `sortable` — `boolean` default `false`. Makes the label a button and shows the sort marker.
58
+ - `sortDirection` — `SortDirection` default `'none'`. Which way this column is sorted. Also set as `aria-sort`, which is the only way a screen reader learns a table is ordered at all — a caret drawn in the header tells it nothing.
59
+ - `onSort` — `() => void`.
60
+
61
+ Also accepts: `Omit<ThHTMLAttributes<HTMLTableCellElement>, 'align'>`.
62
+
63
+ ## TD
64
+
65
+ ### Props
66
+
67
+ - `align` — `TableAlign` default `'start'`. Which edge the cell's contents sit against. Match the column's header.
68
+
69
+ Also accepts: `Omit<TdHTMLAttributes<HTMLTableCellElement>, 'align'>`.
70
+
71
+ ## Types
72
+
73
+ - `TableAlign` = `'start' | 'center' | 'end'`
74
+ - `TableBorders` = `'rows' | 'grid' | 'bordered' | 'bordered-grid' | 'none'`
75
+ - `SortDirection` = `'ascending' | 'descending' | 'none'`
76
+
77
+ ## Examples
78
+
79
+ ```tsx
80
+ <Table caption="Deploy history">
81
+ <THead><TR><TH>Commit</TH><TH align="end" sortable sortDirection={dir} onSort={sort}>Duration</TH></TR></THead>
82
+ <TBody><TR><TD>a1b2c3d</TD><TD align="end">2m 14s</TD></TR></TBody>
83
+ </Table>
84
+ <Table caption="Inventory" borders="bordered-grid" density="compact">…</Table>
85
+ ```
@@ -0,0 +1,48 @@
1
+ # Tabs
2
+
3
+ One strip, several panels.
4
+
5
+ - Group: Navigation
6
+ - Import: `import { Tabs } from '@misoto22/design'`
7
+ - Version: 0.6.0
8
+ - Docs: https://ui.misoto22.com/components/tabs/
9
+ - Related: accordion
10
+
11
+ ## Accessibility
12
+
13
+ - The strip scrolls rather than wrapping: a wrapped second row moves every tab below it and the reader loses the one they were about to click.
14
+ - 44px tall, because a tab is a pointer target like any other.
15
+
16
+ ## Keyboard
17
+
18
+ - Tab — Moves into the strip, and out of it — the whole strip is one stop.
19
+ - ← / → — Moves between tabs and switches the panel with them.
20
+ - Home / End — Jumps to the first or last tab.
21
+
22
+ ## Tabs
23
+
24
+ Radix Tabs root, re-exported as a typed passthrough.
25
+
26
+ ## TabsList
27
+
28
+ The tab strip. Scrolls horizontally rather than wrapping. A wrapped second row of tabs moves every tab below it when the strip grows, and the reader loses the one they were about to click.
29
+
30
+ ## TabsTrigger
31
+
32
+ One tab. The active marker is a 2px ink rule pulled onto the strip's own border with `-mb-px`, so the two occupy the same line instead of stacking into a 3px edge. 44px tall, because a tab is a pointer target like any other.
33
+
34
+ ## TabsContent
35
+
36
+ The panel paired to a by matching `value`.
37
+
38
+ ## Examples
39
+
40
+ ```tsx
41
+ <Tabs defaultValue="preview">
42
+ <TabsList>
43
+ <TabsTrigger value="preview">Preview</TabsTrigger>
44
+ <TabsTrigger value="code">Code</TabsTrigger>
45
+ </TabsList>
46
+ <TabsContent value="preview">…</TabsContent>
47
+ </Tabs>
48
+ ```
@@ -0,0 +1,35 @@
1
+ # Tag
2
+
3
+ A subject label — a topic, a technology, a filter facet.
4
+
5
+ - Group: Display
6
+ - Import: `import { Tag } from '@misoto22/design'`
7
+ - Version: 0.6.0
8
+ - Docs: https://ui.misoto22.com/components/tag/
9
+ - Related: badge
10
+
11
+ ## When to reach for it
12
+
13
+ Several sit together and the reader scans them. One fact about one record is a Badge.
14
+
15
+ ## Accessibility
16
+
17
+ - Presentational. To filter with it, wrap it in a button and pass active, so the focus ring and the pressed state stay with the element that owns them.
18
+
19
+ ## Tag
20
+
21
+ A subject label — a topic, a technology, a filter facet. Distinct from `Badge`, which carries a state or a count. A tag names what something is ABOUT, so several sit together in a row and the reader scans them; a badge is one fact about one record. Presentational on purpose. To make a tag filter a list, wrap it in a button or a link at the call site and pass `active` — that keeps the focus ring, the pressed state and the keyboard handling with the element that actually owns them.
22
+
23
+ ### Props
24
+
25
+ - `children` (required) — `ReactNode`.
26
+ - `active` — `boolean` default `false`. Renders the pressed/selected look. Pair with `aria-pressed` on a wrapper button.
27
+
28
+ Also accepts: `HTMLAttributes<HTMLSpanElement>`.
29
+
30
+ ## Examples
31
+
32
+ ```tsx
33
+ <Tag>TypeScript</Tag>
34
+ <button type="button" aria-pressed={on} onClick={toggle}><Tag active={on}>Rust</Tag></button>
35
+ ```
@@ -0,0 +1,26 @@
1
+ # Textarea
2
+
3
+ Multi-line text entry, resizable vertically only.
4
+
5
+ - Group: Forms
6
+ - Import: `import { Textarea } from '@misoto22/design'`
7
+ - Version: 0.6.0
8
+ - Docs: https://ui.misoto22.com/components/textarea/
9
+ - Related: input, field
10
+
11
+ ## Textarea
12
+
13
+ Multi-line text entry. Resizes vertically only: horizontal resize lets a reader drag the control past the measure and past the page's own gutter.
14
+
15
+ ### Props
16
+
17
+ - `invalid` — `boolean`. Paints the resting border with `--danger` and reflects `aria-invalid`.
18
+ - `ref` — `Ref<HTMLTextAreaElement>`.
19
+
20
+ Also accepts: `TextareaHTMLAttributes<HTMLTextAreaElement>`.
21
+
22
+ ## Examples
23
+
24
+ ```tsx
25
+ <Field label="Notes" htmlFor="notes"><Textarea id="notes" rows={4} /></Field>
26
+ ```
@@ -0,0 +1,26 @@
1
+ # Toast
2
+
3
+ A transient confirmation, mounted once near the app root.
4
+
5
+ - Group: Feedback
6
+ - Import: `import { Toast } from '@misoto22/design'`
7
+ - Version: 0.6.0
8
+ - Docs: https://ui.misoto22.com/components/toast/
9
+ - Related: alert
10
+
11
+ ## When to reach for it
12
+
13
+ Something succeeded and needs no response. A toast is dismissed by time, and time is not an acknowledgement.
14
+
15
+ ## Toaster
16
+
17
+ Transient confirmations, mounted once near the app root. A toast is for something that succeeded and needs no response. Anything the reader must act on belongs in the page — a toast is dismissed by time, and time is not an acknowledgement. `richColors` stays off by default: sonner's rich palette is not this system's status scale, and turning it on reintroduces chroma the White Reset spends only on state.
18
+
19
+ ## Examples
20
+
21
+ ```tsx
22
+ // app root
23
+ <Toaster />
24
+ // anywhere
25
+ toast.success('Saved')
26
+ ```
@@ -0,0 +1,44 @@
1
+ # ToggleGroup
2
+
3
+ A segmented control: several options, one strip.
4
+
5
+ - Group: Forms
6
+ - Import: `import { ToggleGroup } from '@misoto22/design'`
7
+ - Version: 0.6.0
8
+ - Docs: https://ui.misoto22.com/components/toggle-group/
9
+ - Related: tabs, radio-group
10
+
11
+ ## When to reach for it
12
+
13
+ It changes a VALUE. Something that switches panels is Tabs.
14
+
15
+ ## Accessibility
16
+
17
+ - type="single" gets radio semantics; type="multiple" gets independent toggles. Choosing wrong tells a screen reader that picking one option unpicks the others.
18
+
19
+ ## Keyboard
20
+
21
+ - Tab — Moves into the strip — one stop for the group.
22
+ - ← / → — Moves between segments.
23
+ - Enter / Space — Toggles the focused segment.
24
+
25
+ ## ToggleGroup
26
+
27
+ A segmented control: several options, one strip. `type="single"` is a choice — Radix gives it radio semantics, and it is the right shape for a view switcher or a density setting. `type="multiple"` is a set of independent toggles, which is a different thing announced differently; choosing the wrong one is how a "filter by tag" control ends up telling a screen reader that picking one tag unpicks the others. The two look different on purpose. A single-value strip moves ONE filled pill between its options, so the eye follows a thing travelling; a multiple-value strip fills each pressed option separately, because there is no single selection to travel. Two people looking at a screenshot should be able to tell which kind they are looking at, and before this they could not. Distinct from `Tabs`, which switches PANELS and owns a tabpanel relationship. A toggle group changes a value.
28
+
29
+ Also accepts: `ComponentProps<typeof ToggleGroupPrimitive.Root>`.
30
+
31
+ ## ToggleGroupItem
32
+
33
+ One segment. In a single-value group the item draws no background of its own — the travelling pill behind it does — so it only changes ink. In a multiple-value group it fills, because there is nothing travelling.
34
+
35
+ Also accepts: `ComponentProps<typeof ToggleGroupPrimitive.Item>`.
36
+
37
+ ## Examples
38
+
39
+ ```tsx
40
+ <ToggleGroup type="single" defaultValue="grid" aria-label="Layout">
41
+ <ToggleGroupItem value="grid">Grid</ToggleGroupItem>
42
+ <ToggleGroupItem value="list">List</ToggleGroupItem>
43
+ </ToggleGroup>
44
+ ```
@@ -0,0 +1,51 @@
1
+ # Tooltip
2
+
3
+ A short label on hover and on focus.
4
+
5
+ - Group: Overlays
6
+ - Import: `import { Tooltip } from '@misoto22/design'`
7
+ - Version: 0.6.0
8
+ - Docs: https://ui.misoto22.com/components/tooltip/
9
+ - Related: dialog
10
+
11
+ ## When to reach for it
12
+
13
+ Never for anything the reader NEEDS: a tooltip is unreachable on touch and invisible while scanning.
14
+
15
+ ## Accessibility
16
+
17
+ - Inside a bounded frame — a device preview, an embedded console — wrap the subtree in `<OverlayContainer container={el}>`. The panel then renders into that element and collides with its edges instead of the viewport’s, and inherits the `dir` and `data-density` set there.
18
+ - The trigger is asChild, so the child must be focusable — a div trigger simply has no keyboard tooltip, which this API shape makes obvious rather than silent.
19
+ - Not an accessible name. An icon-only button still needs its own aria-label.
20
+
21
+ ## Keyboard
22
+
23
+ - Tab — Shows the tip — focus reveals it, not only hover.
24
+ - Escape — Dismisses it.
25
+
26
+ ## TooltipProvider
27
+
28
+ Wrap the app — or the smallest subtree that has tooltips — once. Radix needs it to share the open/close timing between neighbouring triggers, which is what stops a row of icon buttons flashing a tooltip per hover.
29
+
30
+ ## Tooltip
31
+
32
+ A short label on hover and on focus. `asChild` on the trigger by design: the tooltip must not add a wrapper that swallows the trigger's own focus ring or breaks a flex row. It also means the child has to be focusable — a `<div>` trigger gets no keyboard tooltip, which is the failure this API shape makes obvious rather than silent. Not a replacement for an accessible name. An icon-only button still needs its own `aria-label`; the tooltip repeats that name for sighted pointer users.
33
+
34
+ ### Props
35
+
36
+ - `children` (required) — `ReactNode`. The element the tooltip describes. Must be focusable.
37
+ - `content` (required) — `ReactNode`. The tip. Keep it to a phrase — a tooltip is unreachable on touch and invisible to a reader who is scanning, so anything a user NEEDS belongs on the page instead.
38
+ - `side` — `ComponentProps<typeof TooltipPrimitive.Content>['side']` default `'top'`.
39
+ - `sideOffset` — `number` default `6`.
40
+
41
+ Also accepts: `Pick<ComponentProps<typeof TooltipPrimitive.Root>, 'open' | 'defaultOpen' | 'onOpenChange' | 'delayDuration'>`.
42
+
43
+ ## Examples
44
+
45
+ ```tsx
46
+ <TooltipProvider>
47
+ <Tooltip content="Copy to clipboard">
48
+ <Button iconOnly aria-label="Copy"><Copy size={16} /></Button>
49
+ </Tooltip>
50
+ </TooltipProvider>
51
+ ```