@usefragments/ui 1.0.2 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/README.md +16 -16
  2. package/dist/assets/fragments-wordmark.cjs +30 -0
  3. package/dist/assets/fragments-wordmark.d.ts +13 -0
  4. package/dist/assets/fragments-wordmark.d.ts.map +1 -0
  5. package/dist/assets/fragments-wordmark.js +30 -0
  6. package/dist/assets/ui.css +3173 -3108
  7. package/dist/components/Accordion/Accordion.module.scss.cjs +8 -8
  8. package/dist/components/Accordion/Accordion.module.scss.js +8 -8
  9. package/dist/components/Accordion/index.cjs +22 -10
  10. package/dist/components/Accordion/index.d.ts.map +1 -1
  11. package/dist/components/Accordion/index.js +22 -10
  12. package/dist/components/Alert/Alert.module.scss.cjs +13 -13
  13. package/dist/components/Alert/Alert.module.scss.js +13 -13
  14. package/dist/components/AppShell/AppShell.module.scss.cjs +22 -10
  15. package/dist/components/AppShell/AppShell.module.scss.js +22 -10
  16. package/dist/components/AppShell/index.cjs +1 -1
  17. package/dist/components/AppShell/index.js +1 -1
  18. package/dist/components/Avatar/Avatar.module.scss.cjs +13 -13
  19. package/dist/components/Avatar/Avatar.module.scss.js +13 -13
  20. package/dist/components/Badge/Badge.module.scss.cjs +16 -16
  21. package/dist/components/Badge/Badge.module.scss.js +16 -16
  22. package/dist/components/BentoGrid/BentoGrid.module.scss.cjs +14 -14
  23. package/dist/components/BentoGrid/BentoGrid.module.scss.js +14 -14
  24. package/dist/components/Box/Box.module.scss.cjs +152 -152
  25. package/dist/components/Box/Box.module.scss.js +152 -152
  26. package/dist/components/Breadcrumbs/Breadcrumbs.module.scss.cjs +8 -8
  27. package/dist/components/Breadcrumbs/Breadcrumbs.module.scss.js +8 -8
  28. package/dist/components/Button/Button.module.scss.cjs +13 -13
  29. package/dist/components/Button/Button.module.scss.js +13 -13
  30. package/dist/components/Card/Card.module.scss.cjs +17 -17
  31. package/dist/components/Card/Card.module.scss.js +17 -17
  32. package/dist/components/Card/index.d.ts +3 -3
  33. package/dist/components/Card/index.d.ts.map +1 -1
  34. package/dist/components/Chart/Chart.module.scss.cjs +15 -15
  35. package/dist/components/Chart/Chart.module.scss.js +15 -15
  36. package/dist/components/Checkbox/Checkbox.module.scss.cjs +11 -11
  37. package/dist/components/Checkbox/Checkbox.module.scss.js +11 -11
  38. package/dist/components/Chip/Chip.module.scss.cjs +15 -15
  39. package/dist/components/Chip/Chip.module.scss.js +15 -15
  40. package/dist/components/CodeBlock/CodeBlock.module.scss.cjs +21 -21
  41. package/dist/components/CodeBlock/CodeBlock.module.scss.js +21 -21
  42. package/dist/components/Collapsible/Collapsible.module.scss.cjs +10 -10
  43. package/dist/components/Collapsible/Collapsible.module.scss.js +10 -10
  44. package/dist/components/ColorPicker/ColorPicker.module.scss.cjs +14 -14
  45. package/dist/components/ColorPicker/ColorPicker.module.scss.js +14 -14
  46. package/dist/components/Combobox/Combobox.module.scss.cjs +23 -23
  47. package/dist/components/Combobox/Combobox.module.scss.js +23 -23
  48. package/dist/components/Command/Command.module.scss.cjs +11 -11
  49. package/dist/components/Command/Command.module.scss.js +11 -11
  50. package/dist/components/ConversationList/ConversationList.module.scss.cjs +10 -10
  51. package/dist/components/ConversationList/ConversationList.module.scss.js +10 -10
  52. package/dist/components/DataTable/DataTable.module.scss.cjs +34 -34
  53. package/dist/components/DataTable/DataTable.module.scss.js +34 -34
  54. package/dist/components/DataTable/index.d.ts +3 -1
  55. package/dist/components/DataTable/index.d.ts.map +1 -1
  56. package/dist/components/DatePicker/DatePicker.module.scss.cjs +33 -33
  57. package/dist/components/DatePicker/DatePicker.module.scss.js +33 -33
  58. package/dist/components/DatePicker/index.d.ts +1 -1
  59. package/dist/components/DatePicker/index.d.ts.map +1 -1
  60. package/dist/components/Dialog/Dialog.module.scss.cjs +14 -14
  61. package/dist/components/Dialog/Dialog.module.scss.js +14 -14
  62. package/dist/components/Drawer/Drawer.module.scss.cjs +28 -28
  63. package/dist/components/Drawer/Drawer.module.scss.js +28 -28
  64. package/dist/components/Drawer/index.cjs +20 -3
  65. package/dist/components/Drawer/index.d.ts.map +1 -1
  66. package/dist/components/Drawer/index.js +20 -3
  67. package/dist/components/Editor/Editor.module.scss.cjs +17 -17
  68. package/dist/components/Editor/Editor.module.scss.js +17 -17
  69. package/dist/components/EmptyState/EmptyState.module.scss.cjs +8 -8
  70. package/dist/components/EmptyState/EmptyState.module.scss.js +8 -8
  71. package/dist/components/Field/Field.module.scss.cjs +4 -4
  72. package/dist/components/Field/Field.module.scss.js +4 -4
  73. package/dist/components/Field/index.cjs +10 -9
  74. package/dist/components/Field/index.d.ts +7 -7
  75. package/dist/components/Field/index.d.ts.map +1 -1
  76. package/dist/components/Field/index.js +10 -9
  77. package/dist/components/Fieldset/Fieldset.module.scss.cjs +3 -3
  78. package/dist/components/Fieldset/Fieldset.module.scss.js +3 -3
  79. package/dist/components/Header/Header.module.scss.cjs +44 -29
  80. package/dist/components/Header/Header.module.scss.js +44 -29
  81. package/dist/components/Header/index.cjs +56 -3
  82. package/dist/components/Header/index.d.ts +25 -3
  83. package/dist/components/Header/index.d.ts.map +1 -1
  84. package/dist/components/Header/index.js +56 -3
  85. package/dist/components/Icon/Icon.module.scss.cjs +8 -8
  86. package/dist/components/Icon/Icon.module.scss.js +8 -8
  87. package/dist/components/IconButton/IconButton.module.scss.cjs +8 -8
  88. package/dist/components/IconButton/IconButton.module.scss.js +8 -8
  89. package/dist/components/Image/Image.module.scss.cjs +27 -27
  90. package/dist/components/Image/Image.module.scss.js +27 -27
  91. package/dist/components/Input/Input.module.scss.cjs +19 -19
  92. package/dist/components/Input/Input.module.scss.js +19 -19
  93. package/dist/components/Link/Link.module.scss.cjs +10 -10
  94. package/dist/components/Link/Link.module.scss.js +10 -10
  95. package/dist/components/List/List.module.scss.cjs +19 -19
  96. package/dist/components/List/List.module.scss.js +19 -19
  97. package/dist/components/Listbox/Listbox.module.scss.cjs +8 -8
  98. package/dist/components/Listbox/Listbox.module.scss.js +8 -8
  99. package/dist/components/Loading/Loading.module.scss.cjs +30 -30
  100. package/dist/components/Loading/Loading.module.scss.js +30 -30
  101. package/dist/components/Markdown/Markdown.module.scss.cjs +1 -1
  102. package/dist/components/Markdown/Markdown.module.scss.js +1 -1
  103. package/dist/components/Menu/Menu.module.scss.cjs +14 -14
  104. package/dist/components/Menu/Menu.module.scss.js +14 -14
  105. package/dist/components/Message/Message.module.scss.cjs +18 -18
  106. package/dist/components/Message/Message.module.scss.js +18 -18
  107. package/dist/components/NavigationMenu/NavigationMenu.module.scss.cjs +28 -28
  108. package/dist/components/NavigationMenu/NavigationMenu.module.scss.js +28 -28
  109. package/dist/components/Pagination/Pagination.module.scss.cjs +7 -7
  110. package/dist/components/Pagination/Pagination.module.scss.js +7 -7
  111. package/dist/components/Popover/Popover.module.scss.cjs +10 -10
  112. package/dist/components/Popover/Popover.module.scss.js +10 -10
  113. package/dist/components/Popover/index.cjs +27 -2
  114. package/dist/components/Popover/index.d.ts.map +1 -1
  115. package/dist/components/Popover/index.js +27 -2
  116. package/dist/components/Progress/Progress.module.scss.cjs +25 -25
  117. package/dist/components/Progress/Progress.module.scss.js +25 -25
  118. package/dist/components/Prompt/Prompt.module.scss.cjs +18 -18
  119. package/dist/components/Prompt/Prompt.module.scss.js +18 -18
  120. package/dist/components/RadioGroup/RadioGroup.module.scss.cjs +17 -17
  121. package/dist/components/RadioGroup/RadioGroup.module.scss.js +17 -17
  122. package/dist/components/RadioGroup/index.cjs +2 -0
  123. package/dist/components/RadioGroup/index.d.ts +2 -0
  124. package/dist/components/RadioGroup/index.d.ts.map +1 -1
  125. package/dist/components/RadioGroup/index.js +2 -0
  126. package/dist/components/ScrollArea/ScrollArea.module.scss.cjs +10 -10
  127. package/dist/components/ScrollArea/ScrollArea.module.scss.js +10 -10
  128. package/dist/components/Select/Select.module.scss.cjs +17 -17
  129. package/dist/components/Select/Select.module.scss.js +17 -17
  130. package/dist/components/Separator/Separator.module.scss.cjs +10 -10
  131. package/dist/components/Separator/Separator.module.scss.js +10 -10
  132. package/dist/components/Sidebar/Sidebar.module.scss.cjs +42 -42
  133. package/dist/components/Sidebar/Sidebar.module.scss.js +42 -42
  134. package/dist/components/Skeleton/Skeleton.module.scss.cjs +38 -38
  135. package/dist/components/Skeleton/Skeleton.module.scss.js +38 -38
  136. package/dist/components/Slider/Slider.module.scss.cjs +12 -12
  137. package/dist/components/Slider/Slider.module.scss.js +12 -12
  138. package/dist/components/Table/Table.module.scss.cjs +18 -18
  139. package/dist/components/Table/Table.module.scss.js +18 -18
  140. package/dist/components/TableOfContents/TableOfContents.module.scss.cjs +15 -15
  141. package/dist/components/TableOfContents/TableOfContents.module.scss.js +15 -15
  142. package/dist/components/Tabs/Tabs.module.scss.cjs +12 -12
  143. package/dist/components/Tabs/Tabs.module.scss.js +12 -12
  144. package/dist/components/Text/Text.module.scss.cjs +51 -51
  145. package/dist/components/Text/Text.module.scss.js +51 -51
  146. package/dist/components/Textarea/Textarea.module.scss.cjs +23 -23
  147. package/dist/components/Textarea/Textarea.module.scss.js +23 -23
  148. package/dist/components/Theme/ThemeToggle.module.scss.cjs +6 -6
  149. package/dist/components/Theme/ThemeToggle.module.scss.js +6 -6
  150. package/dist/components/ThinkingIndicator/ThinkingIndicator.module.scss.cjs +13 -13
  151. package/dist/components/ThinkingIndicator/ThinkingIndicator.module.scss.js +13 -13
  152. package/dist/components/Toast/Toast.module.scss.cjs +21 -21
  153. package/dist/components/Toast/Toast.module.scss.js +21 -21
  154. package/dist/components/Toggle/Toggle.module.scss.cjs +13 -13
  155. package/dist/components/Toggle/Toggle.module.scss.js +13 -13
  156. package/dist/components/ToggleGroup/ToggleGroup.module.scss.cjs +17 -17
  157. package/dist/components/ToggleGroup/ToggleGroup.module.scss.js +17 -17
  158. package/dist/components/Tooltip/Tooltip.module.scss.cjs +3 -3
  159. package/dist/components/Tooltip/Tooltip.module.scss.js +3 -3
  160. package/dist/data-table.cjs +58 -41
  161. package/dist/data-table.js +59 -41
  162. package/dist/datepicker.cjs +32 -72
  163. package/dist/datepicker.js +32 -72
  164. package/dist/index.cjs +2 -0
  165. package/dist/index.d.ts +1 -0
  166. package/dist/index.d.ts.map +1 -1
  167. package/dist/index.js +2 -0
  168. package/fragments.json +1 -1
  169. package/package.json +4 -8
  170. package/src/assets/fragments-wordmark.tsx +47 -0
  171. package/src/components/Accordion/index.tsx +13 -3
  172. package/src/components/Alert/Alert.module.scss +15 -2
  173. package/src/components/AppShell/AppShell.module.scss +73 -15
  174. package/src/components/AppShell/index.tsx +1 -1
  175. package/src/components/Button/Button.module.scss +19 -20
  176. package/src/components/Card/Card.module.scss +3 -3
  177. package/src/components/Card/index.tsx +3 -3
  178. package/src/components/CodeBlock/CodeBlock.module.scss +2 -7
  179. package/src/components/Combobox/Combobox.module.scss +3 -2
  180. package/src/components/DataTable/DataTable.contract.json +16 -23
  181. package/src/components/DataTable/index.tsx +25 -4
  182. package/src/components/DatePicker/index.tsx +32 -84
  183. package/src/components/Drawer/index.tsx +38 -12
  184. package/src/components/Field/index.tsx +35 -35
  185. package/src/components/Header/Header.contract.json +41 -1
  186. package/src/components/Header/Header.module.scss +40 -0
  187. package/src/components/Header/index.tsx +88 -2
  188. package/src/components/IconButton/IconButton.contract.json +120 -0
  189. package/src/components/Listbox/Listbox.module.scss +5 -4
  190. package/src/components/Menu/Menu.module.scss +8 -10
  191. package/src/components/Popover/index.tsx +34 -2
  192. package/src/components/RadioGroup/RadioGroup.contract.json +13 -27
  193. package/src/components/RadioGroup/index.tsx +4 -0
  194. package/src/components/Select/Select.module.scss +13 -12
  195. package/src/components/TableOfContents/TableOfContents.module.scss +10 -1
  196. package/src/components/Toast/Toast.module.scss +15 -2
  197. package/src/components/ToggleGroup/ToggleGroup.module.scss +4 -1
  198. package/src/index.ts +1 -0
  199. package/src/styles/globals.scss +13 -10
  200. package/src/tokens/_component-properties.scss +25 -0
  201. package/src/tokens/_computed.scss +22 -4
  202. package/src/tokens/_derive.scss +55 -6
  203. package/src/tokens/_palettes.scss +24 -0
  204. package/src/tokens/_seeds.scss +20 -12
  205. package/src/tokens/_variables.scss +110 -63
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usefragments/ui",
3
- "version": "1.0.2",
3
+ "version": "1.1.1",
4
4
  "license": "MIT",
