@viraui/react 2026.7.1 → 2026.9.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 (223) hide show
  1. package/dist/catalog.json +9 -3
  2. package/dist/components/button/button.css +1 -1
  3. package/dist/components/direction-provider/index.d.ts +2 -0
  4. package/dist/components/direction-provider/index.js +2 -0
  5. package/dist/components/index.d.ts +1 -0
  6. package/dist/components/menu/menu-popup.d.ts +2 -2
  7. package/dist/components/menu/menu.css +1 -1
  8. package/dist/components/progressive-blur/progressive-blur.css +1 -1
  9. package/dist/components/prose/prose.d.ts +4 -1
  10. package/dist/components/select/select-group.js +1 -1
  11. package/dist/components/select/select.css +1 -1
  12. package/dist/components/select/select.js +4 -1
  13. package/dist/components/spinner/spinner.d.ts +6 -1
  14. package/dist/components/spinner/variants/arc.css +1 -0
  15. package/dist/components/spinner/variants/arc.d.ts +2 -0
  16. package/dist/components/spinner/variants/arc.js +19 -0
  17. package/dist/components/spinner/variants/arc.module.js +8 -0
  18. package/dist/components/spinner/variants/atom.css +1 -0
  19. package/dist/components/spinner/variants/atom.d.ts +6 -0
  20. package/dist/components/spinner/variants/atom.js +40 -0
  21. package/dist/components/spinner/variants/atom.module.js +8 -0
  22. package/dist/components/spinner/variants/blocks.css +1 -0
  23. package/dist/components/spinner/variants/blocks.d.ts +2 -0
  24. package/dist/components/spinner/variants/blocks.js +25 -0
  25. package/dist/components/spinner/variants/blocks.module.js +8 -0
  26. package/dist/components/spinner/variants/bouncing-dots.css +1 -0
  27. package/dist/components/spinner/variants/bouncing-dots.d.ts +2 -0
  28. package/dist/components/spinner/variants/bouncing-dots.js +21 -0
  29. package/dist/components/spinner/variants/bouncing-dots.module.js +8 -0
  30. package/dist/components/spinner/variants/cascade.css +1 -0
  31. package/dist/components/spinner/variants/cascade.d.ts +2 -0
  32. package/dist/components/spinner/variants/cascade.js +34 -0
  33. package/dist/components/spinner/variants/cascade.module.js +8 -0
  34. package/dist/components/spinner/variants/circular-dots.css +1 -0
  35. package/dist/components/spinner/variants/circular-dots.d.ts +2 -0
  36. package/dist/components/spinner/variants/circular-dots.js +26 -0
  37. package/dist/components/spinner/variants/circular-dots.module.js +8 -0
  38. package/dist/components/spinner/variants/classic-v2.css +1 -0
  39. package/dist/components/spinner/variants/classic-v2.d.ts +2 -0
  40. package/dist/components/spinner/variants/classic-v2.js +24 -0
  41. package/dist/components/spinner/variants/classic-v2.module.js +8 -0
  42. package/dist/components/spinner/variants/classic.css +1 -0
  43. package/dist/components/spinner/variants/classic.d.ts +2 -0
  44. package/dist/components/spinner/variants/classic.js +22 -0
  45. package/dist/components/spinner/variants/classic.module.js +8 -0
  46. package/dist/components/spinner/variants/clock.css +1 -0
  47. package/dist/components/spinner/variants/clock.d.ts +2 -0
  48. package/dist/components/spinner/variants/clock.js +18 -0
  49. package/dist/components/spinner/variants/clock.module.js +8 -0
  50. package/dist/components/spinner/variants/comet.css +1 -0
  51. package/dist/components/spinner/variants/comet.d.ts +2 -0
  52. package/dist/components/spinner/variants/comet.js +33 -0
  53. package/dist/components/spinner/variants/comet.module.js +10 -0
  54. package/dist/components/spinner/variants/compass.css +1 -0
  55. package/dist/components/spinner/variants/compass.d.ts +2 -0
  56. package/dist/components/spinner/variants/compass.js +16 -0
  57. package/dist/components/spinner/variants/compass.module.js +8 -0
  58. package/dist/components/spinner/variants/dual.css +1 -0
  59. package/dist/components/spinner/variants/dual.d.ts +2 -0
  60. package/dist/components/spinner/variants/dual.js +25 -0
  61. package/dist/components/spinner/variants/dual.module.js +10 -0
  62. package/dist/components/spinner/variants/eclipse.css +1 -0
  63. package/dist/components/spinner/variants/eclipse.d.ts +6 -0
  64. package/dist/components/spinner/variants/eclipse.js +21 -0
  65. package/dist/components/spinner/variants/eclipse.module.js +9 -0
  66. package/dist/components/spinner/variants/flip.css +1 -0
  67. package/dist/components/spinner/variants/flip.d.ts +2 -0
  68. package/dist/components/spinner/variants/flip.js +17 -0
  69. package/dist/components/spinner/variants/flip.module.js +8 -0
  70. package/dist/components/spinner/variants/gather.css +1 -0
  71. package/dist/components/spinner/variants/gather.d.ts +2 -0
  72. package/dist/components/spinner/variants/gather.js +50 -0
  73. package/dist/components/spinner/variants/gather.module.js +11 -0
  74. package/dist/components/spinner/variants/index.d.ts +9 -1
  75. package/dist/components/spinner/variants/index.js +96 -10
  76. package/dist/components/spinner/variants/leap.css +1 -0
  77. package/dist/components/spinner/variants/leap.d.ts +6 -0
  78. package/dist/components/spinner/variants/leap.js +27 -0
  79. package/dist/components/spinner/variants/leap.module.js +8 -0
  80. package/dist/components/spinner/variants/linear-dots.css +1 -0
  81. package/dist/components/spinner/variants/linear-dots.d.ts +2 -0
  82. package/dist/components/spinner/variants/linear-dots.js +21 -0
  83. package/dist/components/spinner/variants/linear-dots.module.js +8 -0
  84. package/dist/components/spinner/variants/loading.css +1 -0
  85. package/dist/components/spinner/variants/loading.d.ts +2 -0
  86. package/dist/components/spinner/variants/loading.js +50 -0
  87. package/dist/components/spinner/variants/loading.module.js +8 -0
  88. package/dist/components/spinner/variants/morph.css +1 -0
  89. package/dist/components/spinner/variants/morph.d.ts +2 -0
  90. package/dist/components/spinner/variants/morph.js +17 -0
  91. package/dist/components/spinner/variants/morph.module.js +8 -0
  92. package/dist/components/spinner/variants/orbit.css +1 -0
  93. package/dist/components/spinner/variants/orbit.d.ts +2 -0
  94. package/dist/components/spinner/variants/orbit.js +24 -0
  95. package/dist/components/spinner/variants/orbit.module.js +8 -0
  96. package/dist/components/spinner/variants/pulse.css +1 -0
  97. package/dist/components/spinner/variants/pulse.d.ts +2 -0
  98. package/dist/components/spinner/variants/pulse.js +19 -0
  99. package/dist/components/spinner/variants/pulse.module.js +8 -0
  100. package/dist/components/spinner/variants/radar.css +1 -0
  101. package/dist/components/spinner/variants/radar.d.ts +2 -0
  102. package/dist/components/spinner/variants/radar.js +53 -0
  103. package/dist/components/spinner/variants/radar.module.js +8 -0
  104. package/dist/components/spinner/variants/ring.css +1 -0
  105. package/dist/components/spinner/variants/ring.d.ts +2 -0
  106. package/dist/components/spinner/variants/ring.js +24 -0
  107. package/dist/components/spinner/variants/ring.module.js +8 -0
  108. package/dist/components/spinner/variants/ripple.css +1 -0
  109. package/dist/components/spinner/variants/ripple.d.ts +2 -0
  110. package/dist/components/spinner/variants/ripple.js +23 -0
  111. package/dist/components/spinner/variants/ripple.module.js +8 -0
  112. package/dist/components/spinner/variants/slide.css +1 -0
  113. package/dist/components/spinner/variants/slide.d.ts +2 -0
  114. package/dist/components/spinner/variants/slide.js +24 -0
  115. package/dist/components/spinner/variants/slide.module.js +8 -0
  116. package/dist/components/spinner/variants/snake.css +1 -0
  117. package/dist/components/spinner/variants/snake.d.ts +2 -0
  118. package/dist/components/spinner/variants/snake.js +19 -0
  119. package/dist/components/spinner/variants/snake.module.js +11 -0
  120. package/dist/components/spinner/variants/swirl.css +1 -0
  121. package/dist/components/spinner/variants/swirl.d.ts +2 -0
  122. package/dist/components/spinner/variants/swirl.js +35 -0
  123. package/dist/components/spinner/variants/swirl.module.js +8 -0
  124. package/dist/components/spinner/variants/trace.css +1 -0
  125. package/dist/components/spinner/variants/trace.d.ts +2 -0
  126. package/dist/components/spinner/variants/trace.js +28 -0
  127. package/dist/components/spinner/variants/trace.module.js +8 -0
  128. package/dist/components/spinner/variants/wave.css +1 -0
  129. package/dist/components/spinner/variants/wave.d.ts +2 -0
  130. package/dist/components/spinner/variants/wave.js +25 -0
  131. package/dist/components/spinner/variants/wave.module.js +8 -0
  132. package/dist/components/stack/stack.css +1 -1
  133. package/dist/components/stack/stack.d.ts +15 -3
  134. package/dist/components/stack/stack.js +2 -1
  135. package/dist/components/surface/surface.css +1 -1
  136. package/dist/components/text/text.css +1 -1
  137. package/dist/components/text/text.d.ts +10 -2
  138. package/dist/components/title/title.d.ts +7 -0
  139. package/dist/index.js +2 -1
  140. package/dist/preflight.css +1 -1
  141. package/dist/specs/PROTOCOL.md +1 -1
  142. package/dist/specs/activity-hints.xml +4 -4
  143. package/dist/specs/components/accordion/props.xml +2 -2
  144. package/dist/specs/components/accordion/usage.xml +2 -0
  145. package/dist/specs/components/autocomplete/patterns.xml +4 -4
  146. package/dist/specs/components/autocomplete/props.xml +2 -2
  147. package/dist/specs/components/autocomplete/usage.xml +1 -1
  148. package/dist/specs/components/button/patterns.xml +2 -2
  149. package/dist/specs/components/button/props.xml +4 -4
  150. package/dist/specs/components/button/usage.xml +2 -1
  151. package/dist/specs/components/chip/patterns.xml +2 -2
  152. package/dist/specs/components/chip/props.xml +2 -2
  153. package/dist/specs/components/chip/usage.xml +2 -0
  154. package/dist/specs/components/direction-provider/a11y.xml +6 -0
  155. package/dist/specs/components/direction-provider/meta.xml +20 -0
  156. package/dist/specs/components/direction-provider/patterns.xml +27 -0
  157. package/dist/specs/components/direction-provider/props.xml +18 -0
  158. package/dist/specs/components/direction-provider/usage.xml +10 -0
  159. package/dist/specs/components/disclosure/patterns.xml +2 -2
  160. package/dist/specs/components/disclosure/props.xml +2 -2
  161. package/dist/specs/components/disclosure/usage.xml +2 -0
  162. package/dist/specs/components/glow/patterns.xml +1 -1
  163. package/dist/specs/components/grid/patterns.xml +3 -3
  164. package/dist/specs/components/icon-button/props.xml +4 -4
  165. package/dist/specs/components/icon-button/usage.xml +3 -1
  166. package/dist/specs/components/menu/patterns.xml +18 -18
  167. package/dist/specs/components/menu/props.xml +5 -5
  168. package/dist/specs/components/menu/usage.xml +3 -1
  169. package/dist/specs/components/meter/patterns.xml +1 -1
  170. package/dist/specs/components/progressive-blur/usage.xml +2 -0
  171. package/dist/specs/components/prose/meta.xml +1 -1
  172. package/dist/specs/components/prose/patterns.xml +34 -10
  173. package/dist/specs/components/prose/props.xml +3 -1
  174. package/dist/specs/components/prose/usage.xml +4 -1
  175. package/dist/specs/components/select/patterns.xml +2 -2
  176. package/dist/specs/components/select/props.xml +2 -2
  177. package/dist/specs/components/select/usage.xml +1 -0
  178. package/dist/specs/components/slider/patterns.xml +1 -1
  179. package/dist/specs/components/spinner/props.xml +1 -1
  180. package/dist/specs/components/spinner/usage.xml +1 -1
  181. package/dist/specs/components/stack/patterns.xml +102 -4
  182. package/dist/specs/components/stack/props.xml +9 -3
  183. package/dist/specs/components/stack/usage.xml +7 -3
  184. package/dist/specs/components/surface/patterns.xml +3 -3
  185. package/dist/specs/components/surface/usage.xml +1 -1
  186. package/dist/specs/components/tabs/patterns.xml +3 -3
  187. package/dist/specs/components/tabs/props.xml +1 -1
  188. package/dist/specs/components/tabs/usage.xml +2 -1
  189. package/dist/specs/components/text/meta.xml +2 -1
  190. package/dist/specs/components/text/patterns.xml +19 -16
  191. package/dist/specs/components/text/props.xml +8 -6
  192. package/dist/specs/components/text/usage.xml +9 -3
  193. package/dist/specs/components/textfield/patterns.xml +3 -3
  194. package/dist/specs/components/textfield/props.xml +2 -2
  195. package/dist/specs/components/textfield/usage.xml +2 -1
  196. package/dist/specs/components/title/meta.xml +2 -1
  197. package/dist/specs/components/title/patterns.xml +11 -10
  198. package/dist/specs/components/title/props.xml +5 -4
  199. package/dist/specs/components/title/usage.xml +8 -3
  200. package/dist/specs/components/toggle-button/patterns.xml +8 -8
  201. package/dist/specs/components/toggle-button/props.xml +3 -3
  202. package/dist/specs/components/toggle-button/usage.xml +2 -0
  203. package/dist/specs/components/toggle-group/patterns.xml +3 -3
  204. package/dist/specs/components/tooltip/patterns.xml +7 -7
  205. package/dist/specs/foundation/attributes.xml +24 -4
  206. package/dist/specs/foundation/bootstrap.xml +1 -1
  207. package/dist/specs/foundation/modules.xml +14 -5
  208. package/dist/specs/foundation/theme-topics.xml +3 -3
  209. package/dist/specs/foundation/toggles.xml +3 -3
  210. package/dist/specs/index.xml +14 -22
  211. package/dist/specs/patterns/compose-layout-first.xml +1 -1
  212. package/package.json +2 -2
  213. package/dist/components/spinner/variants/bars.d.ts +0 -2
  214. package/dist/components/spinner/variants/bars.js +0 -134
  215. package/dist/components/spinner/variants/dot-grid.css +0 -1
  216. package/dist/components/spinner/variants/dot-grid.d.ts +0 -2
  217. package/dist/components/spinner/variants/dot-grid.js +0 -57
  218. package/dist/components/spinner/variants/dot-grid.module.js +0 -16
  219. package/dist/components/spinner/variants/dots.d.ts +0 -2
  220. package/dist/components/spinner/variants/dots.js +0 -189
  221. package/dist/specs/patterns/equal-width-actions.xml +0 -22
  222. package/dist/specs/patterns/page-chrome.xml +0 -34
  223. package/dist/specs/patterns/stack-as-surface.xml +0 -37
