@fragments-sdk/ui 0.11.1 → 0.13.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 (303) hide show
  1. package/README.md +15 -0
  2. package/dist/assets/ui.css +25 -18
  3. package/dist/blocks/AccountSettings.block.d.ts +1 -1
  4. package/dist/blocks/ActivityFeed.block.d.ts +1 -1
  5. package/dist/blocks/ActivityFeedSkeleton.block.d.ts +1 -1
  6. package/dist/blocks/BlogEditor.block.d.ts +1 -1
  7. package/dist/blocks/ChatInterface.block.d.ts +1 -1
  8. package/dist/blocks/ChatMessages.block.d.ts +1 -1
  9. package/dist/blocks/CheckoutForm.block.d.ts +1 -1
  10. package/dist/blocks/CommandPalette.block.d.ts +1 -1
  11. package/dist/blocks/ContactForm.block.d.ts +1 -1
  12. package/dist/blocks/DashboardLayout.block.d.ts +1 -1
  13. package/dist/blocks/DashboardPage.block.d.ts +1 -1
  14. package/dist/blocks/DashboardSkeleton.block.d.ts +1 -1
  15. package/dist/blocks/DataTable.block.d.ts +1 -1
  16. package/dist/blocks/EmptyState.block.d.ts +1 -1
  17. package/dist/blocks/FAQSection.block.d.ts +1 -1
  18. package/dist/blocks/FeatureGrid.block.d.ts +1 -1
  19. package/dist/blocks/HeroSection.block.d.ts +1 -1
  20. package/dist/blocks/LoginForm.block.d.ts +1 -1
  21. package/dist/blocks/NavigationHeader.block.d.ts +1 -1
  22. package/dist/blocks/PaginatedTable.block.d.ts +1 -1
  23. package/dist/blocks/PricingComparison.block.d.ts +1 -1
  24. package/dist/blocks/ProductCard.block.d.ts +1 -1
  25. package/dist/blocks/RegistrationForm.block.d.ts +1 -1
  26. package/dist/blocks/SettingsDrawer.block.d.ts +1 -1
  27. package/dist/blocks/SettingsPanel.block.d.ts +1 -1
  28. package/dist/blocks/ShoppingCart.block.d.ts +1 -1
  29. package/dist/blocks/StatsCard.block.d.ts +1 -1
  30. package/dist/blocks/StatsCardSkeleton.block.d.ts +1 -1
  31. package/dist/blocks/TableSkeleton.block.d.ts +1 -1
  32. package/dist/blocks/ThinkingStates.block.d.ts +1 -1
  33. package/dist/codeblock.cjs +7 -1
  34. package/dist/codeblock.cjs.map +1 -1
  35. package/dist/codeblock.js +7 -1
  36. package/dist/codeblock.js.map +1 -1
  37. package/dist/components/Accordion/index.cjs +11 -4
  38. package/dist/components/Accordion/index.cjs.map +1 -1
  39. package/dist/components/Accordion/index.d.ts +3 -3
  40. package/dist/components/Accordion/index.d.ts.map +1 -1
  41. package/dist/components/Accordion/index.js +11 -4
  42. package/dist/components/Accordion/index.js.map +1 -1
  43. package/dist/components/Alert/index.cjs.map +1 -1
  44. package/dist/components/Alert/index.d.ts +7 -0
  45. package/dist/components/Alert/index.d.ts.map +1 -1
  46. package/dist/components/Alert/index.js.map +1 -1
  47. package/dist/components/Avatar/index.cjs.map +1 -1
  48. package/dist/components/Avatar/index.d.ts +4 -0
  49. package/dist/components/Avatar/index.d.ts.map +1 -1
  50. package/dist/components/Avatar/index.js.map +1 -1
  51. package/dist/components/Badge/index.cjs.map +1 -1
  52. package/dist/components/Badge/index.d.ts +12 -0
  53. package/dist/components/Badge/index.d.ts.map +1 -1
  54. package/dist/components/Badge/index.js.map +1 -1
  55. package/dist/components/Button/index.cjs +9 -1
  56. package/dist/components/Button/index.cjs.map +1 -1
  57. package/dist/components/Button/index.d.ts +14 -1
  58. package/dist/components/Button/index.d.ts.map +1 -1
  59. package/dist/components/Button/index.js +9 -1
  60. package/dist/components/Button/index.js.map +1 -1
  61. package/dist/components/Card/index.cjs +2 -1
  62. package/dist/components/Card/index.cjs.map +1 -1
  63. package/dist/components/Card/index.d.ts +12 -2
  64. package/dist/components/Card/index.d.ts.map +1 -1
  65. package/dist/components/Card/index.js +2 -1
  66. package/dist/components/Card/index.js.map +1 -1
  67. package/dist/components/Checkbox/index.cjs.map +1 -1
  68. package/dist/components/Checkbox/index.d.ts +6 -1
  69. package/dist/components/Checkbox/index.d.ts.map +1 -1
  70. package/dist/components/Checkbox/index.js.map +1 -1
  71. package/dist/components/Chip/index.cjs +2 -1
  72. package/dist/components/Chip/index.cjs.map +1 -1
  73. package/dist/components/Chip/index.d.ts +10 -3
  74. package/dist/components/Chip/index.d.ts.map +1 -1
  75. package/dist/components/Chip/index.js +2 -1
  76. package/dist/components/Chip/index.js.map +1 -1
  77. package/dist/components/CodeBlock/index.d.ts +1 -1
  78. package/dist/components/CodeBlock/index.d.ts.map +1 -1
  79. package/dist/components/Collapsible/index.cjs +45 -10
  80. package/dist/components/Collapsible/index.cjs.map +1 -1
  81. package/dist/components/Collapsible/index.d.ts +6 -12
  82. package/dist/components/Collapsible/index.d.ts.map +1 -1
  83. package/dist/components/Collapsible/index.js +45 -10
  84. package/dist/components/Collapsible/index.js.map +1 -1
  85. package/dist/components/Combobox/index.cjs +18 -9
  86. package/dist/components/Combobox/index.cjs.map +1 -1
  87. package/dist/components/Combobox/index.d.ts +8 -12
  88. package/dist/components/Combobox/index.d.ts.map +1 -1
  89. package/dist/components/Combobox/index.js +18 -9
  90. package/dist/components/Combobox/index.js.map +1 -1
  91. package/dist/components/Command/index.cjs +54 -21
  92. package/dist/components/Command/index.cjs.map +1 -1
  93. package/dist/components/Command/index.d.ts +2 -2
  94. package/dist/components/Command/index.d.ts.map +1 -1
  95. package/dist/components/Command/index.js +54 -21
  96. package/dist/components/Command/index.js.map +1 -1
  97. package/dist/components/DataTable/index.cjs +13 -1
  98. package/dist/components/DataTable/index.cjs.map +1 -1
  99. package/dist/components/DataTable/index.d.ts.map +1 -1
  100. package/dist/components/DataTable/index.js +13 -1
  101. package/dist/components/DataTable/index.js.map +1 -1
  102. package/dist/components/DatePicker/index.d.ts +2 -3
  103. package/dist/components/DatePicker/index.d.ts.map +1 -1
  104. package/dist/components/Dialog/index.cjs +12 -9
  105. package/dist/components/Dialog/index.cjs.map +1 -1
  106. package/dist/components/Dialog/index.d.ts +20 -12
  107. package/dist/components/Dialog/index.d.ts.map +1 -1
  108. package/dist/components/Dialog/index.js +12 -9
  109. package/dist/components/Dialog/index.js.map +1 -1
  110. package/dist/components/Drawer/index.cjs +12 -9
  111. package/dist/components/Drawer/index.cjs.map +1 -1
  112. package/dist/components/Drawer/index.d.ts +22 -12
  113. package/dist/components/Drawer/index.d.ts.map +1 -1
  114. package/dist/components/Drawer/index.js +12 -9
  115. package/dist/components/Drawer/index.js.map +1 -1
  116. package/dist/components/Grid/index.cjs +4 -1
  117. package/dist/components/Grid/index.cjs.map +1 -1
  118. package/dist/components/Grid/index.d.ts +6 -2
  119. package/dist/components/Grid/index.d.ts.map +1 -1
  120. package/dist/components/Grid/index.js +4 -1
  121. package/dist/components/Grid/index.js.map +1 -1
  122. package/dist/components/Input/index.cjs.map +1 -1
  123. package/dist/components/Input/index.d.ts +15 -1
  124. package/dist/components/Input/index.d.ts.map +1 -1
  125. package/dist/components/Input/index.js.map +1 -1
  126. package/dist/components/Menu/index.cjs +30 -16
  127. package/dist/components/Menu/index.cjs.map +1 -1
  128. package/dist/components/Menu/index.d.ts +17 -25
  129. package/dist/components/Menu/index.d.ts.map +1 -1
  130. package/dist/components/Menu/index.js +30 -16
  131. package/dist/components/Menu/index.js.map +1 -1
  132. package/dist/components/NavigationMenu/NavigationMenuContext.cjs.map +1 -1
  133. package/dist/components/NavigationMenu/NavigationMenuContext.d.ts +1 -0
  134. package/dist/components/NavigationMenu/NavigationMenuContext.d.ts.map +1 -1
  135. package/dist/components/NavigationMenu/NavigationMenuContext.js.map +1 -1
  136. package/dist/components/NavigationMenu/index.cjs +43 -11
  137. package/dist/components/NavigationMenu/index.cjs.map +1 -1
  138. package/dist/components/NavigationMenu/index.d.ts.map +1 -1
  139. package/dist/components/NavigationMenu/index.js +43 -11
  140. package/dist/components/NavigationMenu/index.js.map +1 -1
  141. package/dist/components/NavigationMenu/useNavigationMenu.cjs +2 -0
  142. package/dist/components/NavigationMenu/useNavigationMenu.cjs.map +1 -1
  143. package/dist/components/NavigationMenu/useNavigationMenu.d.ts +1 -0
  144. package/dist/components/NavigationMenu/useNavigationMenu.d.ts.map +1 -1
  145. package/dist/components/NavigationMenu/useNavigationMenu.js +2 -0
  146. package/dist/components/NavigationMenu/useNavigationMenu.js.map +1 -1
  147. package/dist/components/Popover/index.cjs +11 -10
  148. package/dist/components/Popover/index.cjs.map +1 -1
  149. package/dist/components/Popover/index.d.ts +17 -12
  150. package/dist/components/Popover/index.d.ts.map +1 -1
  151. package/dist/components/Popover/index.js +11 -10
  152. package/dist/components/Popover/index.js.map +1 -1
  153. package/dist/components/RadioGroup/index.cjs.map +1 -1
  154. package/dist/components/RadioGroup/index.d.ts +4 -0
  155. package/dist/components/RadioGroup/index.d.ts.map +1 -1
  156. package/dist/components/RadioGroup/index.js.map +1 -1
  157. package/dist/components/Select/index.cjs +7 -6
  158. package/dist/components/Select/index.cjs.map +1 -1
  159. package/dist/components/Select/index.d.ts +20 -9
  160. package/dist/components/Select/index.d.ts.map +1 -1
  161. package/dist/components/Select/index.js +7 -6
  162. package/dist/components/Select/index.js.map +1 -1
  163. package/dist/components/Sidebar/index.cjs +71 -24
  164. package/dist/components/Sidebar/index.cjs.map +1 -1
  165. package/dist/components/Sidebar/index.d.ts +21 -33
  166. package/dist/components/Sidebar/index.d.ts.map +1 -1
  167. package/dist/components/Sidebar/index.js +71 -24
  168. package/dist/components/Sidebar/index.js.map +1 -1
  169. package/dist/components/Slider/index.cjs +3 -1
  170. package/dist/components/Slider/index.cjs.map +1 -1
  171. package/dist/components/Slider/index.d.ts +10 -0
  172. package/dist/components/Slider/index.d.ts.map +1 -1
  173. package/dist/components/Slider/index.js +3 -1
  174. package/dist/components/Slider/index.js.map +1 -1
  175. package/dist/components/Stack/index.cjs +6 -0
  176. package/dist/components/Stack/index.cjs.map +1 -1
  177. package/dist/components/Stack/index.d.ts +12 -6
  178. package/dist/components/Stack/index.d.ts.map +1 -1
  179. package/dist/components/Stack/index.js +6 -0
  180. package/dist/components/Stack/index.js.map +1 -1
  181. package/dist/components/Tabs/index.cjs.map +1 -1
  182. package/dist/components/Tabs/index.d.ts +13 -1
  183. package/dist/components/Tabs/index.d.ts.map +1 -1
  184. package/dist/components/Tabs/index.js.map +1 -1
  185. package/dist/components/Text/Text.module.scss.cjs +44 -32
  186. package/dist/components/Text/Text.module.scss.cjs.map +1 -1
  187. package/dist/components/Text/Text.module.scss.js +44 -32
  188. package/dist/components/Text/Text.module.scss.js.map +1 -1
  189. package/dist/components/Text/index.cjs.map +1 -1
  190. package/dist/components/Text/index.d.ts +18 -3
  191. package/dist/components/Text/index.d.ts.map +1 -1
  192. package/dist/components/Text/index.js.map +1 -1
  193. package/dist/components/Theme/index.cjs.map +1 -1
  194. package/dist/components/Theme/index.d.ts +12 -0
  195. package/dist/components/Theme/index.d.ts.map +1 -1
  196. package/dist/components/Theme/index.js.map +1 -1
  197. package/dist/components/Toggle/index.cjs +2 -1
  198. package/dist/components/Toggle/index.cjs.map +1 -1
  199. package/dist/components/Toggle/index.d.ts +9 -0
  200. package/dist/components/Toggle/index.d.ts.map +1 -1
  201. package/dist/components/Toggle/index.js +2 -1
  202. package/dist/components/Toggle/index.js.map +1 -1
  203. package/dist/components/ToggleGroup/index.cjs +4 -1
  204. package/dist/components/ToggleGroup/index.cjs.map +1 -1
  205. package/dist/components/ToggleGroup/index.d.ts +13 -4
  206. package/dist/components/ToggleGroup/index.d.ts.map +1 -1
  207. package/dist/components/ToggleGroup/index.js +4 -1
  208. package/dist/components/ToggleGroup/index.js.map +1 -1
  209. package/dist/components/Tooltip/index.cjs +20 -10
  210. package/dist/components/Tooltip/index.cjs.map +1 -1
  211. package/dist/components/Tooltip/index.d.ts +5 -1
  212. package/dist/components/Tooltip/index.d.ts.map +1 -1
  213. package/dist/components/Tooltip/index.js +20 -10
  214. package/dist/components/Tooltip/index.js.map +1 -1
  215. package/dist/datepicker.cjs +24 -10
  216. package/dist/datepicker.cjs.map +1 -1
  217. package/dist/datepicker.js +24 -10
  218. package/dist/datepicker.js.map +1 -1
  219. package/dist/index.cjs +4 -0
  220. package/dist/index.cjs.map +1 -1
  221. package/dist/index.d.ts.map +1 -1
  222. package/dist/index.js +4 -0
  223. package/dist/index.js.map +1 -1
  224. package/dist/utils/css-warning.cjs +18 -0
  225. package/dist/utils/css-warning.cjs.map +1 -0
  226. package/dist/utils/css-warning.d.ts +2 -0
  227. package/dist/utils/css-warning.d.ts.map +1 -0
  228. package/dist/utils/css-warning.js +18 -0
  229. package/dist/utils/css-warning.js.map +1 -0
  230. package/fragments.json +1 -1
  231. package/package.json +2 -2
  232. package/src/components/Accordion/Accordion.test.tsx +33 -0
  233. package/src/components/Accordion/index.tsx +10 -3
  234. package/src/components/Alert/index.tsx +7 -0
  235. package/src/components/Avatar/index.tsx +4 -0
  236. package/src/components/Badge/Badge.fragment.tsx +10 -2
  237. package/src/components/Badge/index.tsx +12 -0
  238. package/src/components/Button/Button.fragment.tsx +12 -2
  239. package/src/components/Button/Button.test.tsx +16 -0
  240. package/src/components/Button/index.tsx +27 -2
  241. package/src/components/Card/Card.fragment.tsx +14 -2
  242. package/src/components/Card/Card.test.tsx +5 -0
  243. package/src/components/Card/index.tsx +15 -2
  244. package/src/components/Checkbox/index.tsx +6 -1
  245. package/src/components/Chip/Chip.fragment.tsx +12 -2
  246. package/src/components/Chip/Chip.test.tsx +5 -0
  247. package/src/components/Chip/index.tsx +14 -4
  248. package/src/components/CodeBlock/index.tsx +13 -2
  249. package/src/components/Collapsible/Collapsible.test.tsx +41 -0
  250. package/src/components/Collapsible/index.tsx +53 -16
  251. package/src/components/Combobox/Combobox.test.tsx +55 -0
  252. package/src/components/Combobox/index.tsx +23 -17
  253. package/src/components/Command/Command.test.tsx +93 -0
  254. package/src/components/Command/index.tsx +61 -18
  255. package/src/components/DataTable/DataTable.test.tsx +11 -2
  256. package/src/components/DataTable/index.tsx +22 -2
  257. package/src/components/DatePicker/DatePicker.test.tsx +79 -0
  258. package/src/components/DatePicker/index.tsx +29 -14
  259. package/src/components/Dialog/Dialog.test.tsx +23 -0
  260. package/src/components/Dialog/index.tsx +27 -16
  261. package/src/components/Drawer/Drawer.test.tsx +27 -0
  262. package/src/components/Drawer/index.tsx +29 -16
  263. package/src/components/Grid/Grid.fragment.tsx +14 -2
  264. package/src/components/Grid/Grid.test.tsx +6 -0
  265. package/src/components/Grid/index.tsx +12 -3
  266. package/src/components/Input/index.tsx +15 -1
  267. package/src/components/Menu/index.tsx +35 -30
  268. package/src/components/NavigationMenu/NavigationMenu.fragment.tsx +1 -1
  269. package/src/components/NavigationMenu/NavigationMenu.test.tsx +40 -4
  270. package/src/components/NavigationMenu/NavigationMenuContext.ts +3 -0
  271. package/src/components/NavigationMenu/index.tsx +49 -13
  272. package/src/components/NavigationMenu/useNavigationMenu.ts +4 -0
  273. package/src/components/Popover/Popover.test.tsx +23 -0
  274. package/src/components/Popover/index.tsx +24 -18
  275. package/src/components/RadioGroup/index.tsx +4 -0
  276. package/src/components/Select/Select.test.tsx +41 -0
  277. package/src/components/Select/index.tsx +24 -12
  278. package/src/components/Sidebar/Sidebar.test.tsx +83 -4
  279. package/src/components/Sidebar/index.tsx +87 -45
  280. package/src/components/Slider/Slider.fragment.tsx +5 -1
  281. package/src/components/Slider/Slider.test.tsx +6 -0
  282. package/src/components/Slider/index.tsx +13 -1
  283. package/src/components/Stack/Stack.fragment.tsx +22 -2
  284. package/src/components/Stack/Stack.test.tsx +6 -0
  285. package/src/components/Stack/index.tsx +20 -6
  286. package/src/components/Tabs/index.tsx +13 -1
  287. package/src/components/Text/Text.fragment.tsx +10 -8
  288. package/src/components/Text/Text.module.scss +8 -2
  289. package/src/components/Text/Text.test.tsx +15 -0
  290. package/src/components/Text/index.tsx +18 -3
  291. package/src/components/Theme/index.tsx +12 -0
  292. package/src/components/Toggle/Toggle.fragment.tsx +5 -1
  293. package/src/components/Toggle/Toggle.test.tsx +19 -0
  294. package/src/components/Toggle/index.tsx +11 -1
  295. package/src/components/ToggleGroup/ToggleGroup.fragment.tsx +5 -2
  296. package/src/components/ToggleGroup/ToggleGroup.test.tsx +20 -0
  297. package/src/components/ToggleGroup/index.tsx +15 -4
  298. package/src/components/Tooltip/Tooltip.test.tsx +17 -0
  299. package/src/components/Tooltip/index.tsx +58 -34
  300. package/src/index.ts +6 -0
  301. package/src/tokens/_seeds.scss +5 -3
  302. package/src/tokens/_variables.scss +2 -0
  303. package/src/utils/css-warning.ts +29 -0