5
5
  "description": "Customizable UI components built on Base UI headless primitives",
6
6
  "author": "Conan McNicholl",
@@ -155,13 +155,9 @@
155
155
  "remark-gfm": ">=4.0.0",
156
156
  "shiki": ">=1.0.0"
157
157
  },
158
- "scarfSettings": {
159
- "defaultOptIn": true
160
- },
161
158
  "dependencies": {
162
- "@base-ui/react": "^1.5.0",
163
- "@phosphor-icons/react": "^2.1.10",
164
- "@scarf/scarf": "^1.4.0"
159
+ "@base-ui/react": "1.6.0",
160
+ "@phosphor-icons/react": "^2.1.10"
165
161
  },
166
162
  "devDependencies": {
167
163
  "@storybook/addon-a11y": "^8",
@@ -194,7 +190,7 @@
194
190
  "vite": "^6.0.0",
195
191
  "vitest": "^2.1.8",
196
192
  "vitest-axe": "^0.1.0",
197
- "@usefragments/core": "1.0.0"
193
+ "@usefragments/core": "1.2.0"
198
194
  },
199
195
  "files": [
200
196
  "src",
@@ -0,0 +1,47 @@
1
+ import * as React from "react";
2
+
3
+ // Baked from the brand lockup artwork (star + wordmark), trimmed to content and
4
+ // stored as a monochrome alpha mask so it can be filled with currentColor. That
5
+ // keeps the lockup themeable — it renders in the current text color, matching
6
+ // the light and dark brand versions from a single asset.
7
+ const WORDMARK_MASK =
8
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAYAAAABgCAYAAAAU9KWJAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAgAElEQVR42u1dCbxd47W/Z7hX0tBITLeCpC9IXgipUiXUlJiJGiu0UlrDK9JnprT1UFNpa4hIzEqoiia0eCpFtaHB0xCtGFqPqrlSyr3nOHt3f7K++Gfdtff59nCGe85av9/3O3c4Z5+9v2HN6786OlqQfN/P0+s6wbgrGIPo91yHkpKSklJrkmHyIADm+ktpV/q9oDOkpKSk1LoCoEivZ/mf0B1oGSgpKSkptR7zL9DrPobre573EQmAnmCMUiGgpKSk1Np+/9HBeIsYfyUYZfr5DLQQlJSUlJRaxO9PrwMDrf8PTPuv0Oufzf81GKykpKTUmq6fq4nZl/3lyQqBfdIEg42VoYFkJSUlpeYL+h7DNP9lBH+7K24cgLKKiho7UFJSUmpOzX+rYJQMr6fRRwbQa6+JEbgIAdL2i+xvY4NxdjC61ZWkpKSk1Pigb3cwXgzT/oGsW+jsMDeQpO2bIrJgHBCM++jzLwdjgAoAJSUlpQYWe5nXgOn/KsTv74fEAYywWIkFjyVt32QTnUcMH4XLg8r8lZSUlBrv9/+BI/PnsYADzeeD31dg2v4AqiG4h1xK9LGPP9dDv1+vVcVKSkpKjWX++4Nm78UUAPcwN9K6xjUU/P+vEDPwSLBUmJD5H60nUFJSUmqQ3z9g1P8ZjHeYa8dJBtCr0e43C8bEYNwN2v3H2n6VTKKD1QJQUlJSakyx14rBeMwh6BslBAyj/wcID67tR8UQtlIBoKSkpNSYlM/r4vj9Q7T4R4LxLFgDnovlEHz+g+DlPxRTSElJSan+fv+pSZk/aPB/D8aWzN/v+tmXFU5CSUmp3VIuC5QfX7Dpl/VigFbzDxj2NlTI5cq0w5j4ztQoxk8gAObrjlBSUmo733sUNk6thANk6XwmEAB/SRD05UVg59D19ox5Lfv5m9T/r6Sk1E6VttsHzPdG434JXvcOXjcJxhrB6HIBTiPhsJyASNDZ6+4Urh/7mbkQRzg65vW4ANEUUCUlpbYQAMZdsoS5S5YQvPL9wZgZCIaTCS7hC8FY00IlpBEO4Pc/O4Og7wvBWB0yiS5KWEB2iFoASkpK7ZZ5Mw3A1KrR+8F4zkAmBONa04glYKAHBa9bkDAZ6KD5d9HP+wID9hKkexr6MBjj6XqdHdArOEYaqXUVbasCQElJqd0EwHhgqhV4LdvhyKQ/NL78YPwuGD8Nfv9+MKYEY+tgfJZy/K3lsQEUe3kptP8judsm+PmJGAIABcm6rZoCKgT7rVW23NBToaTUnsJgvgPTtBAKFXqfEQ4lR+HQE7zv/6nI6/pgLMog6DvTCjJw/wwNvuf1BBlArwbjU62WAqpNbpSUlFysgCNSVN+GCYdyRAWul1LzfwQZNlgWo+C6XozrPdqKawuCsZuyo4xVNj0YtwZjNo3byW12lQHQ0zoIJaX2g2BYlYqokmrlLgIChUOS77CfMU3h12PBbCvIdkqYAnpLK/n/YT4MEN41wfhnhFC0ldB/a0UrSElJyY1ZXJYiHbPW5AFT36ujr9/fZhUdmTAF9PxWSQGF9dwPGT8J35IwbPD/JRUASkrtmxK6eRr3TI3JMuozJUYNAuD8hALg0FawAID57wVrWbLWV4iLTy0AJSV1BX38+mDKWEAtmf+csCwdYHy/SJgCun1/FwDo7ydmjvNQYaB3r1D9hBnPU4B+vmIhKSnFjrPZeqdiK2iOU1Lk5teCLINebIq9qqVpBv9bkCAF1LhA1u/vKaCwhmcywVmhOfkDVXsPM20zDbNnY4AeaSWl9rYABpN2WKtgcNJir82jmr3T65CYgWz7ntdMjUJ/1nztfVMbTAuFXQFBeBsICNXulZSySbE+NxizgnFzMC7s12cLGMRPmiEYDMzriI6IAC3EMNZjTWBcr/9YC8VxPicIuDcM4B79v9MC+klDj7WSkrOyvBLUHPkEaFnst0oW3Pw4y/wxp59+rtQpSGyFz5XVfPMguCYkTAG9rQX8/3btJrN1M3SDgtwpKWWubI0gaBx7zv4E/8v1J4iAIvq+TSZIMJ6uYgV4tRIOoJnPdylMAub3jYQZQD/s7wwS5uAYeDb7fFNVACgpZd7D5ItM4Xyu3wgA8l8V8UZNM/bg9wuoM5ZhxK8H49uUW29aNf6agrFLqjD5MOFQcRAONmD5Oi/2cmB+P0giAILv+mYLCYDj7bOBIJ3SH56vUe6oZnKF1eI+Gvl8zTCvWd8DeBwmQdvZmgiATNcOAMG4tm9QOe8Fxola/PMQgLVC4zPB2DQYXw7GSQQtYD7/Z9aMPYlwsJ+d5Mq0YEF+HlMA2O/asb+5gADQ7eMBltKJggCwMNdd/HNs5GGPFPheidiceX6tmNZnwQHALl8jYLx8FQUpX+UakfMkzE0uwXcVuLIWQ8ErVLuvGimWYc+aS/I8YfveIT0zV2Xtco57BccABmXfCwKgk82BNHKO35d3eL58Im2fAqZnU/UnMv1ySCD1jGpdxOh71qBA5CTSRi8PGNGvgtdngte3o4QDMKzvxdFYISjzSJIUUGP5tEAKqLUAjrVaCczDwVkKOGk/pQWoo40/lJSL1Qg5Nid8Lp8B41/uupQ5tSrt3aEWVrzWgHrS8wj3UhQOfj7m3HbStcw1V7HKAmeqGSgkeeFvK1OvjtUo9TgX53kSurP5dwyie1id76uwPeHAa4aScowK5+KkFoC9D+HvA2jduulsDBXmOXx/socdQIz5LsT+B43cC9GS7QMa4LDBTJssujADekCzCTYKxu7kWrqU7mUBNHGfHYcZQ/rjp23xk2MQ2BZFGYtl1f7kAqK5NAHvfWg996Kfd6AeDT5YVoZ+FIyJVAOwlzDMNfY33eBoHvemvxnLcCupi5udL6op2Jx6MU8iq3APc+ikg8AA6szGPtjgMAXjSUrH/adRFKgw7XECrTvOwIdL10jCoOgcTKAUvntJc3uT9oK5h2eCcSdZU2PhfvEaq7B5Gh/CKDan/+9Ja7SGAGNiFKYzgvFLYiJvGKh0coM+TSmGBqxxnShBiE2XTF0LKQNm/haRO/cdyghbHPz8m+D1YrO++HxphBjNj/EMnGISK4Lv+KNB2aXvfYtcywsIDdjUG3XHZcKmYJPmcS+a0xWF+xhI+/DyYPzWKLm0p3BfGXysfc1+l+YUM33YWTHXPZaezWdu7VdhT0Sds/XZd+C9D6b9cgUV5T5P+9K43ZfQOprvnkM8dBS7Tk6atBGEAvlihLZfjVmWQMptAgwgF+ESKLqaKeSKGg0LkksQkS/F7C9gn920pFyl2YUALHQnVfBmUqsBlsI1JKDxb09yLYPahs4kSO/3BauqR4rfwOeH0eF812G9PNCyjLKwaVwhAEG7T5NVujgOXLjpb2EEKGs6NImZ//P4oabXOTAnhr4G97URWceeo7X6HjHtwcLcFgH8bxZ8n8vcmtjeZnGFAGq7xIznO86pfY9Z/xnm3EetKeMxi9j+3AL25wBqB/tCjGc3QulocKHm2esY4TN+wj4mVpE+FfgnMv7vESx9nGuba95neq6Ic0iHGi9YidD2XYOmH2D7REcGHyoc2ALnY/o6LVPZNiGOkWWgL5qoviTYmlAAFKll5zJXD+D8VEKY2EdhA5jYVaTx4lobjWllMKWnCXDbHttXRiiMZAepSEx4b3IFcoC6Xhol9nsv268e1al0uSgKwBi3BZMd7zcMHM/eiwfW4k+hGv04OAuG7gsRALPxfcHrd+jvJ4DCUgmZgzKDVq9AC9Rx3H1j8KzgfqyCx6/Jr1uBtTjZ1fqG71yfBAhXLPkaLncPDGnA/O0se+6ivh807x666b3hPh5hBZCRz87Oi/nsMEEjHw33az9TCnMzVzln9p7tPHdB2v0Lwr33kIsa46Rl+F+J8brzkdd2gEYwDya6klZbhIe/DDSiQgZujXwC094e8EMy6C3cA0VnhWaLCTABsDjDau0yWABjWTbWB8FYiyy0h5nLsJftKazcHsktBzJZfWZ9ll33HKttuLNaijAwqaPgc/Z7Kw6xImRmZUBMXYeqPlGz/3WIALiDve9SYv5+CHMPWx8PLXESohvBs57EFLxylefD58eU4XNi1N3sQ1bJsqSDCMBB6czx+7wbXTIh372QWV5H0Hq8xp6l7LCuHghf690YzvjK6DjFpY7n7BR4npGAXlBynT9UoNi5uKbPmSDmeoGw8GkgGuwXGqawdqPcJ7BQZ6asXsZ5mQGR/kKbCgDc7OMgw6o3woKUBEAnYkwhA4Z7X0JuuHNMHYNJyzWasvlOqq7k7Urt4b/WARzwJJYV5aH2Sf7VBykOcQW5tn5Onet6hMw1n3zo/8dSAKsJgDK4PT6Ea1bgOgvIfXMF+ckfAAaL82Wv9ZTV/IW59cCCm0vPdaUBSxTmlMf6pkTArth5PdAKJWleyZUxl9B5T6S4wDSa63fZvCITfigKlgUEgL3XWbRWvmBZGKFwD83lNIqlPArrWmHz71M72xVgHaMEQCoXEF3/Lvb9H4FrajqtrbFeNw7G5ynWdjS5Fj9kcQg7h6cvWyvmu90fJXaGEu018JEW65xnbJ9tVgYopjiJC4CJNZVLiAT6CAoAmWyu9aiGwwTZzxPSQE+kgOto+3421iVf50pQ3OLBdX4jwEmXaBMeTQHV9dn1OlHLIUbLD10P1W50hx14OowHEyPzBSb4Vc6sWD8EBDb04P6NyX2YTQAICW6OpPv7JwusVxgDqyoA2L5cxqTp74YxbxjC8Ezmx1ng2qmw6/3M1u0wJeYJCkgOCsks2c0yU4YYa5l3t+B/t8+0FQlhjz2HT8z4KxCnkJ7J7MVvQQZimTHBG3mMQRAAlZBmU9ZKOxpclzxOafqT/5i7+OD7vwN7oIudlfVpX2xg96T9XkpkGUX7f72QMQqSTrZkDNzex4VhSRRsj5nzfL8QjDb7dUSf9CgIaizMSgjA4aqgD7FemjNskntBQ/WysG7IzN6dB2yaOQ0U3CxSGmjRQZBuKWg3/ICZbJWxDhXa9prTuQuSMra+wNx/RTYwY2gVyujgQmQRuoLg/esATguvMbkAYa9Dcr3xWsPt/uJCIIEAwPv4l/9JcyOxBgOZLsROKsJzIRO+HmMkYbUIxNxuZ/dnmfG5bA1zkKyxkAtW+twp8L6wNc0z8EZeu9PHCqkiALhgvwETSaRnhzndERRiZMBvhiklDHTxhSRpoMCLzxWUtcsEmOllewH+XoB9cy/cR4m7maQvNsG8m4R0QT8D98nPYAGK9RIApAFnLdgqrAlNrllcQrAh7OiC4GJUIVg+ZHQKAsAT1vcCoaCnIF3TarCUBugzzX9LuKdqhTGdAlPHQ7+nEBCdyQ6nfZbDXIurLAPjCRWozSe0ADxW7NhZpZCsExiWqIDh+atWq8EUwq4QwfoSY6R2Xs8ImdfJoCgVHda0CHM1jZ85clOtLGQ79bEA4NmvdKlTYXO6W4jy99+o+CEDhlTTF9lcPAeB7ELEWSuGuAd9DO478IBOyMqrMNf8vKr50OYhhQ2UhftkEWiINXefwEYaRH4+P0Xf4bAaCOOrG9qsqaJVoCC+mtAC8NgBu821cCgkOF9iWk5njOfrFBiQXZvp7D1jMBsGTPSTERE1Zg2B1SbvDnHFOAkAeP8VMYsd7XxeESFQjCt2zRiZPPaaX4D1Rl6wK3vfqlBrg+87zUWYV6khuFcAMZwq1EwsFKwgn+pIBsasIepEC5W5OB9ysAAkAeBiAdj3zGWfXwK1HnEL0x5j13rVuuE6qmgA28KiZhkXeA+0gppn1LDNdByYp+UMA94vgtui2eICtRIAVgi8Y/2Krmm/4MMeRz7uDennIXGrJWFtNxSY6Xw2B+cIc/BAkjRjYX5GQe1D3BgA5vOPjMms7PXGcu0b7uH0uAoKPNedAgM+1zI7ev0mV66oRiKfFKcGvn+0ndcomHYmANBK3SVFHcNYIQ7yHhbeZSwA7D69TuC728YRprDfNvCXFsltHdzbNhSnGeDKNIZRoC8TzZlJ0h/CIhc66oBsCnnfr9ZAsJkYw+HNlipaQwFQZlp7MWOAq7zjWGZ626pxFvSzWVs5m6HD3Hi7pt2DMMfThDmuKgDgvb9Ici9w7YcFoVJKAmkidQKE+5zLnnuu4KrZO+2+gOtfLrhENmXzuZC7JY0QSgrAFjKnds9MEBIMshQAxwo812RBrSRhVtVE4UTm7C+tMvRj5qK6uE/mQUOSeriE7ASvWUPBNgP87oUWFgB2zrZLyrRYAKsrg5qRBaxY7W2o5B4JBTcfwcEckBalEeZo64QWgD0PxyRhmrDGFwma+B8TMkCseOVurSfgfYOtQsWyXlasxbzCXB3H3rNQ4C/HJxVCcN0LBdfiUYILKksX0GchJbbCYKUnc8wmDgInFNDGBmTsEIp1DmKpopnkmJOb6Ut1FAIo2C7KULDh5lwuVbQFBQCmBQ5Jc9B5NofZ3FRgthG5hsY6jjHk70VG9A/I2thZYBDXZVWsSK8rQtvUOHUAdj53SChM7Rp/S2BWN6W0KoZS9ovPUmU/1dG305z9zpsznteBkFlj5/WWMAsArJ/xKbCM7Jx+XYhVnV4LAcDgSU6G5/VYDcdfyCran1w8XWHoqFm7T8ZazI0s3Se0sabWq2ECcx1MroVg85eCXO3R6LhALQQAfP7hlMw/z6DHb6OD/i+26eMMDujXLRxkrkUWM0w4eJhVpLoWgpl73jAJ+iys0ZcFq+LihFYFMrTFgtI2FLGPeLEXFU79gQqsEg+6xnyrEUtWCAoA+P+7AM+RJgaxB4PlEBtFZSUA2F6Z7lDFXabrm7MzlUD3BmSFlCtNyEqEJJhJqihuVlPokYVJ3mjBxubl+41MFa2RBWA39q1p0CLpdS+GxZMFoQCwLsYjBQFweIYCoMAYe1wBUAHws6QCYEdBAFyQRgAQA1koZJKswmMEfn2oApXM6BPn9/gKWClpBMAOguV4aS0FANsnR1IWEFrfvRGV9x5ZCFfaxJSkSLnVMmpOEApN0ixqqRFNWKBQ6tNUPVmLVNG5jUIVrbEAuCqhe8HuofMESIVSBvPfRwCwTBV7//9VAwEwu4ECYKIgAM6vsQD4ekgNRI8A+pZmlNlzvcFgoxcKVkrWAsDyqEvqIAByDCWXozYjn+kl+Ide4X9zLDZUVkIANecdGNiSl+bA0gOMrncTFobnfmLGNRBl8JvWPVW02QQAXG8qq0yuCEzf87NzAR0oWJzfrYEL6KE2EwCT62wBeICiOrxBAqDmFkBILwcD1bGTvxT19rdQ/MjnxwrNCpz572TKV0FzXgsqBpNqbh5I9ZUb0TyZCbYJgMRXzrCZfQ9UnLadAGBZDkt43jbd1x3EVDahAJfL2JDy8B8XgsC20cp44f5nZ3EoWJOQV9nhb3UBsKNQFX43CdzDg/GNrAdZc1NsppEEBtcqAoDzJvb3IXQuTfD/6mA8hb1PGKospmzna5FRc4mQthSXQT5Rrb2ki6TMiGGuDdpcFi4hzCK4NQoUq4UFQIH1TMV0xbchCyaXgFEVbLCSCQAbCOy20BPw/79loXBwBsyYYasKgKG8AA/+f0u906BD7rFZBMDiGFlAUg1MzqVnMb13A0JafZMF50uYwlqrjJqvUQZH3ICenag5aXzKQm9axKNJ0kymSOZW0lRRT4jcf0j1D3VxdTVpDOBxIVj+FYBjKMYoBLPPtxGHASABsFrHJymmvxHW8isZFCzZObpeuH6rCoAhkMH1ElPkXqP+xTlYz6Sjk1KDlxtC18FmEgAvCGmzhawVPo67xdZtFeg6h0r5y3btauU+GZcAk94u2o8S+JQ//m4CfVvdUcL2EQ4hjcZRsL0fwyVUYc06bHGM6W60bju6gGBjdlv0SlaQNTCh5Wddkd/l6YhcAIQA4j1ucd4TMgo7v5sK5ncrC4CV4b23C5by5CxrAfqLBUAC71nB0hzkaAEMJEj3dWiMsNlOMRTyIljF9wtKyb61YjZdgokfRwAclQQIy4DXQQzhGeqnOo2Y3iQqVumudrAE4VCEZ9oY2y2GpF9xK8E81//6S/HnP5V5bm7/EgCW8Q0HQLYy65sbl/HZa66OYGQRAmA49B9AJn1+EmhvmNsBlLceGwyuRQTAFMGiewwQMNNAMZheFgfQGdqPuo1NhvTeXDMIAPbZR9keMAHZUVU6mtnv28XWMcBePS2BYmwB7XYXshIvqHWV7a0xBABWlu4Wg6HgAlVrzebRZD5LDHkGYZSb4qPNKM2q6MBohjCzypO0fQogX2wLe5Bh1BsjqAktgGEQAC5j7CcBYqS9txsEbP3lBEAIXAIy7JOZEtDHMmQ9AopQ/cvRG9tNAAyF5jOeAELXmWBti8LaVkDRW6PZBAB85hbhrH09Cum2yjm7MwGYXx4UV4/NzR01ZTqEBOiaGoZN6ce4HACArV6Ldd/BBiVYOedV+X7TKecF8hFfazYuZTFsQWZYF9tsR2FbQNj45rkPsXjpLhjkbSgAugQXoQfgXl0ReO3LMV/622VhMMhMANgDsSoE6bi2foMEbSHgq+TA3blQqIRtGwHAXXCsstmz7gbHfgB5VJL8T9BbS9g8PaBjI+CgGykAiizFGV1ij0ODIjHWQf9b2VqzrMve52OigVqeta+wJ66oJfb+QNQGYgiAtyC1LOeAGGkYwX0xBU1c4eBTUPslygi6ifz4u9DmLNF9TydLAplEUyCCNmkW0C2CX3K+0LFpuY5RApz0bBaXqUQIgByvmBVaMPqUW23W9nMhoFtDaP1n2Vx/qY6hjQRAHgop/yx0BDNn5DTGrKWOYDnWv2NmSEey30oB1SYRAHmABv9Q8HDMjuJx8J0zBAGyENxeeaFDXkE4KytCPwB0AR1SyxaMI7DpRowU0Ccx3Skm8mHawpIkwqGXMIQ2Zp242qEfQFoBsFtIz9lFZHkNDmm6UaB4zmUMG6Yi9ILtIwDYPRzBhRBb8woJ/gUUSHuQkDXfEjreIfpoT5sJgBzrCdwT0RN4n6gMFIrTHc2ziphwHhWS+dcUMQCYi5sZeB26r6YTxIMdR9BzH2i7GDIeit3YDpIUE2HdNiUcJZ4F9KZ1n9XK//+lOGmg2E+2GjMBc/MgyeyuYeVhhRVVYHHFt9Dk6miPjmBpKoFzIZg5FVYQaGItl/pLUQ+nU+e1Z9HFIEADzAlLA+XWGb0ehj1nQZhUy/SqCPvgaWNZQJvLcrsIAHYPk5lw9Ng5fYu6fE0nKJDzye36KPbh5fnrZl6D8UWpH3GTCoD1OYBdBL+yzPkZwEI7UUADrUADqmtNdS8pTDuTUnWoyaQkEL2P2LnqDe0JnHFl8EEJM4AuiWImsCAbk8/erwPzdypea5Z+wP1MAKxGc7fsnhxrLSxTKMHBeofaF27HqkFFAcCeazO4D2x2I42S/wlWEQb9byNze20hw6ktBADba/uBC6RshYGLqxbeV2ZpwhtW4Q/NlAVkP7sT7gd8NjZ6oXcD9kS+BNczZi1ShX2fTy7sXK0ZzZkJBcDUCKlqJ2QwTVI9sUeqIhNKHYKaUAAcB3AUVhs42MHq4gKgDMG4GSnB4FYDAD6EgyjRd/DRyzN9SOsfznrYIiT3qg4+1y5KZVzgULuCuFXGKtkerjdSEAD3hQiA22E9LHMZlVIATIB7s2t0XloBYDupwTVfDqughnvZBD7nwTNGDiGYfiNUHUft0yeZRflyRgJge9DE7fP/JIbiZPbkn3jSA1N4SuDnL7LPHyP0sUaQvBJTUMKQQi/Cjnu1tABujCkA7KRMCltkmIybMsTt9zPE/r+hHwgAyQKYEmMjbyVYANekgING3/H21Hu2p1o6r/9Jk+xfAHSEvc6aYB2GxQBybCAQoPn98/5S5MV7DFMxGRnmOuR/fpoY9ym2zSKm9lExYpntjbnsHu3rHG4BJGndyNZoZ8ECuCgDC+Apdq9/j2oEhIKVcHuedbTY7f/fp4Dp+Ki6Gbami4R4waAMBMBEIYjq1AYV25WSn//3tHe9CBdQp5C0YIrBfgzxJ9d5LJO7bYvM0EBd0RBjpoD6Yc0wgIGd2GTMnwcbhzcCxC7GugwPxq6kJU6gTJbPuGRd2fQ0yp6ZCJ8fk7IhTI411jYgcQdQSuHVVNBnYDN+aYRNME6lZ+gOgSgvUOPrHWls5xKbiQre0+Fdg7JcJMiRAhzU7QRmcTXbxzlwZe5Cc2nndFDCamgs/98Z1tjM1Xpp96XxvRMK5QR6/ZIL84MMoRVoPUx7xftNVTz59N8m993LJmBJyqOpuh8WF8eeUrb5PRYyODdD6XoTYU5Hx2juwlOIu8kqmAjD3vMWIYkPeC/7U/zk9+Qae8XOI6XEm1jKLCqOHZNpPwDH7kEvxYCB8ATQrlxIo4sK87v6zWQFQCFR08YDmjhOUUhSBZy01R9LmysI6bvFiIrNPP+/ZGWBADitGVqDNkr5kAQr8Yk1aHRbGJAs1reJ56GQpnNeiGckR1hMq9E8DmjIPIIGtjZg5sRJAX1KyPPGa74SE1uoEXGAp7iW16SbsBCFKBjz8/laAAtiPrNQExD5vWENsGE/zSQl5SlyGxj3xOfCXC/cZVRl/88TqlbF+JDUrDvtvqnVGqVqLN4XtKza2iWqlk97j/Wa05DrOd0zg6rJ1WIe06aAjk+YAnoPO0g5eNh5KVw/Hkvvq9QqTdRvQDczpUSxkHMFLf3iqDJ9x+tuJygFrzSqv0U/0IhzNQ1K6jzW14Sn1wNiZujwoEqBvV6ckvlXQnCBeMFXKvcSPO+senczU4ptpW4mFHB9ELfUniU+rMCKbsR9raTU6trVGQlTQI9DjIyOvi38vITB2bcJo/1Bf2nj8feq8/JlaVmYd+tVERD27+b6I1UINK/GRK93Ca6aFzELB819Ngqs3L7TprIyKOoPkmb1KCn1VwFwXRwBAJrzlxmA0caAGOmlSM88FHKZVyAkSqMB7m+CtoS5cT9hmLzrKBzKTDhUWGrk6S4C0BcAAANRSURBVO0a9OtHVsDmUGHpQeWwyUg5xrU3QfD/bW0RGQPuMnSWav9K7aZZzUuYAmrxdHIm3S5lsRevLO50QBftpCj6uOA796Zsjssph/ZpKO93+d6mDgbrXl3KkEkBEEvtKZ3uMqpo3ZjAvUyK6mhK2zuVisY8Bltgi5Du1z2g1G7Mv9O2QouTAkqaN+Z135zC728/86AA4ZyLm1ECbimTZjU2GHsE41hqE3kXCao3GYCYof1U++sXlsAlIWX6nEqECltyKLd/qF79npWUmkkADIvptlmGAAk42SelYP4VqFIcEcf3yoRDIYZwyBM88Bgq6jk8GFcCXrn6fpvcEqDGQBUB5K0UEn+qYPk98/vfmKb6VEmpPx+kzXlLPkf/v20HuBPCyPoJIZ1NJWSWPvgQ4VBU7b41inNg794j4Ax5DASuFNJ4xliCe9S73aeSUjMJgH0TpoBeQjADf09R7GWvdWo9A7ARwkG1v37mDrIAZoS58gzgEoUhLf6VsKn2BPyWgq69UrtmAJ2aMAX0eIuMmDLoe6v63ZVSVLrmWdzHAHBt7S9tYnIUxX4OJtz1sVh2j9aEklK7CoCZjgLAg1ZnFdK2krh9UGA8HYVOqKQUA5OoGPP9yviVNAjs0J+Xd1ryyO1TTpjrj9Cxm6j2r5Sxa4+79ZbLHFNFQ0kPyvKY4YslHz6rpvWpqcevyHc6yOb8x/X9x+lqpaSkpKRUOwHQTZDOvK0eZgS9Qv0/RzPX0QkJ/P/WzfQj1fyVlJSUGltQs6nQbcoy9Qf8pY2iV2RBt05oVPJejPoBy/wfsFkXao4rKSkpNS4FdA+mxb9BpfTjBCzrvPD5WY5WAMLsrqPFVkpKSkqNzwA6nRj476gH6FDHdntS381q8M7mdaK6fpSUlJSawwU0yVTyJk2RI+vgsSpCwLp+TtCgr5KSklI/6QHq2FTj2xF1BPZvN6vbR0lJSan5KikLKRvKrwWY/J7UN9hARWuxl5KSklJrBpOvZUzfCoIlNqCsfn8lJSWl1mzSsQ3WElhBELwepH5/JSUlpRYnaK5t0Rh/qJq/kpKSUnuklB4Lwd/7tNhLSUlJqX1gJVYnn/8bJjDcoVk/SkpKSm0VDJ4RjAPU9aOkpKSUPf0bpUz0aIAR5P0AAAAASUVORK5CYII=";
9
+
10
+ /** Aspect ratio (width / height) of the trimmed lockup. */
11
+ const WORDMARK_ASPECT = 4.000;
12
+
13
+ export interface FragmentsWordmarkProps {
14
+ /** Rendered height in px; width follows the lockup aspect ratio. */
15
+ height?: number;
16
+ /** Optional className */
17
+ className?: string;
18
+ }
19
+
20
+ /**
21
+ * The Fragments logo lockup — star + wordmark as one image. Use this anywhere
22
+ * the brand is shown with its name, instead of the icon beside a text label.
23
+ * Themeable: the artwork is a CSS mask filled with currentColor.
24
+ */
25
+ export function FragmentsWordmark({ height = 20, className }: FragmentsWordmarkProps) {
26
+ return (
27
+ <span
28
+ role="img"
29
+ aria-label="Fragments"
30
+ className={className}
31
+ style={{
32
+ display: "inline-block",
33
+ height,
34
+ width: height * WORDMARK_ASPECT,
35
+ backgroundColor: "currentColor",
36
+ maskImage: `url("${WORDMARK_MASK}")`,
37
+ WebkitMaskImage: `url("${WORDMARK_MASK}")`,
38
+ maskRepeat: "no-repeat",
39
+ WebkitMaskRepeat: "no-repeat",
40
+ maskSize: "contain",
41
+ WebkitMaskSize: "contain",
42
+ maskPosition: "left center",
43
+ WebkitMaskPosition: "left center",
44
+ }}
45
+ />
46
+ );
47
+ }
@@ -184,7 +184,7 @@ function AccordionItem({
184
184
  className,
185
185
  ...htmlProps
186
186
  }: AccordionItemProps) {
187
- const { openItems } = useAccordionContext();
187
+ const { openItems, toggle } = useAccordionContext();
188
188
  const isOpen = openItems.includes(value);
189
189
  const baseId = React.useId();
190
190
  const triggerId = `accordion-trigger-${baseId}`;
@@ -201,7 +201,17 @@ function AccordionItem({
201
201
 
202
202
  return (
203
203
  <AccordionItemContext.Provider value={{ value, isOpen, disabled, triggerId, contentId }}>
204
- <BaseCollapsible.Root open={isOpen} disabled={disabled}>
204
+ <BaseCollapsible.Root
205
+ open={isOpen}
206
+ disabled={disabled}
207
+ onOpenChange={(nextOpen, eventDetails) => {
208
+ // Trigger presses stay in AccordionTrigger so its public onClick can cancel them.
209
+ // Base owns the browser-only beforematch event used by hiddenUntilFound panels.
210
+ if (eventDetails.event.type === "beforematch" && nextOpen !== isOpen) {
211
+ toggle(value);
212
+ }
213
+ }}
214
+ >
205
215
  <div
206
216
  {...htmlProps}
207
217
  className={classes}
@@ -285,7 +295,7 @@ function AccordionContent({
285
295
  data-state={isOpen ? "open" : "closed"}
286
296
  role="region"
287
297
  aria-labelledby={triggerId}
288
- keepMounted={keepMounted}
298
+ keepMounted={keepMounted || hiddenUntilFound}
289
299
  hiddenUntilFound={hiddenUntilFound}
290
300
  >
291
301
  <div className={styles.contentInner}>{children}</div>
@@ -3,7 +3,9 @@
3
3
 
4
4
  .alert {
5
5
  display: flex;
6
- align-items: flex-start;
6
+ // Single-line alerts (icon + one line, no title) read best vertically
7
+ // centered. Multi-line alerts top-align instead — see `:has(.title)` below.
8
+ align-items: center;
7
9
  gap: var(--fui-space-3, $fui-space-3);
8
10
  padding: var(--fui-space-2, $fui-space-2) var(--fui-space-3, $fui-space-3);
9
11
  border-radius: var(--fui-radius-lg, $fui-radius-lg);
@@ -12,6 +14,12 @@
12
14
  line-height: var(--fui-line-height-normal, $fui-line-height-normal);
13
15
  }
14
16
 
17
+ // With a title (title + description), the block is multi-line: top-align so
18
+ // the icon and close sit with the first line rather than the block's centre.
19
+ .alert:has(.title) {
20
+ align-items: flex-start;
21
+ }
22
+
15
23
  // Severity variants
16
24
  .info {
17
25
  background: var(--fui-color-info-bg, $fui-color-info-bg);
@@ -47,10 +55,15 @@
47
55
  font-size: var(--fui-font-size-xs, $fui-font-size-xs);
48
56
  font-weight: var(--fui-font-weight-semibold, $fui-font-weight-semibold);
49
57
  flex-shrink: 0;
50
- margin-top: var(--fui-space-px, $fui-space-px);
51
58
  color: var(--fui-text-inverse, $fui-text-inverse);
52
59
  }
53
60
 
61
+ // Only nudge the icon down to meet the first text line when the alert is
62
+ // multi-line; centered single-line alerts need no offset.
63
+ .alert:has(.title) .icon {
64
+ margin-top: var(--fui-space-px, $fui-space-px);
65
+ }
66
+
54
67
  .info .icon {
55
68
  background-color: var(--fui-color-info-text, $fui-color-info-text);
56
69
  }
@@ -72,7 +72,10 @@
72
72
  grid-area: header;
73
73
  position: sticky;
74
74
  top: 0;
75
- height: var(--appshell-header-height, var(--fui-appshell-header-height, $fui-appshell-header-height));
75
+ height: var(
76
+ --appshell-header-height,
77
+ var(--fui-appshell-header-height, $fui-appshell-header-height)
78
+ );
76
79
  z-index: var(--fui-header-z-index, $fui-header-z-index);
77
80
  background-color: inherit;
78
81
 
@@ -88,9 +91,22 @@
88
91
  .sidebar {
89
92
  grid-area: sidebar;
90
93
  position: sticky;
91
- top: var(--appshell-header-height, var(--fui-appshell-header-height, $fui-appshell-header-height));
92
- height: calc(100vh - var(--appshell-header-height, var(--fui-appshell-header-height, $fui-appshell-header-height)));
93
- height: calc(100dvh - var(--appshell-header-height, var(--fui-appshell-header-height, $fui-appshell-header-height)));
94
+ top: var(
95
+ --appshell-header-height,
96
+ var(--fui-appshell-header-height, $fui-appshell-header-height)
97
+ );
98
+ height: calc(
99
+ 100vh - var(
100
+ --appshell-header-height,
101
+ var(--fui-appshell-header-height, $fui-appshell-header-height)
102
+ )
103
+ );
104
+ height: calc(
105
+ 100dvh - var(
106
+ --appshell-header-height,
107
+ var(--fui-appshell-header-height, $fui-appshell-header-height)
108
+ )
109
+ );
94
110
  z-index: 30;
95
111
  overflow: hidden;
96
112
  background-color: inherit;
@@ -171,9 +187,8 @@
171
187
  border: 1px solid var(--fui-border, $fui-border);
172
188
  border-radius: var(--fui-radius-xl, $fui-radius-xl);
173
189
  background-color: var(--fui-app-main-bg, var(--fui-main-bg, #{$fui-main-bg}));
174
- box-shadow:
175
- inset 0 1px 0
176
- color-mix(in srgb, var(--fui-text-primary, #{$fui-text-primary}) 5%, transparent);
190
+ box-shadow: inset 0 1px 0
191
+ color-mix(in srgb, var(--fui-text-primary, #{$fui-text-primary}) 5%, transparent);
177
192
  overflow: hidden auto;
178
193
 
179
194
  @include below-md {
@@ -185,6 +200,25 @@
185
200
  }
186
201
  }
187
202
 
203
+ // Canonical spacing scale (shared with Card/Box/Stack/Grid): sm=2 md=3 lg=4.
204
+ // Keep these compound rules after `.mainFloating`: the explicit padding prop
205
+ // owns the content inset even when the floating surface supplies its default.
206
+ .main.paddingNone {
207
+ padding: 0;
208
+ }
209
+
210
+ .main.paddingSm {
211
+ padding: var(--fui-space-2, $fui-space-2);
212
+ }
213
+
214
+ .main.paddingMd {
215
+ padding: var(--fui-space-3, $fui-space-3);
216
+ }
217
+
218
+ .main.paddingLg {
219
+ padding: var(--fui-space-4, $fui-space-4);
220
+ }
221
+
188
222
  // ============================================
189
223
  // Aside (Right Panel)
190
224
  // ============================================
@@ -192,9 +226,22 @@
192
226
  .aside {
193
227
  grid-area: aside;
194
228
  position: sticky;
195
- top: var(--appshell-header-height, var(--fui-appshell-header-height, $fui-appshell-header-height));
196
- height: calc(100vh - var(--appshell-header-height, var(--fui-appshell-header-height, $fui-appshell-header-height)));
197
- height: calc(100dvh - var(--appshell-header-height, var(--fui-appshell-header-height, $fui-appshell-header-height)));
229
+ top: var(
230
+ --appshell-header-height,
231
+ var(--fui-appshell-header-height, $fui-appshell-header-height)
232
+ );
233
+ height: calc(
234
+ 100vh - var(
235
+ --appshell-header-height,
236
+ var(--fui-appshell-header-height, $fui-appshell-header-height)
237
+ )
238
+ );
239
+ height: calc(
240
+ 100dvh - var(
241
+ --appshell-header-height,
242
+ var(--fui-appshell-header-height, $fui-appshell-header-height)
243
+ )
244
+ );
198
245
  width: var(--aside-width, 280px);
199
246
  min-height: 0; // Allow shrinking in grid
200
247
  z-index: 30;
@@ -216,16 +263,27 @@
216
263
  // Override the explicit width so the aside fills its grid cell
217
264
  // and the margin creates visible inset space from the viewport edge.
218
265
  width: auto;
219
- height: calc(100vh - var(--appshell-header-height, var(--fui-appshell-header-height, $fui-appshell-header-height)) - var(--fui-space-1, $fui-space-1) * 2);
220
- height: calc(100dvh - var(--appshell-header-height, var(--fui-appshell-header-height, $fui-appshell-header-height)) - var(--fui-space-1, $fui-space-1) * 2);
266
+ height: calc(
267
+ 100vh - var(
268
+ --appshell-header-height,
269
+ var(--fui-appshell-header-height, $fui-appshell-header-height)
270
+ ) - var(--fui-space-1, $fui-space-1) *
271
+ 2
272
+ );
273
+ height: calc(
274
+ 100dvh - var(
275
+ --appshell-header-height,
276
+ var(--fui-appshell-header-height, $fui-appshell-header-height)
277
+ ) - var(--fui-space-1, $fui-space-1) *
278
+ 2
279
+ );
221
280
  margin: var(--fui-space-1, $fui-space-1) var(--fui-space-1, $fui-space-1)
222
281
  var(--fui-space-1, $fui-space-1) 0;
223
282
  border: 1px solid var(--fui-border, $fui-border);
224
283
  border-radius: var(--fui-radius-xl, $fui-radius-xl);
225
284
  background-color: var(--fui-panel-bg, inherit);
226
- box-shadow:
227
- inset 0 1px 0
228
- color-mix(in srgb, var(--fui-text-primary, #{$fui-text-primary}) 5%, transparent);
285
+ box-shadow: inset 0 1px 0
286
+ color-mix(in srgb, var(--fui-text-primary, #{$fui-text-primary}) 5%, transparent);
229
287
  overflow: hidden;
230
288
 
231
289
  @include below-md {
@@ -448,7 +448,7 @@ function AppShellMain({
448
448
 
449
449
  const classes = [
450
450
  styles.main,
451
- padding !== "none" && styles[`padding${padding.charAt(0).toUpperCase() + padding.slice(1)}`],
451
+ styles[`padding${padding.charAt(0).toUpperCase() + padding.slice(1)}`],
452
452
  resolvedVariant === "floating" && styles.mainFloating,
453
453
  className,
454
454
  ]
@@ -29,6 +29,14 @@
29
29
  white-space: nowrap;
30
30
  user-select: none;
31
31
 
32
+ // A composed link — `Button asChild` cloning an <a>, or an anchor rendered
33
+ // as a child — must wear the button's own foreground, never the global
34
+ // naked-link accent color (which would collide, e.g. accent-on-accent).
35
+ a {
36
+ color: inherit;
37
+ text-decoration: none;
38
+ }
39
+
32
40
  &:hover:not(:disabled):not([data-disabled]) {
33
41
  border-color: var(--_button-border-hover);
34
42
  background: var(--_button-bg-hover);
@@ -173,22 +181,17 @@
173
181
  }
174
182
 
175
183
  .danger {
176
- --_button-bg: linear-gradient(
177
- to bottom,
178
- color-mix(in srgb, var(--fui-color-danger, #{$fui-color-danger}) 82%, white 18%) 0%,
179
- var(--fui-color-danger, #{$fui-color-danger}) 52%,
180
- color-mix(in srgb, var(--fui-color-danger, #{$fui-color-danger}) 86%, black 14%) 100%
181
- );
182
- --_button-bg-hover: linear-gradient(
183
- to bottom,
184
- color-mix(in srgb, var(--fui-color-danger, #{$fui-color-danger}) 76%, white 24%) 0%,
185
- color-mix(in srgb, var(--fui-color-danger, #{$fui-color-danger}) 94%, black 6%) 52%,
186
- color-mix(in srgb, var(--fui-color-danger, #{$fui-color-danger}) 78%, black 22%) 100%
184
+ // Flat danger fill — no gradient. Hover/active darken the solid colour.
185
+ --_button-bg: var(--fui-color-danger, #{$fui-color-danger});
186
+ --_button-bg-hover: color-mix(
187
+ in srgb,
188
+ var(--fui-color-danger, #{$fui-color-danger}) 88%,
189
+ black 12%
187
190
  );
188
- --_button-bg-active: linear-gradient(
189
- to bottom,
190
- color-mix(in srgb, var(--fui-color-danger, #{$fui-color-danger}) 72%, black 28%) 0%,
191
- color-mix(in srgb, var(--fui-color-danger, #{$fui-color-danger}) 82%, black 18%) 100%
191
+ --_button-bg-active: color-mix(
192
+ in srgb,
193
+ var(--fui-color-danger, #{$fui-color-danger}) 80%,
194
+ black 20%
192
195
  );
193
196
  --_button-border: color-mix(
194
197
  in srgb,
@@ -213,11 +216,7 @@
213
216
  --_button-border: var(--fui-button-neutral-border);
214
217
  --_button-border-hover: var(--fui-button-neutral-border-hover);
215
218
  --_button-color: var(--fui-button-neutral-color, var(--fui-text-primary, #{$fui-text-primary}));
216
- --_button-shadow: inset 0 1px 0
217
- light-dark(
218
- color-mix(in srgb, white 72%, transparent),
219
- color-mix(in srgb, white 5%, transparent)
220
- );
219
+ --_button-shadow: var(--fui-button-outlined-shadow, #{$fui-button-outlined-shadow});
221
220
  --_button-shadow-hover: var(--fui-button-neutral-shadow);
222
221
  --_button-shadow-active: var(--fui-button-neutral-shadow-active);
223
222
  }
@@ -129,15 +129,15 @@
129
129
  margin-bottom: var(--fui-space-2, $fui-space-2);
130
130
  }
131
131
 
132
- // Full-bleed panel-header bar: fixed 40px rail with horizontal padding and a
133
- // hairline bottom divider. No trailing margin — it sits flush above the
132
+ // Full-bleed panel-header region with a 40px minimum height, canonical inset,
133
+ // and a hairline bottom divider. No trailing margin — it sits flush above the
134
134
  // panel body, which manages its own padding. Use inside `variant="panel"`.
135
135
  .headerDivided {
136
136
  display: flex;
137
137
  align-items: center;
138
138
  gap: var(--fui-space-1, $fui-space-1);
139
139
  min-height: 2.857rem; // 40px
140
- padding: 0 var(--fui-space-2, $fui-space-2);
140
+ padding: var(--fui-space-2, $fui-space-2);
141
141
  margin-bottom: 0;
142
142
  border-bottom: 1px solid var(--fui-border, $fui-border);
143
143
  }
@@ -34,9 +34,9 @@ export interface CardProps extends Omit<React.HTMLAttributes<HTMLElement>, "chil
34
34
 
35
35
  export interface CardHeaderProps extends React.HTMLAttributes<HTMLDivElement> {
36
36
  children: React.ReactNode;
37
- /** Renders the header as a divided bar: fixed height, horizontal padding,
38
- * hairline bottom border, no trailing margin. Used with `variant="panel"`
39
- * for dashboard-style panel headers. */
37
+ /** Renders the header as a divided region with a minimum height, canonical
38
+ * inset, hairline bottom border, and no trailing margin. Used with
39
+ * `variant="panel"` for dashboard-style panel headers. */
40
40
  divided?: boolean;
41
41
  }
42
42
 
@@ -2,7 +2,8 @@
2
2
  @use "../../tokens/mixins" as *;
3
3
 
4
4
  // Local component tokens (code blocks always dark — no light variant).
5
- // $fui-code-bg stays global (shared with Markdown's <pre> styling).
5
+ // Background and selection stay global so apps can theme CodeBlock and
6
+ // Markdown consistently without replacing either component.
6
7
  $fui-code-text: #e2e2e5 !default;
7
8
  $fui-code-text-muted: #8a8a9a !default;
8
9
  $fui-code-border: rgba(255, 255, 255, 0.06) !default;
@@ -11,12 +12,6 @@ $fui-code-tab-text-active: #e2e2e5 !default;
11
12
  $fui-code-copy-bg: rgba(255, 255, 255, 0.06) !default;
12
13
  $fui-code-copy-bg-hover: rgba(255, 255, 255, 0.12) !default;
13
14
  $fui-code-highlight-bg: rgba(255, 255, 255, 0.06) !default;
14
- // Selection tint derived from the primary action blue instead of semantic state colors.
15
- $fui-code-selection-bg: color-mix(
16
- in srgb,
17
- var(--fui-button-primary-base, #0a84ff) 30%,
18
- transparent
19
- ) !default;
20
15
  $fui-code-scrollbar-track: transparent !default;
21
16
  $fui-code-scrollbar-thumb: rgba(255, 255, 255, 0.1) !default;
22
17
  $fui-code-scrollbar-thumb-hover: rgba(255, 255, 255, 0.2) !default;
@@ -197,7 +197,7 @@
197
197
  border-color: var(--fui-form-group-border, $fui-border);
198
198
 
199
199
  // Item height derived from text-base font + vertical padding
200
- --_item-h: calc(
200
+ --_combobox-item-h: calc(
201
201
  var(--fui-font-size-sm, #{$fui-font-size-sm}) *
202
202
  var(--fui-line-height-normal, #{$fui-line-height-normal}) +
203
203
  var(--fui-space-2, #{$fui-space-2}) * 2
@@ -207,7 +207,8 @@
207
207
  // Show N items + half-peek scroll hint (default 4.5 items)
208
208
  // !important needed to override Base UI's inline max-height: 100%
209
209
  max-height: calc(
210
- var(--_item-h) * var(--fui-select-max-items, 4.5) + var(--fui-space-1, #{$fui-space-1}) * 2
210
+ var(--_combobox-item-h) * var(--fui-select-max-items, 4.5) +
211
+ var(--fui-space-1, #{$fui-space-1}) * 2
211
212
  ) !important;
212
213
  overflow-y: auto !important;
213
214
 
@@ -3,14 +3,7 @@
3
3
  "name": "DataTable",
4
4
  "description": "Data table with sorting, selection, and column management. Powered by TanStack Table.",
5
5
  "category": "display",
6
- "tags": [
7
- "table",
8
- "data",
9
- "grid",
10
- "list",
11
- "sorting",
12
- "tanstack"
13
- ],
6
+ "tags": ["table", "data", "grid", "list", "sorting", "tanstack"],
14
7
  "status": "stable",
15
8
  "sourcePath": "src/components/DataTable/index.tsx",
16
9
  "exportName": "DataTable",
@@ -26,6 +19,7 @@
26
19
  "rowSelection: object",
27
20
  "onRowSelectionChange: function",
28
21
  "onRowClick: function",
22
+ "getRowProps: function",
29
23
  "getSubRows: function",
30
24
  "expanded: union",
31
25
  "onExpandedChange: function",
@@ -97,6 +91,11 @@
97
91
  "description": "Handler for row clicks/keyboard activation. Called as (row, event)",
98
92
  "required": false
99
93
  },
94
+ "getRowProps": {
95
+ "type": "function",
96
+ "description": "Returns row-level HTML, ARIA, data, class, and event props for each rendered data row",
97
+ "required": false
98
+ },
100
99
  "getSubRows": {
101
100
  "type": "function",
102
101
  "description": "Extract sub-rows for expandable tree tables",
@@ -123,10 +122,7 @@
123
122
  "description": "Table density",
124
123
  "default": "md",
125
124
  "required": false,
126
- "values": [
127
- "sm",
128
- "md"
129
- ]
125
+ "values": ["sm", "md"]
130
126
  },
131
127
  "caption": {
132
128
  "type": "string",
@@ -181,13 +177,15 @@
181
177
  "Use consistent alignment (numbers right, text left)",
182
178
  "Provide meaningful empty states",
183
179
  "Consider mobile responsiveness",
184
- "Use showCheckbox for bulk selection workflows"
180
+ "Use showCheckbox for bulk selection workflows",
181
+ "Use getRowProps to provide an accessible name and intentional semantics for clickable rows"
185
182
  ],
186
183
  "accessibility": [
187
184
  "Proper table semantics with headers",
188
185
  "Sortable columns are keyboard accessible",
189
186
  "Row selection checkboxes include aria-labels",
190
- "Expand/collapse buttons have aria-expanded state"
187
+ "Expand/collapse buttons have aria-expanded state",
188
+ "Clickable rows expose an accessible action label through getRowProps"
191
189
  ]
192
190
  },
193
191
  "examples": [
@@ -224,7 +222,7 @@
224
222
  {
225
223
  "name": "Clickable Rows",
226
224
  "description": "Rows respond to click and keyboard activation",
227
- "code": "<DataTable\n columns={endpointColumns}\n data={sampleEndpoints}\n onRowClick={(row) => alert(`${row.method} ${row.path}`)}\n size=\"sm\"\n aria-label=\"API endpoints\"\n/>"
225
+ "code": "<DataTable\n columns={endpointColumns}\n data={sampleEndpoints}\n getRowProps={(row) => ({\n role: 'button',\n 'aria-label': `Open ${row.method} ${row.path}`,\n })}\n onRowClick={(row) => alert(`${row.method} ${row.path}`)}\n size=\"sm\"\n aria-label=\"API endpoints\"\n/>"
228
226
  },
229
227
  {
230
228
  "name": "Striped",
@@ -273,22 +271,17 @@
273
271
  "showCheckbox: boolean - add checkbox column",
274
272
  "getSubRows: (row) => T[] - enable expandable rows",
275
273
  "onRowClick: (row, event) => void - row activation handler with event access",
274
+ "getRowProps: (row) => HTMLAttributes<HTMLTableRowElement> - row-level ARIA, role, data, class, and event props",
276
275
  "size: sm|md - table density",
277
276
  "striped: boolean - alternating row backgrounds",
278
277
  "bordered: boolean - bordered container",
279
278
  "wrapperClassName / wrapperProps - style and configure the outer wrapper div"
280
279
  ],
281
- "a11yRules": [
282
- "A11Y_TABLE_HEADERS",
283
- "A11Y_TABLE_SORT"
284
- ]
280
+ "a11yRules": ["A11Y_TABLE_HEADERS", "A11Y_TABLE_SORT"]
285
281
  },
286
282
  "ai": {
287
283
  "compositionPattern": "simple",
288
- "subComponents": [
289
- "Root",
290
- "Columns"
291
- ],
284
+ "subComponents": ["Root", "Columns"],
292
285
  "commonPatterns": [
293
286
  "<DataTable columns={createColumns([{key:\"name\",header:\"Name\"},{key:\"status\",header:\"Status\"}])} data={[{name:\"Item 1\",status:\"Active\"}]} />"
294
287
  ]
@@ -82,6 +82,8 @@ export interface DataTableProps<T> extends Omit<React.HTMLAttributes<HTMLTableEl
82
82
  onRowSelectionChange?: OnChangeFn<RowSelectionState>;
83
83
  /** Row click handler */
84
84
  onRowClick?: (row: T, event: DataTableRowClickEvent) => void;
85
+ /** Props applied to each rendered data row. Use this for row-level ARIA labels, roles, and data attributes. */
86
+ getRowProps?: (row: T) => React.HTMLAttributes<HTMLTableRowElement>;
85
87
  /** Extract sub-rows from a row for expandable tree tables */
86
88
  getSubRows?: (row: T) => T[] | undefined;
87
89
  /** Controlled expanded state */
@@ -143,7 +145,11 @@ function isInteractiveTarget(target: EventTarget | null, currentTarget: HTMLTabl
143
145
  'button, a, input, select, textarea, [role="button"], [role="link"], [role="checkbox"], [role="switch"]'
144
146
  );
145
147
 
146
- return Boolean(interactiveElement && currentTarget.contains(interactiveElement));
148
+ return Boolean(
149
+ interactiveElement &&
150
+ interactiveElement !== currentTarget &&
151
+ currentTarget.contains(interactiveElement)
152
+ );
147
153
  }
148
154
 
149
155
  function DataTableRoot<T>({
@@ -158,6 +164,7 @@ function DataTableRoot<T>({
158
164
  rowSelection: controlledRowSelection,
159
165
  onRowSelectionChange,
160
166
  onRowClick,
167
+ getRowProps,
161
168
  getSubRows,
162
169
  expanded: controlledExpanded,
163
170
  onExpandedChange,
@@ -411,13 +418,25 @@ function DataTableRoot<T>({
411
418
  const isSelected = selectable ? row.getIsSelected() : false;
412
419
  const depth: number = row.depth ?? 0;
413
420
  const canExpand = hasSubRows && row.getCanExpand();
421
+ const resolvedRowProps = getRowProps?.(row.original) ?? {};
422
+ const {
423
+ className: rowClassName,
424
+ onClick: rowOnClick,
425
+ onKeyDown: rowOnKeyDown,
426
+ tabIndex: rowTabIndex,
427
+ ...rowHtmlProps
428
+ } = resolvedRowProps;
414
429
  const handleRowClick = (event: React.MouseEvent<HTMLTableRowElement>) => {
430
+ rowOnClick?.(event);
431
+ if (event.defaultPrevented) return;
415
432
  if (!onRowClick) return;
416
433
  if (isInteractiveTarget(event.target, event.currentTarget)) return;
417
434
  onRowClick(row.original, event);
418
435
  };
419
436
 
420
437
  const handleRowKeyDown = (event: React.KeyboardEvent<HTMLTableRowElement>) => {
438
+ rowOnKeyDown?.(event);
439
+ if (event.defaultPrevented) return;
421
440
  if (!onRowClick) return;
422
441
  if (isInteractiveTarget(event.target, event.currentTarget)) return;
423
442
  if (event.key === "Enter" || event.key === " ") {
@@ -428,18 +447,20 @@ function DataTableRoot<T>({
428
447
 
429
448
  return (
430
449
  <tr
450
+ {...rowHtmlProps}
431
451
  key={row.id}
432
452
  className={[
433
453
  styles.row,
434
454
  isClickable && styles.clickable,
435
455
  isSelected && styles.selected,
436
456
  depth > 0 && styles.subRow,
457
+ rowClassName,
437
458
  ]
438
459
  .filter(Boolean)
439
460
  .join(" ")}
440
- onClick={isClickable ? handleRowClick : undefined}
441
- onKeyDown={isClickable ? handleRowKeyDown : undefined}
442
- tabIndex={isClickable ? 0 : undefined}
461
+ onClick={isClickable || rowOnClick ? handleRowClick : undefined}
462
+ onKeyDown={isClickable || rowOnKeyDown ? handleRowKeyDown : undefined}
463
+ tabIndex={rowTabIndex ?? (isClickable ? 0 : undefined)}
443
464
  data-selected={isSelected || undefined}
444
465
  data-depth={depth > 0 ? depth : undefined}
445
466
  >