@@ -48,17 +48,17 @@
48
48
  aria-label="Repository clone URL"
49
49
  readOnly
50
50
  value="https://github.com/vira-soft/vira-ui.git"
51
- endAddon={<Duplicate size={16} />}
51
+ endAddon={<Duplicate />}
52
52
  />
53
53
  <Text tone="muted">Clone using the web URL.</Text>
54
54
  </Stack>
55
55
  </Surface>
56
56
 
57
57
  <Stack hAlign="start" rowGap="2xs">
58
- <Button addon={<CircleImage size={18} />} variant="flat" size="s">
58
+ <Button addon={<CircleImage />} variant="flat" size="s">
59
59
  Open with GitHub Desktop
60
60
  </Button>
61
- <Button addon={<ArrowDown size={18} />} variant="flat" size="s">
61
+ <Button addon={<ArrowDown />} variant="flat" size="s">
62
62
  Download ZIP
63
63
  </Button>
64
64
  </Stack>
@@ -13,7 +13,7 @@
13
13
  <description>Horizontal alignment of tab list within root Stack.</description>
14
14
  </prop>
15
15
  <prop name="Tab.addon" kind="react">
16
- <description>Decorative addon rendered alongside the tab label.</description>
16
+ <description>Decorative addon rendered alongside the tab label. Omit glyph `size` / width / height unless expressly overriding the icon-library default.</description>
17
17
  </prop>