@@ -2,7 +2,9 @@ import * as React from 'react';
2
2
  import styles from './Stack.module.scss';
3
3
 
4
4
  type Direction = 'row' | 'column';
5
- type Gap = 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl';
5
+ type GapToken = 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl';
6
+ /** Gap accepts string tokens or numbers (1-8) mapping to the spacing scale */
7
+ type Gap = GapToken | number;
6
8
 
7
9
  /** Responsive value — either a single value or per-breakpoint overrides */
8
10
  export interface ResponsiveDirection {
@@ -21,17 +23,21 @@ export interface ResponsiveDirection {
21
23
  /** Responsive gap value */
22
24
  export interface ResponsiveGap {
23
25
  /** Default (mobile-first) */
24
- base?: Gap;
26
+ base?: GapToken;
25
27
  /** ≥640px */
26
- sm?: Gap;
28
+ sm?: GapToken;
27
29
  /** ≥768px */
28
- md?: Gap;
30
+ md?: GapToken;
29
31
  /** ≥1024px */
30
- lg?: Gap;
32
+ lg?: GapToken;
31
33
  /** ≥1280px */
32
- xl?: Gap;
34
+ xl?: GapToken;
33
35
  }
34
36
 
37
+ /**
38
+ * Flexbox layout component for vertical or horizontal stacking with consistent spacing.
39
+ * @see https://usefragments.com/components/stack
40
+ */
35
41
  export interface StackProps {
36
42
  children: React.ReactNode;
37
43
  /**
@@ -70,6 +76,10 @@ function isResponsiveGap(gap: StackProps['gap']): gap is ResponsiveGap {
70
76
  return typeof gap === 'object' && gap !== null;
71
77
  }
72
78
 
79
+ function isNumericGap(gap: StackProps['gap']): gap is number {
80
+ return typeof gap === 'number';
81
+ }
82
+
73
83
  const StackRoot = React.forwardRef<HTMLElement, StackProps>(
74
84
  function Stack(
75
85
  {
@@ -114,6 +124,10 @@ const StackRoot = React.forwardRef<HTMLElement, StackProps>(
114
124
  if (gap.lg && gap.lg !== 'none') gapVars['--fui-stack-gap-lg'] = `var(--fui-space-${gapToSpace(gap.lg)})`;
115
125
  if (gap.xl && gap.xl !== 'none') gapVars['--fui-stack-gap-xl'] = `var(--fui-space-${gapToSpace(gap.xl)})`;
116
126
  inlineStyle = { ...inlineStyle, ...gapVars } as React.CSSProperties;
127
+ } else if (isNumericGap(gap)) {
128
+ // Numeric gap: maps to space scale tokens (e.g. gap={2} → var(--fui-space-2))
129
+ gapClass = false;
130
+ inlineStyle = { ...inlineStyle, gap: `var(--fui-space-${gap})` } as React.CSSProperties;
117
131
  } else {
118
132
  gapClass = gap !== 'none' && styles[`gap-${gap}`];
119
133
  }
@@ -8,18 +8,30 @@ import styles from './Tabs.module.scss';
8
8
  // Types
9
9
  // ============================================
10
10
 
11
- export type TabValue = string | number;
11
+ export type TabValue = string;
12
12
 
13
+ /**
14
+ * Tabbed navigation for switching between content panels.
15
+ * @see https://usefragments.com/components/tabs
16
+ */
13
17
  export interface TabsProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'defaultValue'> {
14
18
  children: React.ReactNode;
19
+ /** Default active tab value (uncontrolled) */
15
20
  defaultValue?: TabValue;
21
+ /** Controlled active tab value */
16
22
  value?: TabValue;
23
+ /** Called when the active tab changes */
17
24
  onValueChange?: (value: TabValue) => void;
25
+ /** Tab layout direction.
26
+ * @default "horizontal" */
18
27
  orientation?: 'horizontal' | 'vertical';
19
28
  }
20
29
 
21
30
  export interface TabsListProps extends React.HTMLAttributes<HTMLDivElement> {
22
31
  children: React.ReactNode;
32
+ /** Tab list visual style.
33
+ * @default "underline"
34
+ * @see https://usefragments.com/components/tabs#variants */
23
35
  variant?: 'underline' | 'pills';
24
36
  }
25
37
 
@@ -53,18 +53,18 @@ export default defineFragment({
53
53
  },
54
54
  size: {
55
55
  type: 'enum',
56
- description: 'Font size',
57
- values: ['2xs', 'xs', 'sm', 'base', 'lg', 'xl', '2xl'],
56
+ description: 'Font size (md is an alias for base)',
57
+ values: ['2xs', 'xs', 'sm', 'base', 'md', 'lg', 'xl', '2xl'],
58
58
  },
59
59
  weight: {
60
60
  type: 'enum',
61
61
  description: 'Font weight',
62
- values: ['normal', 'medium', 'semibold'],
62
+ values: ['normal', 'medium', 'semibold', 'bold'],
63
63
  },
64
64
  color: {
65
65
  type: 'enum',
66
- description: 'Text color',
67
- values: ['primary', 'secondary', 'tertiary'],
66
+ description: 'Text color (muted is an alias for tertiary)',
67
+ values: ['primary', 'secondary', 'tertiary', 'muted'],
68
68
  },
69
69
  font: {
70
70
  type: 'enum',
@@ -91,9 +91,9 @@ export default defineFragment({
91
91
  contract: {
92
92
  propsSummary: [
93
93
  'as: string - HTML element',
94
- 'size: 2xs|xs|sm|base|lg|xl|2xl - font size',
95
- 'weight: normal|medium|semibold - font weight',
96
- 'color: primary|secondary|tertiary - text color',
94
+ 'size: 2xs|xs|sm|base|md|lg|xl|2xl - font size (md = base)',
95
+ 'weight: normal|medium|semibold|bold - font weight',
96
+ 'color: primary|secondary|tertiary|muted - text color (muted = tertiary)',
97
97
  'font: sans|mono - font family',
98
98
  'truncate: boolean - enable truncation',
99
99
  'lineClamp: number - max lines',
@@ -130,6 +130,7 @@ export default defineFragment({
130
130
  <Text weight="normal">Normal weight</Text>
131
131
  <Text weight="medium">Medium weight</Text>
132
132
  <Text weight="semibold">Semibold weight</Text>
133
+ <Text weight="bold">Bold weight</Text>
133
134
  </div>
134
135
  ),
135
136
  },
@@ -141,6 +142,7 @@ export default defineFragment({
141
142
  <Text color="primary">Primary color (default)</Text>
142
143
  <Text color="secondary">Secondary color</Text>
143
144
  <Text color="tertiary">Tertiary color</Text>
145
+ <Text color="muted">Muted color (alias for tertiary)</Text>
144
146
  </div>
145
147
  ),
146
148
  },
@@ -25,7 +25,8 @@
25
25
  font-size: var(--fui-font-size-sm, $fui-font-size-sm);
26
26
  }
27
27
 
28
- .size-base {
28
+ .size-base,
29
+ .size-md {
29
30
  font-size: var(--fui-font-size-base, $fui-font-size-base);
30
31
  }
31
32
 
@@ -54,6 +55,10 @@
54
55
  font-weight: var(--fui-font-weight-semibold, $fui-font-weight-semibold);
55
56
  }
56
57
 
58
+ .weight-bold {
59
+ font-weight: var(--fui-font-weight-bold, $fui-font-weight-bold);
60
+ }
61
+
57
62
  // Text colors
58
63
  .color-primary {
59
64
  color: var(--fui-text-primary, $fui-text-primary);
@@ -63,7 +68,8 @@
63
68
  color: var(--fui-text-secondary, $fui-text-secondary);
64
69
  }
65
70
 
66
- .color-tertiary {
71
+ .color-tertiary,
72
+ .color-muted {
67
73
  color: var(--fui-text-tertiary, $fui-text-tertiary);
68
74
  }
69
75
 
@@ -22,6 +22,21 @@ describe('Text', () => {
22
22
  expect(el).toHaveClass('color-secondary');
23
23
  });
24
24
 
25
+ it('applies bold weight class', () => {
26
+ render(<Text weight="bold">Bold text</Text>);
27
+ expect(screen.getByText('Bold text')).toHaveClass('weight-bold');
28
+ });
29
+
30
+ it('applies muted color class (alias for tertiary)', () => {
31
+ render(<Text color="muted">Muted text</Text>);
32
+ expect(screen.getByText('Muted text')).toHaveClass('color-muted');
33
+ });
34
+
35
+ it('applies md size class (alias for base)', () => {
36
+ render(<Text size="md">Medium text</Text>);
37
+ expect(screen.getByText('Medium text')).toHaveClass('size-md');
38
+ });
39
+
25
40
  it('applies section-label variant class', () => {
26
41
  render(<Text variant="section-label">Label</Text>);
27
42
  expect(screen.getByText('Label')).toHaveClass('variant-section-label');
@@ -1,13 +1,28 @@
1
1
  import * as React from 'react';
2
2
  import styles from './Text.module.scss';
3
3
 
4
+ /**
5
+ * Typography component for rendering text with consistent styling.
6
+ * @see https://usefragments.com/components/text
7
+ */
4
8
  export interface TextProps extends Omit<React.HTMLAttributes<HTMLElement>, 'color'> {
5
9
  children: React.ReactNode;
10
+ /** HTML element to render.
11
+ * @default "span" */
6
12
  as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span' | 'label' | 'div' | 'strong' | 'em' | 'small' | 'mark' | 'del' | 'ins' | 'sub' | 'sup' | 'time' | 'address' | 'blockquote' | 'cite' | 'code' | 'abbr';
13
+ /** Preset text variant */
7
14
  variant?: 'section-label';
8
- size?: '2xs' | 'xs' | 'sm' | 'base' | 'lg' | 'xl' | '2xl';
9
- weight?: 'normal' | 'medium' | 'semibold';
10
- color?: 'primary' | 'secondary' | 'tertiary';
15
+ /** Font size. `"md"` is an alias for `"base"`.
16
+ * @see https://usefragments.com/components/text#sizes */
17
+ size?: '2xs' | 'xs' | 'sm' | 'base' | 'md' | 'lg' | 'xl' | '2xl';
18
+ /** Font weight.
19
+ * @default "normal" */
20
+ weight?: 'normal' | 'medium' | 'semibold' | 'bold';
21
+ /** Text color. `"muted"` is an alias for `"tertiary"`.
22
+ * @default "primary" */
23
+ color?: 'primary' | 'secondary' | 'tertiary' | 'muted';
24
+ /** Font family.
25
+ * @default "sans" */
11
26
  font?: 'sans' | 'mono';
12
27
  /** Truncate text with ellipsis when it overflows */
13
28
  truncate?: boolean;
@@ -368,8 +368,20 @@ export { ThemeProvider, ThemeToggle, useTheme };
368
368
  // configureTheme — JS-only seed configuration
369
369
  // ============================================
370
370
 
371
+ /**
372
+ * Neutral palette for surfaces, text, and borders.
373
+ * - `stone`: Cool gray (balanced, professional) — default
374
+ * - `ice`: Cool blue-tinted grays (crisp, technical)
375
+ * - `earth`: Warm brown-tinted grays (natural, grounded)
376
+ * - `sand`: Warm tan-tinted grays (organic, approachable)
377
+ * - `fire`: Warm red-tinted grays (bold, energetic)
378
+ *
379
+ * Preview palettes at https://usefragments.com/themes
380
+ */
371
381
  export type NeutralPalette = 'stone' | 'ice' | 'earth' | 'sand' | 'fire';
382
+ /** Spacing density: `compact` (tight), `default` (balanced), `relaxed` (spacious) */
372
383
  export type DensityPreset = 'compact' | 'default' | 'relaxed';
384
+ /** Border radius style: `sharp` (0), `subtle` (2px), `default` (4px), `rounded` (8px), `pill` (9999px) */
373
385
  export type RadiusStyle = 'sharp' | 'subtle' | 'default' | 'rounded' | 'pill';
374
386
 
375
387
  export interface ConfigureThemeOptions {
@@ -58,6 +58,10 @@ export default defineFragment({
58
58
  type: 'function',
59
59
  description: 'Callback with new checked state: (checked: boolean) => void',
60
60
  },
61
+ onCheckedChange: {
62
+ type: 'function',
63
+ description: 'Alias for onChange (Radix convention): (checked: boolean) => void',
64
+ },
61
65
  label: {
62
66
  type: 'string',
63
67
  description: 'Visible label text',
@@ -87,7 +91,7 @@ export default defineFragment({
87
91
  contract: {
88
92
  propsSummary: [
89
93
  'checked: boolean - on/off state',
90
- 'onChange: (checked) => void - state change handler',
94
+ 'onChange: (checked) => void - state change handler (or onCheckedChange)',
91
95
  'label: string - visible label text',
92
96
  'description: string - helper text below label',
93
97
  'disabled: boolean - non-interactive state',
@@ -42,6 +42,25 @@ describe('Switch', () => {
42
42
  expect(handleChange.mock.calls[0][0]).toBe(true);
43
43
  });
44
44
 
45
+ it('calls onCheckedChange alias when toggled', async () => {
46
+ const handleChange = vi.fn();
47
+ const user = userEvent.setup();
48
+ render(<Switch aria-label="Dark mode" onCheckedChange={handleChange} />);
49
+ await user.click(screen.getByRole('switch'));
50
+ expect(handleChange).toHaveBeenCalled();
51
+ expect(handleChange.mock.calls[0][0]).toBe(true);
52
+ });
53
+
54
+ it('prefers onChange over onCheckedChange when both provided', async () => {
55
+ const onChange = vi.fn();
56
+ const onCheckedChange = vi.fn();
57
+ const user = userEvent.setup();
58
+ render(<Switch aria-label="Test" onChange={onChange} onCheckedChange={onCheckedChange} />);
59
+ await user.click(screen.getByRole('switch'));
60
+ expect(onChange).toHaveBeenCalled();
61
+ expect(onCheckedChange).not.toHaveBeenCalled();
62
+ });
63
+
45
64
  it('has no accessibility violations', async () => {
46
65
  const { container } = render(<Switch aria-label="Accessible switch" />);
47
66
  await expectNoA11yViolations(container);
@@ -4,10 +4,19 @@ import * as React from 'react';
4
4
  import { Switch as BaseSwitch } from '@base-ui/react/switch';
5
5
  import styles from './Toggle.module.scss';
6
6
 
7
+ /**
8
+ * Binary on/off switch for settings and preferences.
9
+ * @see https://usefragments.com/components/switch
10
+ */
7
11
  export interface SwitchProps {
12
+ /** Whether the switch is on */
8
13
  checked?: boolean;
14
+ /** Default checked state (uncontrolled) */
9
15
  defaultChecked?: boolean;
16
+ /** Called when the switch is toggled */
10
17
  onChange?: (checked: boolean) => void;
18
+ /** Alias for onChange (Radix convention) */
19
+ onCheckedChange?: (checked: boolean) => void;
11
20
  label?: string;
12
21
  description?: string;
13
22
  disabled?: boolean;
@@ -28,6 +37,7 @@ const SwitchRoot = React.forwardRef<HTMLButtonElement, SwitchProps>(
28
37
  checked,
29
38
  defaultChecked,
30
39
  onChange,
40
+ onCheckedChange,
31
41
  label,
32
42
  description,
33
43
  disabled = false,
@@ -73,7 +83,7 @@ const SwitchRoot = React.forwardRef<HTMLButtonElement, SwitchProps>(
73
83
  id={id}
74
84
  checked={checked}
75
85
  defaultChecked={defaultChecked}
76
- onCheckedChange={onChange}
86
+ onCheckedChange={onChange ?? onCheckedChange}
77
87
  disabled={disabled}
78
88
  name={name}
79
89
  className={rootClasses}
@@ -157,7 +157,10 @@ export default defineFragment({
157
157
  onChange: {
158
158
  type: "function",
159
159
  description: "Called with new value when selection changes",
160
- required: true,
160
+ },
161
+ onValueChange: {
162
+ type: "function",
163
+ description: "Alias for onChange (Radix convention): (value: string) => void",
161
164
  },
162
165
  children: {
163
166
  type: "node",
@@ -197,7 +200,7 @@ export default defineFragment({
197
200
  contract: {
198
201
  propsSummary: [
199
202
  "value: string - selected value (required)",
200
- "onChange: (value: string) => void - change handler (required)",
203
+ "onChange: (value: string) => void - change handler (or onValueChange)",
201
204
  "children: ToggleGroup.Item[] - toggle items",
202
205
  "variant: default|pills|outline - visual style",
203
206
  "size: sm|md - size variant",
@@ -83,6 +83,26 @@ describe('ToggleGroup', () => {
83
83
  expect(optionB).toHaveAttribute('tabindex', '-1');
84
84
  });
85
85
 
86
+ it('calls onValueChange alias when an item is clicked', async () => {
87
+ const user = userEvent.setup();
88
+ const onValueChange = vi.fn();
89
+ renderToggleGroup({ onChange: undefined, onValueChange });
90
+
91
+ await user.click(screen.getByRole('radio', { name: /option b/i }));
92
+ expect(onValueChange).toHaveBeenCalledWith('b');
93
+ });
94
+
95
+ it('prefers onChange over onValueChange when both provided', async () => {
96
+ const user = userEvent.setup();
97
+ const onChange = vi.fn();
98
+ const onValueChange = vi.fn();
99
+ renderToggleGroup({ onChange, onValueChange });
100
+
101
+ await user.click(screen.getByRole('radio', { name: /option b/i }));
102
+ expect(onChange).toHaveBeenCalledWith('b');
103
+ expect(onValueChange).not.toHaveBeenCalled();
104
+ });
105
+
86
106
  it('has no accessibility violations', async () => {
87
107
  const { container } = renderToggleGroup();
88
108
  await expectNoA11yViolations(container);
@@ -7,16 +7,25 @@ import styles from './ToggleGroup.module.scss';
7
7
  // Types
8
8
  // ============================================
9
9
 
10
+ /**
11
+ * A group of toggle buttons where only one can be selected at a time.
12
+ * @see https://usefragments.com/components/togglegroup
13
+ */
10
14
  export interface ToggleGroupProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> {
11
15
  /** Current selected value */
12
16
  value: string;
13
17
  /** Callback when selection changes */
14
- onChange: (value: string) => void;
18
+ onChange?: (value: string) => void;
19
+ /** Alias for onChange (Radix convention) */
20
+ onValueChange?: (value: string) => void;
15
21
  /** Toggle items */
16
22
  children: React.ReactNode;
17
- /** Visual variant */
23
+ /** Visual variant.
24
+ * @default "default"
25
+ * @see https://usefragments.com/components/togglegroup#variants */
18
26
  variant?: 'default' | 'pills' | 'outline';
19
- /** Size */
27
+ /** Size.
28
+ * @default "md" */
20
29
  size?: 'sm' | 'md' | 'lg';
21
30
  /** Gap between items (for pills/outline variants) */
22
31
  gap?: 'none' | 'xs' | 'sm';
@@ -63,6 +72,7 @@ function useToggleGroupContext() {
63
72
  function ToggleGroupRoot({
64
73
  value,
65
74
  onChange,
75
+ onValueChange,
66
76
  children,
67
77
  variant = 'default',
68
78
  size = 'md',
@@ -70,6 +80,7 @@ function ToggleGroupRoot({
70
80
  className,
71
81
  ...htmlProps
72
82
  }: ToggleGroupProps) {
83
+ const resolvedOnChange = onChange ?? onValueChange ?? (() => {});
73
84
  const classes = [
74
85
  styles.group,
75
86
  styles[variant],
@@ -91,7 +102,7 @@ function ToggleGroupRoot({
91
102
 
92
103
  const contextValue: ToggleGroupContextValue = {
93
104
  value,
94
- onChange,
105
+ onChange: resolvedOnChange,
95
106
  variant,
96
107
  size,
97
108
  hasFocusableSelection,
@@ -91,6 +91,23 @@ describe('Tooltip', () => {
91
91
  });
92
92
  });
93
93
 
94
+ it('respects shared Tooltip.Provider delay settings', async () => {
95
+ const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
96
+ render(
97
+ <Tooltip.Provider delay={0}>
98
+ <Tooltip content="Provider tooltip">
99
+ <button>Provider Trigger</button>
100
+ </Tooltip>
101
+ </Tooltip.Provider>
102
+ );
103
+
104
+ await user.hover(screen.getByRole('button', { name: /provider trigger/i }));
105
+
106
+ await waitFor(() => {
107
+ expect(screen.getByText('Provider tooltip')).toBeInTheDocument();
108
+ });
109
+ });
110
+
94
111
  it('has no accessibility violations when open', async () => {
95
112
  const { container } = render(
96
113
  <Tooltip content="Accessible tooltip" open={true} delay={0}>
@@ -46,12 +46,18 @@ export interface TooltipProviderProps {
46
46
  closeDelay?: number;
47
47
  /** Timeout for instant open when moving between tooltips (ms) */
48
48
  timeout?: number;
49
+ /** Alias for `delay` (Radix convention) */
50
+ delayDuration?: number;
51
+ /** Alias for `timeout` (Radix convention) */
52
+ skipDelayDuration?: number;
49
53
  }
50
54
 
51
55
  // ============================================
52
56
  // Components
53
57
  // ============================================
54
58
 
59
+ const TooltipProviderContext = React.createContext(false);
60
+
55
61
  /**
56
62
  * Tooltip - Shows contextual information on hover/focus
57
63
  *
@@ -68,8 +74,8 @@ function TooltipRoot({
68
74
  side = 'top',
69
75
  align = 'center',
70
76
  sideOffset = 6,
71
- delay = 400,
72
- closeDelay = 0,
77
+ delay,
78
+ closeDelay,
73
79
  disabled = false,
74
80
  arrow = true,
75
81
  open,
@@ -78,6 +84,7 @@ function TooltipRoot({
78
84
  className,
79
85
  ...htmlProps
80
86
  }: TooltipProps) {
87
+ const hasExternalProvider = React.useContext(TooltipProviderContext);
81
88
  const renderTrigger = React.useCallback(
82
89
  (triggerProps: React.HTMLAttributes<HTMLElement> & { ref?: React.Ref<HTMLElement> }) => {
83
90
  const childProps = children.props as Record<string, unknown>;
@@ -106,30 +113,39 @@ function TooltipRoot({
106
113
  return children;
107
114
  }
108
115
 
109
- return (
110
- <BaseTooltip.Provider delay={delay} closeDelay={closeDelay}>
111
- <BaseTooltip.Root
112
- open={open}
113
- defaultOpen={defaultOpen}
114
- onOpenChange={onOpenChange}
115
- >
116
- <BaseTooltip.Trigger render={renderTrigger} />
117
- <BaseTooltip.Portal>
118
- <BaseTooltip.Positioner
119
- side={side}
120
- align={align}
121
- sideOffset={sideOffset}
122
- className={styles.positioner}
123
- >
124
- <BaseTooltip.Popup {...htmlProps} className={[styles.popup, className].filter(Boolean).join(' ')}>
125
- {content}
126
- {arrow && <BaseTooltip.Arrow className={styles.arrow} />}
127
- </BaseTooltip.Popup>
128
- </BaseTooltip.Positioner>
129
- </BaseTooltip.Portal>
130
- </BaseTooltip.Root>
131
- </BaseTooltip.Provider>
116
+ const tooltipNode = (
117
+ <BaseTooltip.Root
118
+ open={open}
119
+ defaultOpen={defaultOpen}
120
+ onOpenChange={onOpenChange}
121
+ >
122
+ <BaseTooltip.Trigger render={renderTrigger} />
123
+ <BaseTooltip.Portal>
124
+ <BaseTooltip.Positioner
125
+ side={side}
126
+ align={align}
127
+ sideOffset={sideOffset}
128
+ className={styles.positioner}
129
+ >
130
+ <BaseTooltip.Popup {...htmlProps} className={[styles.popup, className].filter(Boolean).join(' ')}>
131
+ {content}
132
+ {arrow && <BaseTooltip.Arrow className={styles.arrow} />}
133
+ </BaseTooltip.Popup>
134
+ </BaseTooltip.Positioner>
135
+ </BaseTooltip.Portal>
136
+ </BaseTooltip.Root>
132
137
  );
138
+
139
+ // Only create a local provider when no shared provider exists, or when a local delay override is requested.
140
+ if (!hasExternalProvider || delay !== undefined || closeDelay !== undefined) {
141
+ return (
142
+ <BaseTooltip.Provider delay={delay ?? 400} closeDelay={closeDelay ?? 0}>
143
+ {tooltipNode}
144
+ </BaseTooltip.Provider>
145
+ );
146
+ }
147
+
148
+ return tooltipNode;
133
149
  }
134
150
 
135
151
  /**
@@ -145,18 +161,26 @@ function TooltipRoot({
145
161
  */
146
162
  export function TooltipProvider({
147
163
  children,
148
- delay = 400,
164
+ delay,
149
165
  closeDelay = 0,
150
- timeout = 400,
166
+ timeout,
167
+ delayDuration,
168
+ skipDelayDuration,
151
169
  }: TooltipProviderProps) {
170
+ // Resolve Radix-compatible aliases
171
+ const resolvedDelay = delay ?? delayDuration ?? 400;
172
+ const resolvedTimeout = timeout ?? skipDelayDuration ?? 400;
173
+
152
174
  return (
153
- <BaseTooltip.Provider
154
- delay={delay}
155
- closeDelay={closeDelay}
156
- timeout={timeout}
157
- >
158
- {children}
159
- </BaseTooltip.Provider>
175
+ <TooltipProviderContext.Provider value={true}>
176
+ <BaseTooltip.Provider
177
+ delay={resolvedDelay}
178
+ closeDelay={closeDelay}
179
+ timeout={resolvedTimeout}
180
+ >
181
+ {children}
182
+ </BaseTooltip.Provider>
183
+ </TooltipProviderContext.Provider>
160
184
  );
161
185
  }
162
186
 
package/src/index.ts CHANGED
@@ -2,6 +2,12 @@
2
2
  // This ensures --fui-* variables are available when using any component
3
3
  import './styles/globals.scss';
4
4
 
5
+ // Runtime CSS detection — warns if component styles aren't loaded
6
+ import { checkCssLoaded } from './utils/css-warning';
7
+ if (typeof window !== 'undefined') {
8
+ checkCssLoaded();
9
+ }
10
+
5
11
  // Core Components
6
12
  export { Button, type ButtonProps } from './components/Button';
7
13
  export { Input, type InputProps } from './components/Input';
@@ -1,3 +1,5 @@
1
+ @use 'sass:list';
2
+
1
3
  // ============================================
2
4
  // @fragments-sdk/ui Design Token Seeds
3
5
  // ============================================
@@ -44,17 +46,17 @@ $fui-radius-style: "default" !default;
44
46
  // Fail fast at compile time if invalid seed values are provided.
45
47
 
46
48
  $_valid-neutrals: "stone", "ice", "earth", "sand", "fire";
47
- @if not index($_valid-neutrals, $fui-neutral) {
49
+ @if not list.index($_valid-neutrals, $fui-neutral) {
48
50
  @error "Invalid $fui-neutral: '#{$fui-neutral}'. Must be one of: #{$_valid-neutrals}";
49
51
  }
50
52
 
51
53
  $_valid-densities: "compact", "default", "relaxed";
52
- @if not index($_valid-densities, $fui-density) {
54
+ @if not list.index($_valid-densities, $fui-density) {
53
55
  @error "Invalid $fui-density: '#{$fui-density}'. Must be one of: #{$_valid-densities}";
54
56
  }
55
57
 
56
58
  $_valid-radii: "sharp", "subtle", "default", "rounded", "pill";
57
- @if not index($_valid-radii, $fui-radius-style) {
59
+ @if not list.index($_valid-radii, $fui-radius-style) {
58
60
  @error "Invalid $fui-radius-style: '#{$fui-radius-style}'. Must be one of: #{$_valid-radii}";
59
61
  }
60
62
 
@@ -68,6 +68,7 @@ $fui-font-size-2xl: 2.143rem !default; // 30px
68
68
  $fui-font-weight-normal: 400 !default;
69
69
  $fui-font-weight-medium: 500 !default;
70
70
  $fui-font-weight-semibold: 600 !default;
71
+ $fui-font-weight-bold: 700 !default;
71
72
 
72
73
  $fui-line-height-tight: 1.25 !default;
73
74
  $fui-line-height-normal: 1.5 !default;
@@ -436,6 +437,7 @@ $fui-dark-hero-gradient-color: rgba(120, 119, 198, 0.25) !default;
436
437
  --fui-font-weight-normal: #{$fui-font-weight-normal};
437
438
  --fui-font-weight-medium: #{$fui-font-weight-medium};
438
439
  --fui-font-weight-semibold: #{$fui-font-weight-semibold};
440
+ --fui-font-weight-bold: #{$fui-font-weight-bold};
439
441
  --fui-line-height-tight: #{$fui-line-height-tight};
440
442
  --fui-line-height-normal: #{$fui-line-height-normal};
441
443
  --fui-line-height-relaxed: #{$fui-line-height-relaxed};