18
18
  <prop name="Tab.addonPosition" kind="react">
19
19
  <description>Controls whether the addon appears before or after the label.</description>
@@ -4,8 +4,9 @@
4
4
  <do>Login/signup (or similar) form swap: wrap Tabs in Surface; panels hold Stack with Textfield(s) + Button (+ optional Fieldset) — use Stack `rowGap` / `fullWidth` / `expandChildren` and Textfield `fullWidth`; invent 0 width/gap CSS</do>
5
5
  <do>Compound order: `Tabs.List` → `Tabs.Tab`(s), then `Tabs.Viewport` → `Tabs.Panel`(s) with matching `value`</do>
6
6
  <do>Fill list chrome with `listAlignment=&quot;stretch&quot;`; center/end via `listAlignment` on the root</do>
7
- <do>Decorative `Tab` `addon` / `addonPosition` beside a visible text label</do>
7
+ <do>Decorative `Tab` `addon` / `addonPosition` beside a visible text label — omit glyph `size` / width / height unless expressly overriding</do>
8
8
  <dont>Using addons as the only visible label for a tab</dont>
9
+ <dont>Passing `size={16}` on every Tab `addon` icon by habit — prefer icon-library default</dont>
9
10
  <dont>Trying to compose a separate public `Tabs.Indicator`; the indicator is owned by `Tabs.List`</dont>
10
11
  <dont>Inventing panel width/gap/padding CSS when Stack inside `Tabs.Panel` can own spacing and fill</dont>
11
12
  <dont>Vertical tab layouts in v1 styled targets</dont>
@@ -1,11 +1,12 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
2
  <meta id="text" title="Text" category="typography" shared_contract="text">
3
- <summary>Theme-aware body-copy primitive with tone, family, and responsive body sizing.</summary>
3
+ <summary>Theme-aware body-copy primitive with tone, family, sizing, and readable-measure props (`maxWidth` / `lineHeight` / `balanced`).</summary>
4
4
  <exports>
5
5
  <export name="Text" />
6
6
  </exports>
7
7
  <related>
8
8
  <ref id="title" />
9
+ <ref id="prose" />
9
10
  <ref id="clamp-text" />
10
11
  </related>
11
12
  <units>
@@ -1,19 +1,20 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
2
  <patterns surface="text">
3
3
  <!-- Sources: viraui-app + design-kitchen preview-grid — cleaned (no handlers, no i18n, no app-only chrome). -->
4
+ <!-- Type measure: prefer maxWidth in `ch` (not rem) — scaled to size for ~10–12 words/line. -->
4
5
 
5
- <example title="Auth muted lead">
6
+ <example title="Auth muted lead (readable measure)">
6
7
  <tsx><![CDATA[
7
- <Stack vPadding={[0, 'l']} rowGap="xl">
8
- <Title size="1" render={<h1 />} maxWidth="32rem" lineHeight="s">
8
+ <Stack vPadding={[0, 'l']} rowGap="xl" maxWidth="70ch">
9
+ <Title size="1" render={<h1 />} maxWidth="36ch" lineHeight="s" balanced>
9
10
  Design systems that ship
10
11
  </Title>
11
- <Text size="2xl" tone="muted" maxWidth="32rem">
12
+ <Text size="2xl" tone="muted" maxWidth="68ch" balanced>
12
13
  Login to create custom ViraUI themes, access prompts and more.
13
14
  </Text>
14
15
  </Stack>
15
16
  ]]></tsx>
16
- <notes>From `auth/login-view`. Large muted body under Title; `maxWidth` on Text — no CSS width.</notes>
17
+ <notes>From `auth/login-view`. Cap ~10–12 words/line with `maxWidth` in `ch` scaled to size (`2xl` wider than `s`); `balanced` on short wrapping hero copy; `lineHeight="s"` on large Title. Prefer `ch` over rem. No CSS width/line-height.</notes>
17
18
  </example>
18
19
 
19
20
  <example title="Card section blurb">
@@ -22,22 +23,22 @@
22
23
  <Title size="6" render={<h2 />}>
23
24
  Payout Threshold
24
25
  </Title>
25
- <Text tone="muted" size="s">
26
+ <Text tone="muted" size="s" maxWidth="45ch" balanced>
26
27
  Set the minimum balance required before a payout is triggered.
27
28
  </Text>
28
29
  </Stack>
29
30
  ]]></tsx>
30
- <notes>From preview-grid `payout-threshold` (+ preferences / workspace-usage). Default pairing: Title + muted `size="s"` Text.</notes>
31
+ <notes>From preview-grid `payout-threshold` (+ preferences / workspace-usage). Default pairing: Title + muted `size="s"` Text; short blurb prefers `maxWidth` in `ch` + `balanced`.</notes>
31
32
  </example>
32
33
 
33
34
  <example title="Nested weight + tone">
34
35
  <tsx><![CDATA[
35
- <Text lineHeight="s" render={<p />}>
36
+ <Text lineHeight="s" render={<p />} maxWidth="60ch">
36
37
  <Text weight="semibold">Code reviews</Text>{' '}
37
38
  <Text tone="muted">with full codebase context to catch hard-to-find bugs.</Text>
38
39
  </Text>
39
40
  ]]></tsx>
40
- <notes>From preview-grid `vercel-agent`. Outer Text owns paragraph/`lineHeight`; nested Text carry weight/tone — no span CSS.</notes>
41
+ <notes>From preview-grid `vercel-agent`. Outer Text owns paragraph/`lineHeight`/`maxWidth` (`ch`); nested Text carry weight/tone — omit nested lineHeight/size.</notes>
41
42
  </example>
42
43
 
43
44
  <example title="Mono range labels">
@@ -51,14 +52,16 @@
51
52
  </Text>
52
53
  </Stack>
53
54
  ]]></tsx>
54
- <notes>From preview-grid `payout-threshold`. `family="mono"` + muted xs for metric chrome.</notes>
55
+ <notes>From preview-grid `payout-threshold`. `family="mono"` + muted xs for metric chrome — single-line; no `balanced`.</notes>
55
56
  </example>
56
57
 
57
58
  <example title="Form success paragraph">
58
59
  <tsx><![CDATA[
59
- <Text render={<p />}>Check your email — we sent a link to your@email.com</Text>
60
+ <Text render={<p />} maxWidth="60ch" balanced>
61
+ Check your email — we sent a link to your@email.com
62
+ </Text>
60
63
  ]]></tsx>
61
- <notes>From `auth/login-form` acknowledged state. `render={<p />}` for block semantics; default body size/tone.</notes>
64
+ <notes>From `auth/login-form` acknowledged state. `render={<p />}` for block semantics; short wrap prefers `balanced` + `ch` measure.</notes>
62
65
  </example>
63
66
 
64
67
  <example title="Prose link">
@@ -72,16 +75,16 @@
72
75
 
73
76
  <example title="Empty-state centered">
74
77
  <tsx><![CDATA[
75
- <Stack hAlign="center" rowGap="m">
76
- <Title align="center" size="5" render={<h3 />}>
78
+ <Stack hAlign="center" rowGap="m" maxWidth="36ch">
79
+ <Title align="center" size="5" render={<h3 />} balanced>
77
80
  No Team Members
78
81
  </Title>
79
- <Text size="s" align="center" maxWidth="14rem" tone="muted">
82
+ <Text size="s" align="center" maxWidth="40ch" tone="muted" balanced>
80
83
  Invite your team to collaborate on this project.
81
84
  </Text>
82
85
  </Stack>
83
86
  ]]></tsx>
84
- <notes>From preview-grid `empty-team`. Centered muted copy with `maxWidth` — Stack owns alignment.</notes>
87
+ <notes>From preview-grid `empty-team`. Centered muted copy with `maxWidth` in `ch` + `balanced`; Stack owns column cap / centering.</notes>
85
88
  </example>
86
89
 
87
90
  <example title="Claimable balance rows">
@@ -2,6 +2,7 @@
2
2
  <props surface="text">
3
3
  <when_to_use>
4
4
  <item>Body copy with semantic tone (including brand `primary`) and token-backed typography</item>
5
+ <item>Readable UI measure (~10–12 words/line) via `maxWidth` / parent Stack `maxWidth` **scaled to `size`** (smaller → narrower), `lineHeight` matched to size, and `balanced` on short wrapping paragraphs</item>
5
6
  <item>Letter-spacing via `tracking` or text-transform via `transform` — prefer props over CSS on the Text root</item>
6
7
  <item>Helper, label, or inline copy inside other components</item>
7
8
  <item>Mono or body family text; size locked by default (`fluid` false → --font-scale-body-*); pass `fluid` for viewport clamp (also gated by theme `--effect-fluid-typography`)</item>
@@ -9,13 +10,14 @@
9
10
  <when_not_to_use>
10
11
  <item>Headings or display typography — use Title</item>
11
12
  <item>Heading semantics without an appropriate render root</item>
12
- <item>Inventing CSS `letter-spacing` / `text-transform` / `width: 100%` when `tracking` / `transform` / parent Stack `fullWidth` cover the need</item>
13
+ <item>Long-form heading-aware article rhythm — use Prose (keep a reading column)</item>
14
+ <item>Inventing CSS `letter-spacing` / `text-transform` / `line-height` / `max-width` / `text-wrap: balance` / `width: 100%` when `tracking` / `transform` / `lineHeight` / `maxWidth` / `balanced` / parent Stack `fullWidth` cover the need</item>
13
15
  </when_not_to_use>
14
16
  <prop name="align" kind="react">
15
17
  <description>Aligns text using logical alignment values. Omit so `text-align` inherits from the parent.</description>
16
18
  </prop>
17
19
  <prop name="balanced" kind="react">
18
- <description>Enables balanced wrapping for block-like copy.</description>
20
+ <description>Enables balanced wrapping for block-like copy. Prefer on short wrapping UI paragraphs (hero lead, empty state, card blurb); omit when single-line or intentional ragged.</description>
19
21
  </prop>
20
22
  <prop name="color" kind="react">
21
23
  <description>Applies a literal color escape hatch when semantic `tone` is not used.</description>
@@ -24,7 +26,7 @@
24
26
  <description>Selects the semantic font family for body copy and inline code-like text.</description>
25
27
  </prop>
26
28
  <prop name="fluid" kind="react">
27
- <description>Enables viewport fluid clamp sizing for this instance. Default false locks at this size’s --font-scale-body-* token. Pass true for fluid page/body copy. Theme --effect-fluid-typography: initial still locks all Text globally. size xs stays fixed either way (min=max).</description>
29
+ <description>Enables viewport fluid clamp sizing for this instance. Default false locks at this size’s --font-scale-body-* token. Pass true for fluid page/body copy. Theme --effect-fluid-typography: initial still locks all Text globally. size s and xs stay fixed either way (min=max); fluid clamp starts at m.</description>
28
30
  </prop>
29
31
  <prop name="fontWidth" kind="react">
30
32
  <description>Overrides font width as a percentage (100 = normal); maps to `font-stretch` and `wdth`.</description>
@@ -33,10 +35,10 @@
33
35
  <description>Sets inline padding using one token for both sides or a `[start, end]` tuple (`0` allowed). Prefer the tuple for one-sided inset — never `style`/`paddingInline*` CSS when this prop covers it.</description>
34
36
  </prop>
35
37
  <prop name="lineHeight" kind="react">
36
- <description>Controls line height through foundation leading tokens. Root Text without lineHeight uses leading `m`; omit on nested Text so line-height inherits the outer Text.</description>
38
+ <description>Controls line height through foundation leading tokens. Root Text without lineHeight uses leading `m` (`l` when open; `s` for dense chrome); omit on nested Text so line-height inherits the outer Text. Prefer this prop over CSS `line-height`.</description>
37
39
  </prop>
38
40
  <prop name="maxWidth" kind="react">
39
- <description>Constrains the maximum inline size of the rendered text block.</description>
41
+ <description>Constrains the maximum inline size of the rendered text block. Prefer for readable UI measure (~10–12 words/line) over CSS `max-width`. Prefer **`ch`** (not `rem`) in patterns/examples. Width tracks `size` — smaller type → narrower cap (`xs`/`s` much tighter than `xl`/`2xl`); never one fixed ch for every size. Shared columns may use parent Stack `maxWidth` sized for the dominant body `size`.</description>
40
42
  </prop>
41
43
  <prop name="opticalSize" kind="react">
42
44
  <description>Overrides the optical size (`opsz`) axis on variable fonts; static fonts keep `font-optical-sizing: auto`.</description>
@@ -51,7 +53,7 @@
51
53
  <description>Enables text selection on the rendered copy.</description>
52
54
  </prop>
53
55
  <prop name="size" kind="react">
54
- <description>Selects a semantic body typography size from the active theme scale (`4xl` \| `3xl` \| `2xl` \| `xl` \| `l` \| `m` \| `s` \| `xs`). Root Text without size locks at body `m`; omit size on nested Text so font-size inherits the outer Text. Fluid clamp (prev step→token) applies when `fluid` is true and --effect-fluid-typography is empty. Theme effect initial still locks all Text. size xs is always fixed (min=max).</description>
56
+ <description>Selects a semantic body typography size from the active theme scale (`4xl` \| `3xl` \| `2xl` \| `xl` \| `l` \| `m` \| `s` \| `xs`). Root Text without size locks at body `m`; omit size on nested Text so font-size inherits the outer Text. Fluid clamp (prev step→token) applies when `fluid` is true and --effect-fluid-typography is empty. Theme effect initial still locks all Text. size s and xs are always fixed (min=max); fluid starts at m.</description>
55
57
  </prop>
56
58
  <prop name="tone" kind="react">
57
59
  <description>Sets the semantic text color role when no literal `color` override is used. `default` inherits; `primary` → `--global-primary`; `muted` → `--global-muted`; `interactive` → `--global-interactive-text`; `positive` / `informative` / `danger` / `warning` → `--highlight-green` / `--highlight-blue` / `--highlight-red` / `--highlight-yellow`.</description>
@@ -1,17 +1,23 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
2
  <usage surface="text">
3
3
  <do>Body copy, helpers, and labels — semantic `tone` before literal `color` or raw theme vars</do>
4
+ <do>Readable UI measure: aim ~10–12 words per line — `maxWidth` / Stack `maxWidth` **scales with `size`** (smaller type → narrower column; larger → wider); prefer **`ch`** in patterns/examples (not `rem`); never one fixed ch for every size; no full-bleed wrapping blurbs</do>
5
+ <do>Match `lineHeight` to size/context (`m` default on root; `l` when open; `s` for dense chrome); omit `lineHeight`/`size` on nested Text so the outer Text owns type</do>
6
+ <do>Prefer `balanced` on short wrapping paragraphs (hero lead, empty state, card blurb); omit when single-line or intentional ragged</do>
7
+ <do>Omit `align` so copy inherits parent `text-align`; pass `align` only to override</do>
4
8
  <do>Card / settings section blurb: Stack `rowGap` + Title heading + muted `size=&quot;s&quot;` Text — no invent CSS gap</do>
5
9
  <do>Settings row: Stack `direction=&quot;row&quot;` + Text label + Switch/Checkbox (or nest Text inside Switch `label`) — Surface for chrome</do>
6
10
  <do>Label/value metric rows: Stack row `hAlign=&quot;space-between&quot;` + muted Text label + `family=&quot;mono&quot;` Text value</do>
7
11
  <do>Loading placeholder copy: nest Shimmer inside Text (parent region owns `aria-busy`) — not Skeleton for final-dimension text</do>
8
12
  <do>Hero / status motion: nest Typewriter inside Text when body tone/size should drive the typed line</do>
9
13
  <do>Ellipsis: ClampText `render={&lt;Text … /&gt;}` — FitText when copy must scale to fill width instead of truncate</do>
10
- <do>Letter-spacing via `tracking`; text-transform via `transform`; width via `maxWidth` or parent Stack `fullWidth`</do>
11
- <do>Omit `align` so copy inherits parent `text-align`; pass `align` only to override</do>
14
+ <do>Letter-spacing via `tracking`; text-transform via `transform`; width via `maxWidth` or parent Stack `fullWidth`/`maxWidth`</do>
12
15
  <do>Inline prose link: `Text render={&lt;a href=… /&gt;}` — keeps preflight link chrome (no `data-no-link-style`)</do>
16
+ <do>Long articles / docs / privacy walls → Prose (not Text alone for heading-aware rhythm); still keep a reading column</do>
13
17
  <dont>Using Text for headings — use Title (`render` for h1–h6)</dont>
14
18
  <dont>Putting `data-no-link-style` on Text-as-a when the default underline hover is wanted</dont>
15
- <dont>Inventing CSS `letter-spacing` / `text-transform` / `width: 100%` / `paddingBlock*` / `paddingInline*` when `tracking` / `transform` / Stack `fullWidth` / `hPadding`/`vPadding` tuples cover it</dont>
19
+ <dont>Inventing CSS `letter-spacing` / `text-transform` / `line-height` / `max-width` / `text-wrap: balance` / `width: 100%` / `paddingBlock*` / `paddingInline*` when `tracking` / `transform` / `lineHeight` / `maxWidth` / `balanced` / Stack `fullWidth` / `hPadding`/`vPadding` tuples cover it</dont>
16
20
  <dont>Setting body color with raw `--global-*` when `tone` covers the role</dont>
21
+ <dont>Full-bleed marketing/UI paragraphs across a wide shell when a ~10–12-word measure fits</dont>
22
+ <dont>Reusing a large-body shell (`~65ch`–`75ch` / wide rem) for small `size` copy — column width must track `size`</dont>
17
23
  </usage>
@@ -104,7 +104,7 @@
104
104
  aria-label="Invite link"
105
105
  defaultValue="https://app.co/invite/x8f2k"
106
106
  endAddon={
107
- <IconButton aria-label="Copy invite link" icon={<ArrowUpRight size={16} />} size="s" variant="secondary" type="button" />
107
+ <IconButton aria-label="Copy invite link" icon={<ArrowUpRight />} size="s" variant="secondary" type="button" />
108
108
  }
109
109
  />
110
110
  </Stack>
@@ -119,7 +119,7 @@
119
119
  type="search"
120
120
  aria-label="Search holdings or tickers"
121
121
  placeholder="Search holdings or tickers..."
122
- startAddon={<Magnifier size={16} />}
122
+ startAddon={<Magnifier />}
123
123
  />
124
124
  <Stack columnGap="xs" direction="row">
125
125
  <Button variant="secondary">Stocks</Button>
@@ -156,7 +156,7 @@
156
156
  aria-label="Repository clone URL"
157
157
  readOnly
158
158
  value="https://github.com/vira-soft/vira-ui.git"
159
- endAddon={<Duplicate size={16} />}
159
+ endAddon={<Duplicate />}
160
160
  />
161
161
  <Text tone="muted">Clone using the web URL.</Text>
162
162
  </Stack>
@@ -22,7 +22,7 @@
22
22
  <description>Helper copy rendered with `Field.Description` and `Text` while the field is valid.</description>
23
23
  </prop>
24
24
  <prop name="endAddon" kind="react">
25
- <description>Trailing decoration (icon, button). Buttons must use `type=&quot;button&quot;`.</description>
25
+ <description>Trailing decoration (icon, button). Buttons must use `type=&quot;button&quot;`. Omit glyph `size` / width / height on icons unless expressly overriding; IconButton/Button hosts own SVG size via CSS.</description>
26
26
  </prop>
27
27
  <prop name="error" kind="react">
28
28
  <description>Error copy rendered with `Field.Error` and `Text` in the same slot as `description`. Replaces description while the field is invalid and dirty. Validation messages from `validate` are shown by `Field.Error` even when this prop is omitted (also requires dirty).</description>
@@ -37,7 +37,7 @@
37
37
  <description>Visible label rendered with shared internal `FieldLabel`.</description>
38
38
  </prop>
39
39
  <prop name="startAddon" kind="react">
40
- <description>Leading decoration (prefix text, icon). Not a second field control.</description>
40
+ <description>Leading decoration (prefix text, icon). Not a second field control. Omit glyph `size` / width / height on icons unless expressly overriding the icon-library default.</description>
41
41
  </prop>
42
42
  <prop name="control-group-background" kind="css">
43
43
  <description>Textfield control group background.</description>
@@ -5,12 +5,13 @@
5
5
  <do>Several related Textfields under one Fieldset legend (e.g. billing) — each field keeps its own `label`</do>
6
6
  <do>Equal-width form rows (city/ZIP, email + Select): parent Stack `direction=&quot;row&quot;` + `expandChildren` — not width CSS on children</do>
7
7
  <do>Span the form column with Textfield `fullWidth` — open props before inventing width CSS</do>
8
- <do>`startAddon` / `endAddon` for prefix text, icons, or actions inside the control group — IconButton/Button addons use `size=&quot;s&quot;` and `type=&quot;button&quot;`</do>
8
+ <do>`startAddon` / `endAddon` for prefix text, icons, or actions inside the control group — IconButton/Button addons use control `size=&quot;s&quot;` and `type=&quot;button&quot;`; omit glyph `size` / width / height on icons (library default)</do>
9
9
  <do>Label, description, and error copy with validation states</do>
10
10
  <do>Compose the control inside another Field with `controlOnly` — native `size` / `fitContent` still apply; skip a second Field.Root</do>
11
11
  <dont>Multi-line entry — use Textarea</dont>
12
12
  <dont>Inventing `width: 100%` / `w-full` CSS on the Textfield root when `fullWidth` covers it</dont>
13
13
  <dont>Passing visual size or variant props — chrome is fixed medium. Native HTML `size` on the input is allowed</dont>
14
+ <dont>Passing `size={16}` / width / height on every addon icon — prefer icon-library default; IconButton/Button hosts own glyph size via CSS</dont>
14
15
  <dont>Expecting a focus ring when focus is only on addon controls</dont>
15
16
  <dont>Wrapping a lone Textfield in Fieldset when its own label is enough</dont>
16
17
  </usage>
@@ -1,11 +1,12 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
2
  <meta id="title" title="Title" category="typography" shared_contract="title">
3
- <summary>Theme-aware heading and display typography primitive.</summary>
3
+ <summary>Theme-aware heading and display typography with readable-measure props (`maxWidth` / `lineHeight` / `balanced`).</summary>
4
4
  <exports>
5
5
  <export name="Title" />
6
6
  </exports>
7
7
  <related>
8
8
  <ref id="text" />
9
+ <ref id="prose" />
9
10
  <ref id="clamp-text" />
10
11
  </related>
11
12
  <units>
@@ -1,14 +1,15 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
2
  <patterns surface="title">
3
3
  <!-- Sources: viraui-app + design-kitchen preview-grid — cleaned (no handlers, no i18n). -->
4
+ <!-- Type measure: prefer maxWidth in `ch` (not rem) — scaled to size for ~10–12 words/line. -->
4
5
 
5
- <example title="Auth hero heading">
6
+ <example title="Auth hero heading (readable measure)">
6
7
  <tsx><![CDATA[
7
- <Title size="1" render={<h1 />} maxWidth="32rem" lineHeight="s">
8
+ <Title size="1" render={<h1 />} maxWidth="36ch" lineHeight="s" balanced>
8
9
  Ship real UI at the speed of thought.
9
10
  </Title>
10
11
  ]]></tsx>
11
- <notes>From `auth/login-view`. Pair `size` with `render={<h1 />}` — visual preset and heading semantics stay intentional. (Live app nests Typewriter inside; see typewriter patterns.)</notes>
12
+ <notes>From `auth/login-view`. Pair `size` with `render={<h1 />}`; cap measure in `ch`; `lineHeight="s"` + `balanced` on wrapping display. Prefer `ch` over rem. (Live app nests Typewriter inside; see typewriter patterns.)</notes>
12
13
  </example>
13
14
 
14
15
  <example title="Card section heading">
@@ -17,21 +18,21 @@
17
18
  <Title size="6" render={<h2 />}>
18
19
  Preferences
19
20
  </Title>
20
- <Text size="s" maxWidth="18rem" tone="muted">
21
+ <Text size="s" maxWidth="45ch" tone="muted" balanced>
21
22
  Manage your account settings and notifications.
22
23
  </Text>
23
24
  </Stack>
24
25
  ]]></tsx>
25
- <notes>From preview-grid `preferences` (+ payout-threshold, notifications, …). Dense card titles use `size="6"` + `h2`.</notes>
26
+ <notes>From preview-grid `preferences` (+ payout-threshold, notifications, …). Dense card titles use `size="6"` + `h2`; blurb keeps `ch` measure + `balanced`.</notes>
26
27
  </example>
27
28
 
28
29
  <example title="Empty-state heading">
29
30
  <tsx><![CDATA[
30
- <Title align="center" size="5" render={<h3 />}>
31
+ <Title align="center" size="5" render={<h3 />} balanced>
31
32
  No Team Members
32
33
  </Title>
33
34
  ]]></tsx>
34
- <notes>From preview-grid `empty-team`. Centered mid-size heading; pair with muted Text below.</notes>
35
+ <notes>From preview-grid `empty-team`. Centered mid-size heading; prefer `balanced` when the title wraps; pair with muted Text below.</notes>
35
36
  </example>
36
37
 
37
38
  <example title="Display metric">
@@ -45,7 +46,7 @@
45
46
  </Text>
46
47
  </Stack>
47
48
  ]]></tsx>
48
- <notes>From preview-grid `daily-activity-goal`. `size="display"` for hero numerals; `whiteSpace="nowrap"` keeps metric on one line.</notes>
49
+ <notes>From preview-grid `daily-activity-goal`. `size="display"` for hero numerals; `whiteSpace="nowrap"` keeps metric on one line — skip `balanced`.</notes>
49
50
  </example>
50
51
 
51
52
  <example title="Balance display">
@@ -68,11 +69,11 @@
68
69
  <Title size="4" render={<h3 />}>
69
70
  Colors
70
71
  </Title>
71
- <Text size="s" tone="muted">
72
+ <Text size="s" tone="muted" maxWidth="48ch" balanced>
72
73
  Preview of theme voice, contrast, and color rhythm across accents.
73
74
  </Text>
74
75
  </Stack>
75
76
  ]]></tsx>
76
- <notes>From design-kitchen `theme-preview` (+ `base-theme-selector` label scale). Mid panel titles use `size="4"` + `h3`.</notes>
77
+ <notes>From design-kitchen `theme-preview` (+ `base-theme-selector` label scale). Mid panel titles use `size="4"` + `h3`; supporting Text keeps `ch` measure.</notes>
77
78
  </example>
78
79
  </patterns>
@@ -4,19 +4,20 @@
4
4
  <item>Headings and display text with title presets</item>
5
5
  <item>Semantic heading tags chosen explicitly through render</item>
6
6
  <item>Size and render paired together for most heading use cases</item>
7
+ <item>Readable wrapping titles: `maxWidth` (~10–12 words/line, **scaled to `size`** — smaller → narrower), `lineHeight` (`s` on large wrapping / inside Prose), and `balanced` on short multi-line heads</item>
7
8
  <item>Letter-spacing via `tracking` or text-transform via `transform` — prefer props over CSS on the Title root</item>
8
9
  <item>Fluid clamp sizing across title appearance presets (theme `--effect-fluid-typography` or per-instance `fluid`; off → fixed at --font-scale-display / h-* for that size)</item>
9
10
  </when_to_use>
10
11
  <when_not_to_use>
11
12
  <item>Body copy — use Text</item>
12
13
  <item>Body or mono family typography</item>
13
- <item>Inventing CSS `letter-spacing` / `text-transform` / `width: 100%` when `tracking` / `transform` / parent Stack `fullWidth` cover the need</item>
14
+ <item>Inventing CSS `letter-spacing` / `text-transform` / `line-height` / `max-width` / `text-wrap: balance` / `width: 100%` when `tracking` / `transform` / `lineHeight` / `maxWidth` / `balanced` / parent Stack `fullWidth` cover the need</item>
14
15
  </when_not_to_use>
15
16
  <prop name="align" kind="react">
16
17
  <description>Aligns text using logical alignment values. Omit so `text-align` inherits from the parent.</description>
17
18
  </prop>
18
19
  <prop name="balanced" kind="react">
19
- <description>Enables balanced wrapping for block-like titles.</description>
20
+ <description>Enables balanced wrapping for block-like titles. Prefer on short wrapping heads (hero, empty state, section); omit for single-line / `whiteSpace=&quot;nowrap&quot;` metrics.</description>
20
21
  </prop>
21
22
  <prop name="color" kind="react">
22
23
  <description>Applies a literal color escape hatch.</description>
@@ -31,10 +32,10 @@
31
32
  <description>Sets inline padding using one token for both sides or a `[start, end]` tuple (`0` allowed). Prefer the tuple for one-sided inset — never `style`/`paddingInline*` CSS when this prop covers it.</description>
32
33
  </prop>
33
34
  <prop name="lineHeight" kind="react">
34
- <description>Controls line height through foundation leading tokens.</description>
35
+ <description>Controls line height through foundation leading tokens. Default `m`; prefer `s` on large wrapping titles and always inside Prose. Prefer this prop over CSS `line-height`.</description>
35
36
  </prop>
36
37
  <prop name="maxWidth" kind="react">
37
- <description>Constrains the maximum inline size of the rendered text block.</description>
38
+ <description>Constrains the maximum inline size of the rendered text block. Prefer for readable wrapping titles (~10–12 words/line) over CSS `max-width`. Prefer **`ch`** (not `rem`) in patterns/examples. Width tracks `size` — smaller presets → narrower cap; never one fixed measure for every Title size. Shared columns may use parent Stack `maxWidth` instead.</description>
38
39
  </prop>
39
40
  <prop name="opticalSize" kind="react">
40
41
  <description>Overrides the optical size (`opsz`) axis on variable fonts; static fonts keep `font-optical-sizing: auto`.</description>
@@ -1,15 +1,20 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
2
  <usage surface="title">
3
3
  <do>Headings and display type — pair `size` (visual preset) with `render={&lt;h1 /&gt;}`…`h6` for outline semantics (default root is `span`)</do>
4
+ <do>Readable measure on wrapping titles: aim ~10–12 words per line — `maxWidth` / Stack `maxWidth` **scales with `size`** (smaller → narrower; larger → wider); prefer **`ch`** (not `rem`) in patterns/examples; large wrapping presets often `lineHeight=&quot;s&quot;`</do>
5
+ <do>Prefer `balanced` on short wrapping titles (hero, empty state, section head); omit when single-line / `whiteSpace=&quot;nowrap&quot;` metrics</do>
6
+ <do>Omit `align` so the heading inherits parent `text-align`; pass `align` only to override</do>
7
+ <do>Title inside Prose → `lineHeight=&quot;s&quot;`; long-form rhythm stays on Prose — not Stack `rowGap`</do>
4
8
  <do>Card / panel header: Stack `rowGap=&quot;xs&quot;` + Title + muted Text blurb — Surface for card chrome</do>
5
9
  <do>Auth / hero cycle: nest Typewriter inside Title (`preserveSpace`, caret props) so display preset + heading tag stay on the parent</do>
6
10
  <do>Loading title copy: nest Shimmer inside Title — parent region owns busy semantics</do>
7
11
  <do>Truncation: ClampText `render={&lt;Title size=… render={&lt;h* /&gt;} /&gt;}` — FitText wraps Title when short headlines must fill a constrained width</do>
8
12
  <do>Fluid clamp across presets (`fluid` / theme `--effect-fluid-typography`); pass `fluid={false}` inside chrome or dense UI</do>
9
- <do>Letter-spacing via `tracking`; text-transform via `transform`; width via `maxWidth` or parent Stack `fullWidth`</do>
10
- <do>Omit `align` so the heading inherits parent `text-align`; pass `align` only to override</do>
13
+ <do>Letter-spacing via `tracking`; text-transform via `transform`; width via `maxWidth` or parent Stack `fullWidth`/`maxWidth`</do>
11
14
  <do>Title-as-prose-link (`render={&lt;a /&gt;}`) keeps preflight link chrome — omit `data-no-link-style`</do>
12
15
  <dont>Assuming `size` changes the HTML tag — choose `render` explicitly</dont>
13
16
  <dont>Using Title for long body paragraphs — use Text</dont>
14
- <dont>Inventing CSS `letter-spacing` / `text-transform` / `width: 100%` / `paddingBlock*` / `paddingInline*` when props / Stack / `hPadding`/`vPadding` tuples cover it</dont>
17
+ <dont>Inventing CSS `letter-spacing` / `text-transform` / `line-height` / `max-width` / `text-wrap: balance` / `width: 100%` / `paddingBlock*` / `paddingInline*` when props / Stack / `hPadding`/`vPadding` tuples cover it</dont>
18
+ <dont>Full-bleed wrapping headlines across a wide shell when a capped measure + `balanced` fits</dont>
19
+ <dont>One fixed wide measure for every Title `size` — smaller presets need a narrower column for ~10–12 words/line</dont>
15
20
  </usage>
@@ -22,8 +22,8 @@
22
22
  render={
23
23
  <ToggleButton
24
24
  aria-label="Lock in shuffle"
25
- restingIcon={<LockOpen2 aria-hidden size="1rem" />}
26
- pressedIcon={<Lock aria-hidden size="1rem" color="var(--global-primary)" />}
25
+ restingIcon={<LockOpen2 aria-hidden />}
26
+ pressedIcon={<Lock aria-hidden color="var(--global-primary)" />}
27
27
  variant="flat"
28
28
  pressedVariant="secondary"
29
29
  />
@@ -43,7 +43,7 @@
43
43
  <ToggleButton
44
44
  aria-label="Sync light and dark"
45
45
  size="s"
46
- restingIcon={<DarkLight aria-hidden size="1rem" />}
46
+ restingIcon={<DarkLight aria-hidden />}
47
47
  variant="flat"
48
48
  pressedVariant="primary"
49
49
  />
@@ -60,8 +60,8 @@
60
60
  <Badge show color="yellow">
61
61
  <ToggleButton
62
62
  aria-label="Mute notifications"
63
- restingIcon={<BellSlash aria-hidden size={16} />}
64
- pressedIcon={<BellOn aria-hidden size={16} />}
63
+ restingIcon={<BellSlash aria-hidden />}
64
+ pressedIcon={<BellOn aria-hidden />}
65
65
  size="s"
66
66
  interpolateIcons
67
67
  variant="secondary"
@@ -77,7 +77,7 @@
77
77
  <ToggleButton
78
78
  value="light"
79
79
  aria-label="Light"
80
- restingIcon={<Sun aria-hidden size="1rem" />}
80
+ restingIcon={<Sun aria-hidden />}
81
81
  variant="flat"
82
82
  size="s"
83
83
  pressedVariant="secondary"
@@ -85,7 +85,7 @@
85
85
  <ToggleButton
86
86
  value="dark"
87
87
  aria-label="Dark"
88
- restingIcon={<MoonStars aria-hidden size="1rem" />}
88
+ restingIcon={<MoonStars aria-hidden />}
89
89
  variant="flat"
90
90
  size="s"
91
91
  pressedVariant="secondary"
@@ -93,7 +93,7 @@
93
93
  <ToggleButton
94
94
  value="system"
95
95
  aria-label="System"
96
- restingIcon={<DarkLight aria-hidden size="1rem" />}
96
+ restingIcon={<DarkLight aria-hidden />}
97
97
  variant="flat"
98
98
  size="s"
99
99
  pressedVariant="secondary"
@@ -20,13 +20,13 @@
20
20
  <description>Applies a fully rounded pill shape to the button.</description>
21
21
  </prop>
22
22
  <prop name="pressedIcon" kind="react">
23
- <description>Icon shown while the toggle is pressed.</description>
23
+ <description>Icon shown while the toggle is pressed. Omit glyph `size` / width / height — host CSS sizes SVG via `--vui-button-icon-size`.</description>
24
24
  </prop>
25
25
  <prop name="pressedVariant" kind="react">
26
26
  <description>Visual variant while the toggle is pressed.</description>
27
27
  </prop>
28
28
  <prop name="restingIcon" kind="react">
29
- <description>Icon shown while the toggle is not pressed.</description>
29
+ <description>Icon shown while the toggle is not pressed. Omit glyph `size` / width / height — host CSS sizes SVG via `--vui-button-icon-size`.</description>
30
30
  </prop>
31
31
  <prop name="size" kind="react">
32
32
  <description>Controls the visual button size.</description>
@@ -44,7 +44,7 @@
44
44
  <description>Button block size; size prop presets via data attributes.</description>
45
45
  </prop>
46
46
  <prop name="icon-size" kind="css">
47
- <description>Addon icon size inside the button.</description>
47
+ <description>Addon icon size inside the button (`--vui-button-icon-size`). Prefer this over setting `size` on the glyph ReactNode.</description>
48
48
  </prop>
49
49
  <prop name="padding-block" kind="css">
50
50
  <description>Vertical padding inside the button.</description>
@@ -3,12 +3,14 @@
3
3
  <do>Toolbar pin, favorite, mute, or lock toggles identified only by icon</do>
4
4
  <do>Compact on/off controls that share IconButton size, variant, and loading model</do>
5
5
  <do>Two-state actions where pressed state should swap the visible glyph via `restingIcon` / `pressedIcon`</do>
6
+ <do>Omit `size` / width / height on `restingIcon` / `pressedIcon` glyphs — host CSS sizes SVG via `--vui-button-icon-size`; set glyph size only to expressly override</do>
6
7
  <do>Temporal pressed feedback (e.g. copy) with optional `interpolateIcons` crossfade</do>
7
8
  <do>Segmented toolbars: nest ToggleButtons with `value` inside ToggleGroup; label each control and the group</do>
8
9
  <do>Optional chrome: wrap with Tooltip.Trigger / Badge — keep the accessible name on the ToggleButton</do>
9
10
  <dont>Using IconButton for persistent or temporal pressed toolbar state</dont>
10
11
  <dont>Using Switch for compact icon-only toolbar toggles</dont>
11
12
  <dont>Passing `icon` or `render` — ToggleButton owns icon swap and Toggle render wiring; use `restingIcon` / `pressedIcon`</dont>
13
+ <dont>Passing `size={16}` / `size=&quot;1rem&quot;` (or width/height) on every icon slot glyph — ToggleButton CSS sizes SVG descendants</dont>
12
14
  <dont>Omitting `value` on ToggleButton children inside a ToggleGroup</dont>
13
15
  <dont>Omitting both `aria-label` and `aria-labelledby`</dont>
14
16
  </usage>
@@ -9,7 +9,7 @@
9
9
  <ToggleButton
10
10
  value="light"
11
11
  aria-label="Light"
12
- restingIcon={<Sun aria-hidden size="1rem" />}
12
+ restingIcon={<Sun aria-hidden />}
13
13
  variant="flat"
14
14
  size="s"
15
15
  pressedVariant="secondary"
@@ -17,7 +17,7 @@
17
17
  <ToggleButton
18
18
  value="dark"
19
19
  aria-label="Dark"
20
- restingIcon={<MoonStars aria-hidden size="1rem" />}
20
+ restingIcon={<MoonStars aria-hidden />}
21
21
  variant="flat"
22
22
  size="s"
23
23
  pressedVariant="secondary"
@@ -25,7 +25,7 @@
25
25
  <ToggleButton
26
26
  value="system"
27
27
  aria-label="System"
28
- restingIcon={<DarkLight aria-hidden size="1rem" />}
28
+ restingIcon={<DarkLight aria-hidden />}
29
29
  variant="flat"
30
30
  size="s"
31
31
  pressedVariant="secondary"