@devalok/shilp-sutra 0.57.0 → 0.60.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 (288) hide show
  1. package/MIGRATION.md +163 -0
  2. package/dist/_chunks/badge-group.js +75 -188
  3. package/dist/_chunks/badge-group.js.map +1 -1
  4. package/dist/_chunks/chat.js +244 -243
  5. package/dist/_chunks/chat.js.map +1 -1
  6. package/dist/_chunks/image-preview.js +71 -70
  7. package/dist/_chunks/image-preview.js.map +1 -1
  8. package/dist/_chunks/motion-preference.js +15 -0
  9. package/dist/_chunks/motion-preference.js.map +1 -0
  10. package/dist/_chunks/motion-provider.js +1 -1
  11. package/dist/_chunks/motion-provider.js.map +1 -1
  12. package/dist/_chunks/popover.js +50 -49
  13. package/dist/_chunks/popover.js.map +1 -1
  14. package/dist/_chunks/primitives-internal.js +29 -29
  15. package/dist/_chunks/primitives-internal.js.map +1 -1
  16. package/dist/_chunks/shared.js +48 -47
  17. package/dist/_chunks/shared.js.map +1 -1
  18. package/dist/_chunks/success.js +34 -34
  19. package/dist/_chunks/tree-view.js +1 -1
  20. package/dist/_chunks/tree-view.js.map +1 -1
  21. package/dist/ai/block-renderer.js +8 -8
  22. package/dist/ai/command-bar.js +65 -65
  23. package/dist/ai/command-bar.js.map +1 -1
  24. package/dist/ai/conversation.js +35 -35
  25. package/dist/ai/conversation.js.map +1 -1
  26. package/dist/ai/index.js +4 -4
  27. package/dist/composed/activity-feed.d.ts.map +1 -1
  28. package/dist/composed/activity-feed.js +108 -107
  29. package/dist/composed/activity-feed.js.map +1 -1
  30. package/dist/composed/avatar-group.js +3 -3
  31. package/dist/composed/avatar-group.js.map +1 -1
  32. package/dist/composed/bulk-action-bar.d.ts +9 -35
  33. package/dist/composed/bulk-action-bar.d.ts.map +1 -1
  34. package/dist/composed/bulk-action-bar.js +2 -139
  35. package/dist/composed/command-palette.js +137 -137
  36. package/dist/composed/command-palette.js.map +1 -1
  37. package/dist/composed/content-card.js +1 -1
  38. package/dist/composed/content-card.js.map +1 -1
  39. package/dist/composed/date-picker/index.js +5 -5
  40. package/dist/composed/date-picker/index.js.map +1 -1
  41. package/dist/composed/empty-state.js +4 -4
  42. package/dist/composed/file-preview/image-preview.d.ts.map +1 -1
  43. package/dist/composed/file-preview/shared.d.ts.map +1 -1
  44. package/dist/composed/file-preview/video-preview.d.ts.map +1 -1
  45. package/dist/composed/file-preview.js +151 -150
  46. package/dist/composed/file-preview.js.map +1 -1
  47. package/dist/composed/filter-bar.d.ts.map +1 -1
  48. package/dist/composed/filter-bar.js +53 -52
  49. package/dist/composed/filter-bar.js.map +1 -1
  50. package/dist/composed/form-section.d.ts.map +1 -1
  51. package/dist/composed/form-section.js +38 -37
  52. package/dist/composed/form-section.js.map +1 -1
  53. package/dist/composed/index.js +6 -5
  54. package/dist/composed/master-detail.d.ts.map +1 -1
  55. package/dist/composed/master-detail.js +1 -1
  56. package/dist/composed/master-detail.js.map +1 -1
  57. package/dist/composed/multi-select-popover.d.ts.map +1 -1
  58. package/dist/composed/multi-select-popover.js +109 -108
  59. package/dist/composed/multi-select-popover.js.map +1 -1
  60. package/dist/composed/rich-chat-input/attachment-strip.d.ts.map +1 -1
  61. package/dist/composed/rich-chat-input/reply-banner.d.ts.map +1 -1
  62. package/dist/composed/rich-chat-input/schedule-send.d.ts.map +1 -1
  63. package/dist/composed/rich-chat-input.d.ts.map +1 -1
  64. package/dist/composed/rich-chat-input.js +467 -466
  65. package/dist/composed/rich-chat-input.js.map +1 -1
  66. package/dist/composed/rich-text-editor.js +3 -3
  67. package/dist/composed/rich-text-editor.js.map +1 -1
  68. package/dist/composed/schedule-view.js +2 -2
  69. package/dist/composed/schedule-view.js.map +1 -1
  70. package/dist/composed/status-badge.d.ts.map +1 -1
  71. package/dist/composed/status-badge.js +29 -28
  72. package/dist/composed/status-badge.js.map +1 -1
  73. package/dist/motion/index.d.ts +1 -0
  74. package/dist/motion/index.d.ts.map +1 -1
  75. package/dist/motion/index.js +2 -15
  76. package/dist/motion/motion-preference.d.ts +36 -0
  77. package/dist/motion/motion-preference.d.ts.map +1 -0
  78. package/dist/motion/motion-provider.d.ts +2 -1
  79. package/dist/motion/motion-provider.d.ts.map +1 -1
  80. package/dist/motion/primitives-index.js +103 -102
  81. package/dist/motion/primitives-index.js.map +1 -1
  82. package/dist/motion/primitives.d.ts.map +1 -1
  83. package/dist/shell/bottom-navbar.js +19 -19
  84. package/dist/shell/bottom-navbar.js.map +1 -1
  85. package/dist/shell/index.d.ts +1 -1
  86. package/dist/shell/index.d.ts.map +1 -1
  87. package/dist/shell/notification-center.d.ts +17 -0
  88. package/dist/shell/notification-center.d.ts.map +1 -1
  89. package/dist/shell/notification-center.js +109 -104
  90. package/dist/shell/notification-center.js.map +1 -1
  91. package/dist/shell/notification-preferences.js +1 -1
  92. package/dist/shell/notification-preferences.js.map +1 -1
  93. package/dist/shell/top-bar.js +1 -1
  94. package/dist/shell/top-bar.js.map +1 -1
  95. package/dist/tokens/index.css +1 -0
  96. package/dist/tokens/palettes.css +292 -0
  97. package/dist/tokens/semantic.css +157 -11
  98. package/dist/tokens/shilp-sutra.css +1 -0
  99. package/dist/tokens/utilities.css +10 -7
  100. package/dist/ui/accordion.d.ts.map +1 -1
  101. package/dist/ui/accordion.js +27 -26
  102. package/dist/ui/accordion.js.map +1 -1
  103. package/dist/ui/alert-dialog.d.ts.map +1 -1
  104. package/dist/ui/alert-dialog.js +60 -59
  105. package/dist/ui/alert-dialog.js.map +1 -1
  106. package/dist/ui/alert.d.ts.map +1 -1
  107. package/dist/ui/alert.js +55 -113
  108. package/dist/ui/alert.js.map +1 -1
  109. package/dist/ui/autocomplete.js +80 -80
  110. package/dist/ui/autocomplete.js.map +1 -1
  111. package/dist/ui/avatar.d.ts.map +1 -1
  112. package/dist/ui/avatar.js +71 -70
  113. package/dist/ui/avatar.js.map +1 -1
  114. package/dist/ui/badge-indicator.d.ts +4 -8
  115. package/dist/ui/badge-indicator.d.ts.map +1 -1
  116. package/dist/ui/badge-indicator.js +3 -8
  117. package/dist/ui/badge-indicator.js.map +1 -1
  118. package/dist/ui/badge.d.ts +8 -1
  119. package/dist/ui/badge.d.ts.map +1 -1
  120. package/dist/ui/banner.d.ts.map +1 -1
  121. package/dist/ui/banner.js +42 -40
  122. package/dist/ui/banner.js.map +1 -1
  123. package/dist/ui/bulk-action-bar.d.ts +59 -0
  124. package/dist/ui/bulk-action-bar.d.ts.map +1 -0
  125. package/dist/ui/bulk-action-bar.js +145 -0
  126. package/dist/ui/bulk-action-bar.js.map +1 -0
  127. package/dist/ui/button-group.d.ts.map +1 -1
  128. package/dist/ui/button-group.js +3 -23
  129. package/dist/ui/button-group.js.map +1 -1
  130. package/dist/ui/button.d.ts +10 -1
  131. package/dist/ui/button.d.ts.map +1 -1
  132. package/dist/ui/button.js +79 -212
  133. package/dist/ui/button.js.map +1 -1
  134. package/dist/ui/card.d.ts.map +1 -1
  135. package/dist/ui/card.js +75 -72
  136. package/dist/ui/card.js.map +1 -1
  137. package/dist/ui/charts/radar-chart.d.ts.map +1 -1
  138. package/dist/ui/charts/radar-chart.js +1 -1
  139. package/dist/ui/charts/radar-chart.js.map +1 -1
  140. package/dist/ui/chat/message-list.d.ts.map +1 -1
  141. package/dist/ui/chat/message.d.ts.map +1 -1
  142. package/dist/ui/chat/typing-indicator.d.ts.map +1 -1
  143. package/dist/ui/checkbox.d.ts.map +1 -1
  144. package/dist/ui/checkbox.js +1 -1
  145. package/dist/ui/checkbox.js.map +1 -1
  146. package/dist/ui/code.js +1 -1
  147. package/dist/ui/code.js.map +1 -1
  148. package/dist/ui/color-input.d.ts.map +1 -1
  149. package/dist/ui/color-input.js +151 -150
  150. package/dist/ui/color-input.js.map +1 -1
  151. package/dist/ui/combobox.d.ts +14 -0
  152. package/dist/ui/combobox.d.ts.map +1 -1
  153. package/dist/ui/combobox.js +162 -158
  154. package/dist/ui/combobox.js.map +1 -1
  155. package/dist/ui/context-menu.d.ts.map +1 -1
  156. package/dist/ui/context-menu.js +70 -69
  157. package/dist/ui/context-menu.js.map +1 -1
  158. package/dist/ui/data-table-body.d.ts.map +1 -1
  159. package/dist/ui/data-table-body.js +4 -3
  160. package/dist/ui/data-table-body.js.map +1 -1
  161. package/dist/ui/data-table-bulk-actions.d.ts +26 -1
  162. package/dist/ui/data-table-bulk-actions.d.ts.map +1 -1
  163. package/dist/ui/data-table-bulk-actions.js +21 -46
  164. package/dist/ui/data-table-bulk-actions.js.map +1 -1
  165. package/dist/ui/data-table-context.d.ts +36 -2
  166. package/dist/ui/data-table-context.d.ts.map +1 -1
  167. package/dist/ui/data-table-context.js +25 -21
  168. package/dist/ui/data-table-context.js.map +1 -1
  169. package/dist/ui/data-table-header.d.ts.map +1 -1
  170. package/dist/ui/data-table-header.js +42 -40
  171. package/dist/ui/data-table-header.js.map +1 -1
  172. package/dist/ui/data-table-pagination.js +2 -2
  173. package/dist/ui/data-table-pagination.js.map +1 -1
  174. package/dist/ui/dialog.d.ts.map +1 -1
  175. package/dist/ui/dialog.js +61 -60
  176. package/dist/ui/dialog.js.map +1 -1
  177. package/dist/ui/dot.d.ts.map +1 -1
  178. package/dist/ui/dot.js +10 -41
  179. package/dist/ui/dot.js.map +1 -1
  180. package/dist/ui/dropdown-menu.d.ts.map +1 -1
  181. package/dist/ui/dropdown-menu.js +79 -78
  182. package/dist/ui/dropdown-menu.js.map +1 -1
  183. package/dist/ui/file-upload.d.ts.map +1 -1
  184. package/dist/ui/file-upload.js +116 -115
  185. package/dist/ui/file-upload.js.map +1 -1
  186. package/dist/ui/form.d.ts.map +1 -1
  187. package/dist/ui/form.js +27 -26
  188. package/dist/ui/form.js.map +1 -1
  189. package/dist/ui/hover-card.d.ts.map +1 -1
  190. package/dist/ui/hover-card.js +26 -25
  191. package/dist/ui/hover-card.js.map +1 -1
  192. package/dist/ui/icon.js +7 -7
  193. package/dist/ui/index.d.ts +3 -2
  194. package/dist/ui/index.d.ts.map +1 -1
  195. package/dist/ui/index.js +60 -59
  196. package/dist/ui/index.js.map +1 -1
  197. package/dist/ui/input.d.ts.map +1 -1
  198. package/dist/ui/input.js +7 -7
  199. package/dist/ui/input.js.map +1 -1
  200. package/dist/ui/menubar.d.ts.map +1 -1
  201. package/dist/ui/menubar.js +73 -72
  202. package/dist/ui/menubar.js.map +1 -1
  203. package/dist/ui/navigation-menu.d.ts.map +1 -1
  204. package/dist/ui/navigation-menu.js +80 -79
  205. package/dist/ui/navigation-menu.js.map +1 -1
  206. package/dist/ui/popover.d.ts.map +1 -1
  207. package/dist/ui/progress.d.ts.map +1 -1
  208. package/dist/ui/progress.js +72 -68
  209. package/dist/ui/progress.js.map +1 -1
  210. package/dist/ui/radio.d.ts.map +1 -1
  211. package/dist/ui/radio.js +35 -34
  212. package/dist/ui/radio.js.map +1 -1
  213. package/dist/ui/search-input.d.ts.map +1 -1
  214. package/dist/ui/search-input.js +28 -27
  215. package/dist/ui/search-input.js.map +1 -1
  216. package/dist/ui/select.d.ts.map +1 -1
  217. package/dist/ui/select.js +70 -69
  218. package/dist/ui/select.js.map +1 -1
  219. package/dist/ui/sidebar.d.ts.map +1 -1
  220. package/dist/ui/sidebar.js +4 -4
  221. package/dist/ui/sidebar.js.map +1 -1
  222. package/dist/ui/slider.d.ts +63 -9
  223. package/dist/ui/slider.d.ts.map +1 -1
  224. package/dist/ui/slider.js +74 -32
  225. package/dist/ui/slider.js.map +1 -1
  226. package/dist/ui/split-button.d.ts +8 -2
  227. package/dist/ui/split-button.d.ts.map +1 -1
  228. package/dist/ui/split-button.js +49 -95
  229. package/dist/ui/split-button.js.map +1 -1
  230. package/dist/ui/stat-card.d.ts.map +1 -1
  231. package/dist/ui/stat-card.js +89 -88
  232. package/dist/ui/stat-card.js.map +1 -1
  233. package/dist/ui/stat-flash.js +16 -16
  234. package/dist/ui/stepper.d.ts.map +1 -1
  235. package/dist/ui/stepper.js +63 -62
  236. package/dist/ui/stepper.js.map +1 -1
  237. package/dist/ui/switch.d.ts.map +1 -1
  238. package/dist/ui/switch.js +2 -2
  239. package/dist/ui/switch.js.map +1 -1
  240. package/dist/ui/table.d.ts.map +1 -1
  241. package/dist/ui/table.js +3 -3
  242. package/dist/ui/table.js.map +1 -1
  243. package/dist/ui/tabs.d.ts.map +1 -1
  244. package/dist/ui/tabs.js +67 -66
  245. package/dist/ui/tabs.js.map +1 -1
  246. package/dist/ui/textarea.d.ts.map +1 -1
  247. package/dist/ui/textarea.js +7 -7
  248. package/dist/ui/textarea.js.map +1 -1
  249. package/dist/ui/toast.d.ts.map +1 -1
  250. package/dist/ui/toast.js +196 -195
  251. package/dist/ui/toast.js.map +1 -1
  252. package/dist/ui/toggle.d.ts.map +1 -1
  253. package/dist/ui/toggle.js +24 -22
  254. package/dist/ui/toggle.js.map +1 -1
  255. package/dist/ui/tooltip.d.ts.map +1 -1
  256. package/dist/ui/tooltip.js +37 -36
  257. package/dist/ui/tooltip.js.map +1 -1
  258. package/dist/ui/tree-view/tree-item.d.ts.map +1 -1
  259. package/docs/components/composed/bulk-action-bar.md +14 -7
  260. package/docs/components/ui/bulk-action-bar.md +72 -0
  261. package/docs/recipes/customize-brand.md +58 -0
  262. package/docs/recipes/install-astro.md +1 -1
  263. package/docs/recipes/install-next-app-router.md +1 -1
  264. package/docs/recipes/install-next-pages.md +22 -1
  265. package/docs/recipes/install-remix.md +1 -1
  266. package/docs/recipes/install-tanstack-start.md +1 -1
  267. package/docs/recipes/install-vite.md +2 -1
  268. package/docs/recipes/server-components.md +7 -4
  269. package/docs/recipes/troubleshoot.md +1 -0
  270. package/llms.txt +2 -2
  271. package/make-kit/foundations/color.md +3 -1
  272. package/mcp-manifest.json +184 -151
  273. package/package.json +8 -2
  274. package/skill/SKILL.md +1 -1
  275. package/skill/references/components.md +2 -2
  276. package/skill/references/customize-brand.md +58 -0
  277. package/skill/references/server-components.md +7 -4
  278. package/skill/references/setup-astro.md +1 -1
  279. package/skill/references/setup-next-app-router.md +1 -1
  280. package/skill/references/setup-next-pages.md +22 -1
  281. package/skill/references/setup-remix.md +1 -1
  282. package/skill/references/setup-tanstack-start.md +1 -1
  283. package/skill/references/setup-vite.md +2 -1
  284. package/skill/references/troubleshoot.md +1 -0
  285. package/dist/composed/bulk-action-bar.js.map +0 -1
  286. package/dist/motion/check-motion-provider.d.ts +0 -2
  287. package/dist/motion/check-motion-provider.d.ts.map +0 -1
  288. package/dist/motion/index.js.map +0 -1
package/MIGRATION.md CHANGED
@@ -4,6 +4,169 @@ This page indexes all breaking changes across `@devalok/shilp-sutra` versions. F
4
4
 
5
5
  > **Upgrading from < 0.36?** Start here, then read each intermediate version section. Breaking changes stack — skipping versions means stacking migrations.
6
6
 
7
+ ## v0.60.0 — no code changes needed, but several controls look different
8
+
9
+ **Nothing breaks at the TypeScript level.** No prop removed, no type narrowed,
10
+ no export dropped. You do not have to change any code to take this release.
11
+
12
+ It is not "additive" either, and the distinction matters if you keep visual
13
+ snapshots: several controls change appearance, and one screen-reader
14
+ announcement changes. Everything below happens on upgrade, with no opt-out.
15
+
16
+ ### Form controls take a distinct ground
17
+
18
+ Inputs, Textareas, Selects and Comboboxes now sit on their own `--color-field`
19
+ token rather than a surface value — `neutral-1` in light, `neutral-2` in dark.
20
+ Hover moves with them. Validation edges move from colour step 7 to step 8, which
21
+ takes them **over** the WCAG 1.4.11 3:1 bar for the first time (error
22
+ 2.75 → 3.82:1, success 2.49 → 3.36:1).
23
+
24
+ If you override field backgrounds, check them against the new token.
25
+
26
+ ### Switch loses its resting stroke
27
+
28
+ An unchecked Switch is now carried by its fill alone. Validation states still
29
+ paint the border — the 2px is reserved and transparent, so the thumb does not
30
+ move when a field errors.
31
+
32
+ If you relied on the resting outline to find an off switch on a busy surface,
33
+ it is now 1.639:1 against the panel rather than 2.006:1. Recorded as
34
+ `SWITCH-RESTING-STROKE-REMOVED` in `docs/deviations.md`.
35
+
36
+ ### Avatar role rings are fainter
37
+
38
+ Step 7 → 6 on `lead`, `admin` and `client`. Measured on white the lead ring goes
39
+ 2.865 → 2.095:1. **The ring is the only thing conveying role** — no label, no
40
+ icon, no tooltip — so if role matters in your UI, add a text affordance. This is
41
+ a known, accepted shortfall (`AVATAR-ROLE-RING-STEP-6`), not an oversight.
42
+
43
+ ### Disabled elements are slightly more legible
44
+
45
+ `--action-disabled-opacity` 38% → 45%, globally. Every `opacity-action-disabled`
46
+ consumer moves. This improves every case measured; the worst
47
+ (`surface-fg-subtle`) goes 1.673 → 1.859:1.
48
+
49
+ ### DataTable's bulk-action bar changed announcement and emphasis
50
+
51
+ It now uses the shared `BulkActionBar` internally. `bulkActions`,
52
+ `bulkActionsPosition` and `onClick(selectedRows)` are unchanged — nothing to
53
+ migrate — but:
54
+
55
+ - the toolbar's accessible name is the stable `"Bulk actions"` rather than
56
+ `"N items selected"`, which was re-announced on every selection change;
57
+ - non-destructive actions render `ghost` rather than `outline`; destructive
58
+ (`color: 'error'`) actions stay `solid`;
59
+ - it gains roving focus (Arrow / Home / End), inline confirmation via
60
+ `requiresConfirmation`, per-action `loading`, and reduced-motion support —
61
+ props `BulkAction` already declared and the previous implementation ignored.
62
+
63
+ `BulkActionBar`'s implementation moved from `composed/bulk-action-bar` to
64
+ `ui/bulk-action-bar`. **The old import path still works**; a new one is also
65
+ available.
66
+
67
+ ### Shadows
68
+
69
+ Light shadows are 1/255 lighter — invisible, done to match the Figma library
70
+ exactly. Dark shadows changed more: they now render as shadows rather than
71
+ faint glows on the page background, and the pressed ring is visible in dark for
72
+ the first time (it measured 1.012:1, i.e. nothing).
73
+
74
+ ### If you keep visual snapshots
75
+
76
+ Expect diffs on: any form control, Switch, Avatar with a role ring, anything
77
+ disabled, DataTable with bulk actions, and anything elevated in dark mode.
78
+
79
+ ## v0.59.0 — additive (no migration required)
80
+
81
+ Nothing breaks at the TypeScript level; this release is additive.
82
+
83
+ > Auto-stubbed by `version-packages` so the release audit's MIGRATION-section gate can't block. If any behavioral or visual change needs consumer action, replace this stub with the specifics on the Version Packages PR before publishing.
84
+
85
+ ## v0.58.0 — nothing errors, several things look different
86
+
87
+ No API is removed and no prop type narrows, so `tsc` and your build stay green
88
+ on upgrade day. The reason this section exists anyway is that a number of
89
+ components **change appearance**, and two of them start rendering something that
90
+ previously rendered nothing at all. If you screenshot-test, expect diffs.
91
+
92
+ ### Two features that were silently no-ops now work
93
+
94
+ Both were painted in a colour identical to their own background in light mode,
95
+ so they emitted markup and showed nothing:
96
+
97
+ | | before | after |
98
+ |---|---|---|
99
+ | `<Table striped>` | 1.00:1 — no stripes rendered at all | visible zebra |
100
+ | `<Progress>` groove | 1.00:1 — no track behind the bar | visible track |
101
+
102
+ If you worked around either of these — a local `nth-child` stripe rule, a
103
+ wrapper drawing a track behind Progress — remove the workaround, or you will now
104
+ have two.
105
+
106
+ ### Selected rows are more prominent, and correctly so
107
+
108
+ Selection fills moved from accent step 2 to step 4. In dark, steps 1–2 sit
109
+ *below* the panel in lightness while the grey hover sits above it, so a selected
110
+ row receded exactly as a hovered row advanced: the sidebar measured 1.03:1
111
+ selected against 1.30:1 hovered. The wrong row was louder, in the opposite
112
+ direction.
113
+
114
+ Affects Sidebar, MasterDetail, MultiSelectPopover, NotificationCenter, Combobox,
115
+ Autocomplete, Table, BottomNavbar, ScheduleView, TreeView and Toggle. Nothing to
116
+ do unless you override those fills yourself, in which case re-check them against
117
+ your hover state.
118
+
119
+ ### `border-card-strong` is now actually stronger than `border-card`
120
+
121
+ They resolved to the same value. Both pointed at `--color-surface-border`, so
122
+ roughly thirteen call sites asking for the heavier edge silently got the faint
123
+ one. `border-card-strong` now resolves to `--color-surface-border-strong`:
124
+
125
+ | | light | dark |
126
+ |---|---|---|
127
+ | before | 1.22:1 | 1.33:1 |
128
+ | after | 1.38:1 | 1.63:1 |
129
+
130
+ If you picked `border-card-strong` *because* it looked identical to
131
+ `border-card`, switch those to `border-card`.
132
+
133
+ ### The Sidebar's divider was a full-contrast line, and is now a hairline
134
+
135
+ `Sidebar` had a `border-r` with no colour utility. Tailwind 4 leaves an
136
+ uncoloured border at `currentColor` rather than defaulting it to grey, so it
137
+ inherited the text colour and drew at **12.69:1 in light and 15.44:1 in dark**
138
+ where `surface-border` (1.23:1 / 1.47:1) was intended.
139
+
140
+ This is the one place an upgrade might *remove* something you had come to rely
141
+ on. If you overrode the sidebar edge downstream to tame it, drop the override.
142
+
143
+ ### Raised surfaces gain an edge in dark
144
+
145
+ `--shadow-edge-ring` is deliberately swapped to a light ring under `.dark`,
146
+ because a dark ring cannot be seen on a dark ground. Only the larger shadows
147
+ consumed it, so `shadow-raised` and `shadow-raised-hover` kept a near-black ring
148
+ measuring 1.01:1 — no edge at all. `Sidebar variant="floating"`, `Card
149
+ variant="elevated"`, `Menubar` and the keyboard caps had no visible boundary in
150
+ dark. They now measure 1.21:1, still quieter than a floating overlay's 1.42:1.
151
+
152
+ ### Colour is a role now, not a variant
153
+
154
+ The palette layer landed in the same release. Twelve components stopped encoding
155
+ colour as a cross-product of variants and now resolve it through
156
+ `[data-palette]`. Existing `color="…"` values all still type-check — the union
157
+ widened rather than narrowed — and the visual result is unchanged for the six
158
+ built-in palettes. The gain is that a colour we do not ship can now be
159
+ registered in CSS.
160
+
161
+ ### Also in this release
162
+
163
+ Segmented control and Tabs: the selected pill was *darker* than the groove it
164
+ sat in, in dark (1.028:1, inverted). Read-only Input and Textarea, DataTable
165
+ pinned columns and expanded rows, code blocks in RichTextEditor, ContentCard's
166
+ default variant and AI message bubbles all stopped painting themselves in their
167
+ own background colour. Full measurements in
168
+ `docs/audits/2026-08-28-surface-model-audit.md`.
169
+
7
170
  ## v0.57.0 — one token removed, one renamed, and a colour change you will see
8
171
 
9
172
  The surface model was rebuilt. Almost all of it is aliased, so `tsc` and your
@@ -1,142 +1,28 @@
1
1
  "use client";
2
2
  import { Slot as e } from "./primitives-internal.js";
3
3
  import { durations as t, springs as n } from "../ui/lib/motion.js";
4
- import { cn as r } from "../ui/lib/utils.js";
5
- import { Icon as i } from "../ui/icon.js";
6
- import { Dot as a } from "../ui/dot.js";
7
- import { normalizeIcon as o } from "./normalize-icon.js";
8
- import { BadgeIndicator as s } from "../ui/badge-indicator.js";
9
- import * as c from "react";
10
- import { jsx as l, jsxs as u } from "react/jsx-runtime";
11
- import { IconCheck as d, IconX as f } from "@tabler/icons-react";
12
- import { AnimatePresence as p, motion as m } from "framer-motion";
13
- import { cva as h } from "class-variance-authority";
4
+ import { MotionPreference as r } from "./motion-preference.js";
5
+ import { cn as i } from "../ui/lib/utils.js";
6
+ import { Icon as a } from "../ui/icon.js";
7
+ import { Dot as o } from "../ui/dot.js";
8
+ import { normalizeIcon as s } from "./normalize-icon.js";
9
+ import { BadgeIndicator as c } from "../ui/badge-indicator.js";
10
+ import * as l from "react";
11
+ import { jsx as u, jsxs as d } from "react/jsx-runtime";
12
+ import { IconCheck as f, IconX as p } from "@tabler/icons-react";
13
+ import { AnimatePresence as m, motion as h } from "framer-motion";
14
+ import { cva as g } from "class-variance-authority";
14
15
  //#region src/ui/badge.tsx
15
- var g = {
16
- default: {
17
- bg: "bg-surface-panel-hover",
18
- softBg: "bg-surface-panel-hover",
19
- fg: "text-surface-fg-muted",
20
- border: "border-surface-border-strong",
21
- solid: "bg-neutral-5",
22
- solidFg: "text-surface-fg"
23
- },
24
- accent: {
25
- bg: "bg-accent-2",
26
- softBg: "bg-accent-3",
27
- fg: "text-accent-11",
28
- border: "border-accent-4",
29
- solid: "bg-accent-9",
30
- solidFg: "text-accent-fg"
31
- },
32
- error: {
33
- bg: "bg-error-2",
34
- softBg: "bg-error-3",
35
- fg: "text-error-11",
36
- border: "border-error-4",
37
- solid: "bg-error-9",
38
- solidFg: "text-error-fg"
39
- },
40
- success: {
41
- bg: "bg-success-2",
42
- softBg: "bg-success-3",
43
- fg: "text-success-11",
44
- border: "border-success-4",
45
- solid: "bg-success-9",
46
- solidFg: "text-success-fg"
47
- },
48
- warning: {
49
- bg: "bg-warning-2",
50
- softBg: "bg-warning-3",
51
- fg: "text-warning-11",
52
- border: "border-warning-4",
53
- solid: "bg-warning-9",
54
- solidFg: "text-warning-fg"
55
- },
56
- info: {
57
- bg: "bg-info-2",
58
- softBg: "bg-info-3",
59
- fg: "text-info-11",
60
- border: "border-info-4",
61
- solid: "bg-info-9",
62
- solidFg: "text-info-fg"
63
- },
64
- neutral: {
65
- bg: "bg-surface-panel-hover",
66
- softBg: "bg-surface-panel-hover",
67
- fg: "text-surface-fg-muted",
68
- border: "border-surface-border-strong",
69
- solid: "bg-neutral-5",
70
- solidFg: "text-surface-fg"
71
- },
72
- teal: {
73
- bg: "bg-category-teal-2",
74
- softBg: "bg-category-teal-3",
75
- fg: "text-category-teal-11",
76
- border: "border-category-teal-4",
77
- solid: "bg-category-teal-9",
78
- solidFg: "text-category-fg"
79
- },
80
- amber: {
81
- bg: "bg-category-amber-2",
82
- softBg: "bg-category-amber-3",
83
- fg: "text-category-amber-11",
84
- border: "border-category-amber-4",
85
- solid: "bg-category-amber-9",
86
- solidFg: "text-category-fg"
87
- },
88
- slate: {
89
- bg: "bg-category-slate-2",
90
- softBg: "bg-category-slate-3",
91
- fg: "text-category-slate-11",
92
- border: "border-category-slate-4",
93
- solid: "bg-category-slate-9",
94
- solidFg: "text-category-fg"
95
- },
96
- indigo: {
97
- bg: "bg-category-indigo-2",
98
- softBg: "bg-category-indigo-3",
99
- fg: "text-category-indigo-11",
100
- border: "border-category-indigo-4",
101
- solid: "bg-category-indigo-9",
102
- solidFg: "text-category-fg"
103
- },
104
- cyan: {
105
- bg: "bg-category-cyan-2",
106
- softBg: "bg-category-cyan-3",
107
- fg: "text-category-cyan-11",
108
- border: "border-category-cyan-4",
109
- solid: "bg-category-cyan-9",
110
- solidFg: "text-category-fg"
111
- },
112
- orange: {
113
- bg: "bg-category-orange-2",
114
- softBg: "bg-category-orange-3",
115
- fg: "text-category-orange-11",
116
- border: "border-category-orange-4",
117
- solid: "bg-category-orange-9",
118
- solidFg: "text-category-fg"
119
- },
120
- emerald: {
121
- bg: "bg-category-emerald-2",
122
- softBg: "bg-category-emerald-3",
123
- fg: "text-category-emerald-11",
124
- border: "border-category-emerald-4",
125
- solid: "bg-category-emerald-9",
126
- solidFg: "text-category-fg"
127
- }
128
- };
129
16
  function _(e, t) {
130
17
  if (t === "custom") return "";
131
- let n = g[t];
132
18
  switch (e) {
133
- case "subtle": return r(n.bg, n.fg, n.border);
134
- case "solid": return r(n.solid, n.solidFg, "border-transparent");
135
- case "outline": return r("bg-transparent", n.fg, n.border);
136
- case "soft": return r(n.softBg, n.fg, "border-transparent");
19
+ case "subtle": return "bg-palette-subtle text-palette-text border-palette-border";
20
+ case "solid": return "bg-palette-solid text-palette-fg border-transparent";
21
+ case "outline": return "bg-transparent text-palette-text border-palette-border";
22
+ case "soft": return "bg-palette-soft text-palette-text border-transparent";
137
23
  }
138
24
  }
139
- var v = h("relative inline-flex items-center rounded-pill font-sans font-medium overflow-hidden isolate transition-[color,background-color,border-color,padding] duration-fast-02 ease-productive-standard select-none [&>span:not([data-grain])]:relative [&>span:not([data-grain])]:z-[2]", {
25
+ var v = g("relative inline-flex items-center rounded-pill font-sans font-medium overflow-hidden isolate transition-[color,background-color,border-color,padding] duration-fast-02 ease-productive-standard select-none [&>span:not([data-grain])]:relative [&>span:not([data-grain])]:z-[2]", {
140
26
  variants: {
141
27
  variant: {
142
28
  subtle: "border",
@@ -170,67 +56,68 @@ var v = h("relative inline-flex items-center rounded-pill font-sans font-medium
170
56
  sm: "pr-ds-02",
171
57
  md: "pr-ds-02",
172
58
  lg: "pr-ds-02b"
173
- }, S = c.forwardRef(({ className: s, variant: c, color: h, size: g, asChild: S, startIcon: C, endIcon: w, dot: T, dotPulse: E, onDismiss: D, onClick: O, selected: k, disabled: A, maxWidth: j, truncate: M, circle: N, children: P, style: F, ...I }, L) => {
174
- let R = c ?? "subtle", z = h ?? "default", B = g ?? "md", V = S ? e : O && D ? "div" : O ? "button" : "span", H = z === "custom" ? {
175
- ...R === "subtle" && {
59
+ }, S = l.forwardRef(({ className: c, variant: l, color: g, size: S, asChild: C, startIcon: w, endIcon: T, dot: E, dotPulse: D, onDismiss: O, onClick: k, selected: A, disabled: j, maxWidth: M, truncate: N, circle: P, children: F, style: I, ...L }, R) => {
60
+ let z = l ?? "subtle", B = g ?? "default", V = S ?? "md", H = C ? e : k && O ? "div" : k ? "button" : "span", U = B === "custom" ? {
61
+ ...z === "subtle" && {
176
62
  backgroundColor: "color-mix(in oklch, var(--badge-color) 15%, transparent)",
177
63
  color: "var(--badge-color)",
178
64
  borderColor: "color-mix(in oklch, var(--badge-color) 40%, transparent)"
179
65
  },
180
- ...R === "solid" && {
66
+ ...z === "solid" && {
181
67
  backgroundColor: "var(--badge-color)",
182
68
  color: "var(--badge-fg-color, white)",
183
69
  borderColor: "transparent"
184
70
  },
185
- ...R === "outline" && {
71
+ ...z === "outline" && {
186
72
  backgroundColor: "transparent",
187
73
  color: "var(--badge-color)",
188
74
  borderColor: "color-mix(in oklch, var(--badge-color) 50%, transparent)"
189
75
  },
190
- ...R === "soft" && {
76
+ ...z === "soft" && {
191
77
  backgroundColor: "color-mix(in oklch, var(--badge-color) 12%, transparent)",
192
78
  color: "var(--badge-color)",
193
79
  borderColor: "transparent"
194
80
  }
195
- } : void 0, U = O && D ? (e) => {
196
- (e.key === "Enter" || e.key === " ") && (e.preventDefault(), A || O(e)), I.onKeyDown?.(e);
197
- } : I.onKeyDown, W = !!(T || C || O && k && !C && !T), G = !!(D || w);
198
- return /* @__PURE__ */ u(V, {
199
- ref: L,
200
- className: r(v({
201
- variant: R,
202
- size: B
203
- }), _(R, z), W && b[B], G && x[B], O && "cursor-pointer hover:brightness-[0.97] focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-1 transition-[color,background-color,border-color,transform,filter] duration-moderate-01 ease-productive-exit hover:duration-fast-02 hover:ease-productive-entrance active:scale-[0.95] active:brightness-[0.92] active:duration-[0ms]", k && "ring-1 ring-current/20 transition-shadow duration-fast-02", A && "opacity-action-disabled pointer-events-none saturate-[0.3]", N && "justify-center px-0 aspect-square", s),
81
+ } : void 0, W = k && O ? (e) => {
82
+ (e.key === "Enter" || e.key === " ") && (e.preventDefault(), j || k(e)), L.onKeyDown?.(e);
83
+ } : L.onKeyDown, G = !!(E || w || k && A && !w && !E), K = !!(O || T);
84
+ return /* @__PURE__ */ u(r, { children: /* @__PURE__ */ d(H, {
85
+ ref: R,
86
+ "data-palette": B === "custom" ? void 0 : B,
87
+ className: i(v({
88
+ variant: z,
89
+ size: V
90
+ }), _(z, B), G && b[V], K && x[V], k && "cursor-pointer hover:brightness-[0.97] focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-1 transition-[color,background-color,border-color,transform,filter] duration-moderate-01 ease-productive-exit hover:duration-fast-02 hover:ease-productive-entrance active:scale-[0.95] active:brightness-[0.92] active:duration-[0ms]", A && "ring-1 ring-current/20 transition-shadow duration-fast-02", j && "opacity-action-disabled pointer-events-none saturate-[0.3]", P && "justify-center px-0 aspect-square", c),
204
91
  style: {
205
- ...F,
206
- ...H,
207
- ...j ? { maxWidth: j } : void 0
92
+ ...I,
93
+ ...U,
94
+ ...M ? { maxWidth: M } : void 0
208
95
  },
209
- onClick: A ? void 0 : O,
210
- onKeyDown: U,
211
- ...V === "button" && { type: "button" },
212
- ...V === "button" && A ? { disabled: !0 } : {},
213
- ...V === "div" && O ? {
96
+ onClick: j ? void 0 : k,
97
+ onKeyDown: W,
98
+ ...H === "button" && { type: "button" },
99
+ ...H === "button" && j ? { disabled: !0 } : {},
100
+ ...H === "div" && k ? {
214
101
  role: "button",
215
102
  tabIndex: 0
216
103
  } : {},
217
- ...I,
104
+ ...L,
218
105
  children: [
219
- /* @__PURE__ */ l(p, { children: T && /* @__PURE__ */ l(m.span, {
106
+ /* @__PURE__ */ u(m, { children: E && /* @__PURE__ */ u(h.span, {
220
107
  initial: { scale: 0 },
221
108
  animate: { scale: 1 },
222
109
  exit: { scale: 0 },
223
110
  transition: n.snappy,
224
111
  className: "inline-flex shrink-0",
225
- children: /* @__PURE__ */ l(a, {
112
+ children: /* @__PURE__ */ u(o, {
226
113
  color: "current",
227
114
  size: "sm",
228
- pulse: E
115
+ pulse: D
229
116
  })
230
117
  }, "dot") }),
231
- O && !C && !T && /* @__PURE__ */ l(m.span, {
118
+ k && !w && !E && /* @__PURE__ */ u(h.span, {
232
119
  initial: !1,
233
- animate: k ? {
120
+ animate: A ? {
234
121
  opacity: 1,
235
122
  scale: 1,
236
123
  width: "auto",
@@ -252,60 +139,60 @@ var v = h("relative inline-flex items-center rounded-pill font-sans font-medium
252
139
  ]
253
140
  },
254
141
  className: "inline-flex shrink-0 overflow-hidden",
255
- "aria-hidden": !k,
256
- children: /* @__PURE__ */ l(i, {
257
- icon: d,
142
+ "aria-hidden": !A,
143
+ children: /* @__PURE__ */ u(a, {
144
+ icon: f,
258
145
  size: "xs"
259
146
  })
260
147
  }),
261
- C && /* @__PURE__ */ l("span", {
262
- className: r("shrink-0", y[B]),
263
- children: o(C)
148
+ w && /* @__PURE__ */ u("span", {
149
+ className: i("shrink-0", y[V]),
150
+ children: s(w)
264
151
  }),
265
- M || j ? /* @__PURE__ */ l("span", {
152
+ N || M ? /* @__PURE__ */ u("span", {
266
153
  className: "truncate",
267
- title: typeof P == "string" ? P : void 0,
268
- children: P
269
- }) : P,
270
- w && /* @__PURE__ */ l("span", {
271
- className: r("shrink-0", y[B]),
272
- children: o(w)
154
+ title: typeof F == "string" ? F : void 0,
155
+ children: F
156
+ }) : F,
157
+ T && /* @__PURE__ */ u("span", {
158
+ className: i("shrink-0", y[V]),
159
+ children: s(T)
273
160
  }),
274
- D && /* @__PURE__ */ l("button", {
161
+ O && /* @__PURE__ */ u("button", {
275
162
  type: "button",
276
163
  onClick: (e) => {
277
- e.stopPropagation(), D();
164
+ e.stopPropagation(), O();
278
165
  },
279
- className: r("shrink-0 rounded-pill text-current/60 hover:text-current hover:bg-current/10 transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-accent-9", B === "xs" ? "p-0 -mr-0.5 min-w-ds-05 min-h-ds-05" : "p-px"),
280
- "aria-label": `Remove ${typeof P == "string" ? P : ""}`.trim() || "Remove",
281
- children: /* @__PURE__ */ l(i, {
282
- icon: f,
166
+ className: i("shrink-0 rounded-pill text-current/60 hover:text-current hover:bg-current/10 transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-accent-9", V === "xs" ? "p-0 -mr-0.5 min-w-ds-05 min-h-ds-05" : "p-px"),
167
+ "aria-label": `Remove ${typeof F == "string" ? F : ""}`.trim() || "Remove",
168
+ children: /* @__PURE__ */ u(a, {
169
+ icon: p,
283
170
  size: "xs"
284
171
  })
285
172
  })
286
173
  ]
287
- });
174
+ }) });
288
175
  });
289
176
  S.displayName = "Badge";
290
177
  var C = Object.assign(S, {
291
- Indicator: s,
178
+ Indicator: c,
292
179
  Group: T
293
180
  }), w = {
294
181
  tight: "gap-1",
295
182
  default: "gap-1.5",
296
183
  loose: "gap-2"
297
- }, T = c.forwardRef(function({ max: e, gap: t = "default", size: n, onOverflowClick: i, className: a, children: o, ...s }, l) {
298
- let d = c.Children.toArray(o), f = d.length, p = e !== void 0 && f > e, m = p ? d.slice(0, e) : d, h = p ? f - e : 0;
299
- return /* @__PURE__ */ u("div", {
300
- ref: l,
301
- className: r("flex flex-wrap items-center", w[t], a),
184
+ }, T = l.forwardRef(function({ max: e, gap: t = "default", size: n, onOverflowClick: r, className: a, children: o, ...s }, c) {
185
+ let u = l.Children.toArray(o), f = u.length, p = e !== void 0 && f > e, m = p ? u.slice(0, e) : u, h = p ? f - e : 0;
186
+ return /* @__PURE__ */ d("div", {
187
+ ref: c,
188
+ className: i("flex flex-wrap items-center", w[t], a),
302
189
  ...s,
303
- children: [m, p && /* @__PURE__ */ u(C, {
190
+ children: [m, p && /* @__PURE__ */ d(C, {
304
191
  variant: "outline",
305
192
  color: "neutral",
306
193
  size: n ?? "sm",
307
- onClick: i,
308
- "aria-label": i ? `Show ${h} more` : void 0,
194
+ onClick: r,
195
+ "aria-label": r ? `Show ${h} more` : void 0,
309
196
  children: ["+", h]
310
197
  })]
311
198
  });
@@ -1 +1 @@
1
- {"version":3,"file":"badge-group.js","names":[],"sources":["../../src/ui/badge.tsx","../../src/ui/badge-group.tsx"],"sourcesContent":["'use client'\n\nimport { Slot } from '@primitives/react-slot'\nimport { IconCheck, IconX } from '@tabler/icons-react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { AnimatePresence, motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Dot } from './dot'\nimport { Icon } from './icon'\nimport type { IconInput } from './lib/icon-input'\nimport { durations,springs } from './lib/motion'\nimport { normalizeIcon } from './lib/normalize-icon'\nimport { cn } from './lib/utils'\n\n// ── Color map — single source of truth for all badge color × variant combos ──\nconst colorMap = {\n default: { bg: 'bg-surface-panel-hover', softBg: 'bg-surface-panel-hover', fg: 'text-surface-fg-muted', border: 'border-surface-border-strong', solid: 'bg-neutral-5', solidFg: 'text-surface-fg' },\n accent: { bg: 'bg-accent-2', softBg: 'bg-accent-3', fg: 'text-accent-11', border: 'border-accent-4', solid: 'bg-accent-9', solidFg: 'text-accent-fg' },\n error: { bg: 'bg-error-2', softBg: 'bg-error-3', fg: 'text-error-11', border: 'border-error-4', solid: 'bg-error-9', solidFg: 'text-error-fg' },\n success: { bg: 'bg-success-2', softBg: 'bg-success-3', fg: 'text-success-11', border: 'border-success-4', solid: 'bg-success-9', solidFg: 'text-success-fg' },\n warning: { bg: 'bg-warning-2', softBg: 'bg-warning-3', fg: 'text-warning-11', border: 'border-warning-4', solid: 'bg-warning-9', solidFg: 'text-warning-fg' },\n info: { bg: 'bg-info-2', softBg: 'bg-info-3', fg: 'text-info-11', border: 'border-info-4', solid: 'bg-info-9', solidFg: 'text-info-fg' },\n neutral: { bg: 'bg-surface-panel-hover', softBg: 'bg-surface-panel-hover', fg: 'text-surface-fg-muted', border: 'border-surface-border-strong', solid: 'bg-neutral-5', solidFg: 'text-surface-fg' },\n teal: { bg: 'bg-category-teal-2', softBg: 'bg-category-teal-3', fg: 'text-category-teal-11', border: 'border-category-teal-4', solid: 'bg-category-teal-9', solidFg: 'text-category-fg' },\n amber: { bg: 'bg-category-amber-2', softBg: 'bg-category-amber-3', fg: 'text-category-amber-11', border: 'border-category-amber-4', solid: 'bg-category-amber-9', solidFg: 'text-category-fg' },\n slate: { bg: 'bg-category-slate-2', softBg: 'bg-category-slate-3', fg: 'text-category-slate-11', border: 'border-category-slate-4', solid: 'bg-category-slate-9', solidFg: 'text-category-fg' },\n indigo: { bg: 'bg-category-indigo-2', softBg: 'bg-category-indigo-3', fg: 'text-category-indigo-11', border: 'border-category-indigo-4', solid: 'bg-category-indigo-9', solidFg: 'text-category-fg' },\n cyan: { bg: 'bg-category-cyan-2', softBg: 'bg-category-cyan-3', fg: 'text-category-cyan-11', border: 'border-category-cyan-4', solid: 'bg-category-cyan-9', solidFg: 'text-category-fg' },\n orange: { bg: 'bg-category-orange-2', softBg: 'bg-category-orange-3', fg: 'text-category-orange-11', border: 'border-category-orange-4', solid: 'bg-category-orange-9', solidFg: 'text-category-fg' },\n emerald: { bg: 'bg-category-emerald-2', softBg: 'bg-category-emerald-3', fg: 'text-category-emerald-11', border: 'border-category-emerald-4', solid: 'bg-category-emerald-9', solidFg: 'text-category-fg' },\n} as const\n\ntype BadgeColor = keyof typeof colorMap | 'custom'\n\n/** Derive color classes for a given variant + color pair */\nfunction getColorClasses(variant: 'subtle' | 'solid' | 'outline' | 'soft', color: BadgeColor): string {\n if (color === 'custom') return ''\n const c = colorMap[color]\n switch (variant) {\n case 'subtle': return cn(c.bg, c.fg, c.border)\n case 'solid': return cn(c.solid, c.solidFg, 'border-transparent')\n case 'outline': return cn('bg-transparent', c.fg, c.border)\n // soft keeps the original step-3 fill. subtle moved to step 2; soft did not,\n // so the two cannot share one key (design call, Yogin/Goutham 2026-08-24).\n case 'soft': return cn(c.softBg, c.fg, 'border-transparent')\n }\n}\n\nconst badgeVariants = cva(\n 'relative inline-flex items-center rounded-pill font-sans font-medium overflow-hidden isolate transition-[color,background-color,border-color,padding] duration-fast-02 ease-productive-standard select-none [&>span:not([data-grain])]:relative [&>span:not([data-grain])]:z-[2]',\n {\n variants: {\n variant: {\n subtle: 'border',\n solid: 'border border-transparent',\n outline: 'border',\n soft: 'border border-transparent',\n },\n size: {\n xs: 'h-4 px-ds-02b text-body-xs gap-ds-02',\n sm: 'h-5 px-ds-03 text-body-xs gap-ds-02',\n md: 'h-6 px-2.5 text-body-xs gap-ds-02b', /* px-2.5=10px — no exact DS token */\n lg: 'h-7 px-ds-04 text-body-sm gap-ds-02b',\n },\n },\n defaultVariants: {\n variant: 'subtle',\n size: 'md',\n },\n },\n)\n\n/** Icon sizing per badge size */\nconst iconSizeMap: Record<string, string> = {\n xs: '[&>svg]:h-2.5 [&>svg]:w-2.5',\n sm: '[&>svg]:h-3 [&>svg]:w-3',\n md: '[&>svg]:h-3 [&>svg]:w-3',\n lg: '[&>svg]:h-3.5 [&>svg]:w-3.5',\n}\n\n/** Reduced left padding when leading content (icon/dot/selected) is present */\nconst paddingLeftWithIcon: Record<string, string> = {\n xs: 'pl-ds-02',\n sm: 'pl-ds-02b',\n md: 'pl-ds-03',\n lg: 'pl-2.5', /* 10px — no exact DS token */\n}\n\n/** Reduced right padding when trailing content (dismiss/endIcon) is present */\nconst paddingRightWithTrailing: Record<string, string> = {\n xs: 'pr-ds-01',\n sm: 'pr-ds-02',\n md: 'pr-ds-02',\n lg: 'pr-ds-02b',\n}\n\n/**\n * Badge — a compact inline label with a two-axis variant system.\n *\n * **Two axes:**\n * - `variant` controls **visual style**: `\"subtle\"` | `\"solid\"` | `\"outline\"` | `\"soft\"`\n * - `color` controls **semantic intent/category**: 16 built-in colors + `\"custom\"` (CSS variable)\n *\n * **Interactive:** Pass `onClick` to make it a button. Pass `selected` for toggle state.\n * **Dismissible:** Pass `onDismiss` to show a dismiss button.\n * **Custom colors:** Set `color=\"custom\"` and CSS variable `--badge-color` on a parent or via `style`.\n * For light custom colors on solid variant, set `--badge-fg-color` to a dark value.\n *\n * @example\n * <Badge color=\"success\">Active</Badge>\n *\n * @example\n * <Badge variant=\"solid\" size=\"sm\" dot>3 new</Badge>\n *\n * @example\n * <Badge color=\"custom\" style={{ '--badge-color': '#8b5cf6' } as React.CSSProperties}>\n * Custom\n * </Badge>\n */\ninterface BadgeProps\n extends Omit<React.HTMLAttributes<HTMLElement>, 'color'>,\n VariantProps<typeof badgeVariants> {\n /** Semantic intent or category color. Use `\"custom\"` with `--badge-color` CSS variable. */\n color?: BadgeColor\n asChild?: boolean\n startIcon?: IconInput\n endIcon?: IconInput\n dot?: boolean\n /** Pulse the status dot (opt-in; static by default). @default false */\n dotPulse?: boolean\n onDismiss?: () => void\n selected?: boolean\n disabled?: boolean\n maxWidth?: number\n /**\n * Truncate children with ellipsis when they overflow the badge width.\n * Use with a fixed width (e.g. `className=\"w-20\"`) or `maxWidth` for\n * constrained pill badges. Always adds a title tooltip on the full text.\n */\n truncate?: boolean\n circle?: boolean\n}\n\nconst Badge = React.forwardRef<HTMLElement, BadgeProps>(\n (\n {\n className,\n variant,\n color,\n size,\n asChild,\n startIcon,\n endIcon,\n dot,\n dotPulse,\n onDismiss,\n onClick,\n selected,\n disabled,\n maxWidth,\n truncate,\n circle,\n children,\n style,\n ...props\n },\n ref,\n ) => {\n const resolvedVariant = variant ?? 'subtle'\n const resolvedColor = color ?? 'default'\n const resolvedSize = size ?? 'md'\n\n // When both onClick + onDismiss, use div with role=\"button\" to avoid nested <button>\n const Comp = asChild\n ? Slot\n : onClick && onDismiss\n ? 'div'\n : onClick\n ? 'button'\n : 'span'\n\n // Custom color inline styles via CSS variable\n const customStyles =\n resolvedColor === 'custom'\n ? {\n ...(resolvedVariant === 'subtle' && {\n backgroundColor: 'color-mix(in oklch, var(--badge-color) 15%, transparent)',\n color: 'var(--badge-color)',\n borderColor: 'color-mix(in oklch, var(--badge-color) 40%, transparent)',\n }),\n ...(resolvedVariant === 'solid' && {\n backgroundColor: 'var(--badge-color)',\n color: 'var(--badge-fg-color, white)',\n borderColor: 'transparent',\n }),\n ...(resolvedVariant === 'outline' && {\n backgroundColor: 'transparent',\n color: 'var(--badge-color)',\n borderColor: 'color-mix(in oklch, var(--badge-color) 50%, transparent)',\n }),\n ...(resolvedVariant === 'soft' && {\n backgroundColor: 'color-mix(in oklch, var(--badge-color) 12%, transparent)',\n color: 'var(--badge-color)',\n borderColor: 'transparent',\n }),\n }\n : undefined\n\n // Keyboard handler for div[role=\"button\"] (onClick + onDismiss case)\n const handleKeyDown =\n onClick && onDismiss\n ? (e: React.KeyboardEvent<HTMLElement>) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n if (!disabled) onClick(e as unknown as React.MouseEvent<HTMLElement>)\n }\n props.onKeyDown?.(e)\n }\n : props.onKeyDown\n\n const hasLeading = !!(dot || startIcon || (onClick && selected && !startIcon && !dot))\n const hasTrailing = !!(onDismiss || endIcon)\n\n return (\n <Comp\n ref={ref as React.Ref<never>}\n className={cn(\n badgeVariants({ variant: resolvedVariant, size: resolvedSize }),\n getColorClasses(resolvedVariant, resolvedColor),\n hasLeading && paddingLeftWithIcon[resolvedSize],\n hasTrailing && paddingRightWithTrailing[resolvedSize],\n onClick &&\n 'cursor-pointer hover:brightness-[0.97] focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-1 transition-[color,background-color,border-color,transform,filter] duration-moderate-01 ease-productive-exit hover:duration-fast-02 hover:ease-productive-entrance active:scale-[0.95] active:brightness-[0.92] active:duration-[0ms]',\n selected && 'ring-1 ring-current/20 transition-shadow duration-fast-02',\n disabled && 'opacity-action-disabled pointer-events-none saturate-[0.3]',\n circle && 'justify-center px-0 aspect-square',\n className,\n )}\n style={{ ...style, ...customStyles, ...(maxWidth ? { maxWidth } : undefined) }}\n onClick={disabled ? undefined : (onClick as React.MouseEventHandler<HTMLElement>)}\n onKeyDown={handleKeyDown}\n {...(Comp === 'button' && { type: 'button' as const })}\n {...(Comp === 'button' && disabled ? { disabled: true } : {})}\n {...(Comp === 'div' && onClick ? { role: 'button' as const, tabIndex: 0 } : {})}\n {...props}\n >\n {/* Dot indicator — animated entrance, uses the shared <Dot> (current colour + pulse) */}\n <AnimatePresence>\n {dot && (\n <motion.span\n key=\"dot\"\n initial={{ scale: 0 }}\n animate={{ scale: 1 }}\n exit={{ scale: 0 }}\n transition={springs.snappy}\n className=\"inline-flex shrink-0\"\n >\n <Dot color=\"current\" size=\"sm\" pulse={dotPulse} />\n </motion.span>\n )}\n </AnimatePresence>\n\n {/* Selected check icon — always mounted for interactive badges */}\n {onClick && !startIcon && !dot && (\n <motion.span\n initial={false}\n animate={selected\n ? { opacity: 1, scale: 1, width: 'auto', marginRight: 0 }\n : { opacity: 0, scale: 0.5, width: 0, marginRight: -4 }\n }\n transition={{ type: 'tween', duration: durations.moderate01, ease: [0.2, 0, 0.38, 0.9] }} /* ease-productive-standard */\n className=\"inline-flex shrink-0 overflow-hidden\"\n aria-hidden={!selected}\n >\n <Icon icon={IconCheck} size=\"xs\" />\n </motion.span>\n )}\n\n {/* Start icon */}\n {startIcon && (\n <span className={cn('shrink-0', iconSizeMap[resolvedSize])}>{normalizeIcon(startIcon)}</span>\n )}\n\n {/* Children — with optional truncation */}\n {truncate || maxWidth ? (\n <span\n className=\"truncate\"\n title={typeof children === 'string' ? children : undefined}\n >\n {children}\n </span>\n ) : (\n children\n )}\n\n {/* End icon */}\n {endIcon && (\n <span className={cn('shrink-0', iconSizeMap[resolvedSize])}>{normalizeIcon(endIcon)}</span>\n )}\n\n {/* Dismiss button */}\n {onDismiss && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n onDismiss()\n }}\n className={cn(\n 'shrink-0 rounded-pill text-current/60 hover:text-current hover:bg-current/10 transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-accent-9',\n resolvedSize === 'xs'\n ? 'p-0 -mr-0.5 min-w-ds-05 min-h-ds-05'\n : 'p-px',\n )}\n aria-label={\n `Remove ${typeof children === 'string' ? children : ''}`.trim() || 'Remove'\n }\n >\n <Icon icon={IconX} size=\"xs\" />\n </button>\n )}\n </Comp>\n )\n },\n)\nBadge.displayName = 'Badge'\n\nimport { BadgeGroup } from './badge-group'\nimport { BadgeIndicator } from './badge-indicator'\n\nconst BadgeCompound = Object.assign(Badge, {\n Indicator: BadgeIndicator,\n Group: BadgeGroup,\n})\n\nexport { BadgeCompound as Badge, type BadgeColor,type BadgeProps, badgeVariants }\n","'use client'\n\nimport * as React from 'react'\n\nimport { Badge, type BadgeProps } from './badge'\nimport { cn } from './lib/utils'\n\nconst GAP_CLASSES = {\n tight: 'gap-1',\n default: 'gap-1.5',\n loose: 'gap-2',\n} as const\n\nexport interface BadgeGroupProps extends React.HTMLAttributes<HTMLDivElement> {\n max?: number\n gap?: keyof typeof GAP_CLASSES\n size?: BadgeProps['size']\n onOverflowClick?: () => void\n children: React.ReactNode\n}\n\nexport const BadgeGroup = React.forwardRef<HTMLDivElement, BadgeGroupProps>(function BadgeGroup(\n { max, gap = 'default', size, onOverflowClick, className, children, ...rest },\n ref,\n) {\n const childArray = React.Children.toArray(children)\n const total = childArray.length\n const hasOverflow = max !== undefined && total > max\n const visible = hasOverflow ? childArray.slice(0, max) : childArray\n const overflowCount = hasOverflow ? total - max! : 0\n\n return (\n <div\n ref={ref}\n className={cn('flex flex-wrap items-center', GAP_CLASSES[gap], className)}\n {...rest}\n >\n {visible}\n {hasOverflow && (\n // With onClick, Badge renders a real, keyboard-reachable <button>; add a\n // label since \"+N\" alone isn't descriptive to a screen reader.\n <Badge\n variant=\"outline\"\n color=\"neutral\"\n size={size ?? 'sm'}\n onClick={onOverflowClick}\n aria-label={onOverflowClick ? `Show ${overflowCount} more` : undefined}\n >\n +{overflowCount}\n </Badge>\n )}\n </div>\n )\n})\n\nBadgeGroup.displayName = 'BadgeGroup'\n"],"mappings":"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"}
1
+ {"version":3,"file":"badge-group.js","names":[],"sources":["../../src/ui/badge.tsx","../../src/ui/badge-group.tsx"],"sourcesContent":["'use client'\n\nimport { Slot } from '@primitives/react-slot'\nimport { IconCheck, IconX } from '@tabler/icons-react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { AnimatePresence, motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { MotionPreference } from '../motion/motion-preference'\nimport { Dot } from './dot'\nimport { Icon } from './icon'\nimport type { IconInput } from './lib/icon-input'\nimport { durations,springs } from './lib/motion'\nimport { normalizeIcon } from './lib/normalize-icon'\nimport { cn } from './lib/utils'\n\n// ── Color map — single source of truth for all badge color × variant combos ──\nconst colorMap = {\n default: { bg: 'bg-surface-panel-hover', softBg: 'bg-surface-panel-hover', fg: 'text-surface-fg-muted', border: 'border-surface-border-strong', solid: 'bg-neutral-5', solidFg: 'text-surface-fg' },\n accent: { bg: 'bg-accent-2', softBg: 'bg-accent-3', fg: 'text-accent-11', border: 'border-accent-4', solid: 'bg-accent-9', solidFg: 'text-accent-fg' },\n error: { bg: 'bg-error-2', softBg: 'bg-error-3', fg: 'text-error-11', border: 'border-error-4', solid: 'bg-error-9', solidFg: 'text-error-fg' },\n success: { bg: 'bg-success-2', softBg: 'bg-success-3', fg: 'text-success-11', border: 'border-success-4', solid: 'bg-success-9', solidFg: 'text-success-fg' },\n warning: { bg: 'bg-warning-2', softBg: 'bg-warning-3', fg: 'text-warning-11', border: 'border-warning-4', solid: 'bg-warning-9', solidFg: 'text-warning-fg' },\n info: { bg: 'bg-info-2', softBg: 'bg-info-3', fg: 'text-info-11', border: 'border-info-4', solid: 'bg-info-9', solidFg: 'text-info-fg' },\n neutral: { bg: 'bg-surface-panel-hover', softBg: 'bg-surface-panel-hover', fg: 'text-surface-fg-muted', border: 'border-surface-border-strong', solid: 'bg-neutral-5', solidFg: 'text-surface-fg' },\n teal: { bg: 'bg-category-teal-2', softBg: 'bg-category-teal-3', fg: 'text-category-teal-11', border: 'border-category-teal-4', solid: 'bg-category-teal-9', solidFg: 'text-category-fg' },\n amber: { bg: 'bg-category-amber-2', softBg: 'bg-category-amber-3', fg: 'text-category-amber-11', border: 'border-category-amber-4', solid: 'bg-category-amber-9', solidFg: 'text-category-fg' },\n slate: { bg: 'bg-category-slate-2', softBg: 'bg-category-slate-3', fg: 'text-category-slate-11', border: 'border-category-slate-4', solid: 'bg-category-slate-9', solidFg: 'text-category-fg' },\n indigo: { bg: 'bg-category-indigo-2', softBg: 'bg-category-indigo-3', fg: 'text-category-indigo-11', border: 'border-category-indigo-4', solid: 'bg-category-indigo-9', solidFg: 'text-category-fg' },\n cyan: { bg: 'bg-category-cyan-2', softBg: 'bg-category-cyan-3', fg: 'text-category-cyan-11', border: 'border-category-cyan-4', solid: 'bg-category-cyan-9', solidFg: 'text-category-fg' },\n orange: { bg: 'bg-category-orange-2', softBg: 'bg-category-orange-3', fg: 'text-category-orange-11', border: 'border-category-orange-4', solid: 'bg-category-orange-9', solidFg: 'text-category-fg' },\n emerald: { bg: 'bg-category-emerald-2', softBg: 'bg-category-emerald-3', fg: 'text-category-emerald-11', border: 'border-category-emerald-4', solid: 'bg-category-emerald-9', solidFg: 'text-category-fg' },\n} as const\n\n/**\n * A palette name. These fourteen ship with the design system; any palette\n * registered in CSS (`[data-palette='…']`) works too, which is why the type\n * stays open. `custom` is the older escape hatch — it drives colour through\n * inline styles from a `--badge-color` variable and is superseded by\n * registering a palette, but still works.\n */\ntype BadgeColor = keyof typeof colorMap | 'custom' | (string & {})\n\n/**\n * Colour classes per variant. Four lines instead of a 14 × 6 map — the hue\n * arrives via `data-palette` on the element.\n *\n * `subtle` and `soft` map to two different roles on purpose. subtle moved to\n * step 2 and soft stayed at step 3 (design call, Yogin/Goutham 2026-08-24), and\n * the palette contract keeps them as separate roles precisely so that decision\n * survives rather than being averaged away.\n */\nfunction getColorClasses(variant: 'subtle' | 'solid' | 'outline' | 'soft', color: BadgeColor): string {\n if (color === 'custom') return ''\n switch (variant) {\n case 'subtle': return 'bg-palette-subtle text-palette-text border-palette-border'\n case 'solid': return 'bg-palette-solid text-palette-fg border-transparent'\n case 'outline': return 'bg-transparent text-palette-text border-palette-border'\n case 'soft': return 'bg-palette-soft text-palette-text border-transparent'\n }\n}\n\nconst badgeVariants = cva(\n 'relative inline-flex items-center rounded-pill font-sans font-medium overflow-hidden isolate transition-[color,background-color,border-color,padding] duration-fast-02 ease-productive-standard select-none [&>span:not([data-grain])]:relative [&>span:not([data-grain])]:z-[2]',\n {\n variants: {\n variant: {\n subtle: 'border',\n solid: 'border border-transparent',\n outline: 'border',\n soft: 'border border-transparent',\n },\n size: {\n xs: 'h-4 px-ds-02b text-body-xs gap-ds-02',\n sm: 'h-5 px-ds-03 text-body-xs gap-ds-02',\n md: 'h-6 px-2.5 text-body-xs gap-ds-02b', /* px-2.5=10px — no exact DS token */\n lg: 'h-7 px-ds-04 text-body-sm gap-ds-02b',\n },\n },\n defaultVariants: {\n variant: 'subtle',\n size: 'md',\n },\n },\n)\n\n/** Icon sizing per badge size */\nconst iconSizeMap: Record<string, string> = {\n xs: '[&>svg]:h-2.5 [&>svg]:w-2.5',\n sm: '[&>svg]:h-3 [&>svg]:w-3',\n md: '[&>svg]:h-3 [&>svg]:w-3',\n lg: '[&>svg]:h-3.5 [&>svg]:w-3.5',\n}\n\n/** Reduced left padding when leading content (icon/dot/selected) is present */\nconst paddingLeftWithIcon: Record<string, string> = {\n xs: 'pl-ds-02',\n sm: 'pl-ds-02b',\n md: 'pl-ds-03',\n lg: 'pl-2.5', /* 10px — no exact DS token */\n}\n\n/** Reduced right padding when trailing content (dismiss/endIcon) is present */\nconst paddingRightWithTrailing: Record<string, string> = {\n xs: 'pr-ds-01',\n sm: 'pr-ds-02',\n md: 'pr-ds-02',\n lg: 'pr-ds-02b',\n}\n\n/**\n * Badge — a compact inline label with a two-axis variant system.\n *\n * **Two axes:**\n * - `variant` controls **visual style**: `\"subtle\"` | `\"solid\"` | `\"outline\"` | `\"soft\"`\n * - `color` controls **semantic intent/category**: 16 built-in colors + `\"custom\"` (CSS variable)\n *\n * **Interactive:** Pass `onClick` to make it a button. Pass `selected` for toggle state.\n * **Dismissible:** Pass `onDismiss` to show a dismiss button.\n * **Custom colors:** Set `color=\"custom\"` and CSS variable `--badge-color` on a parent or via `style`.\n * For light custom colors on solid variant, set `--badge-fg-color` to a dark value.\n *\n * @example\n * <Badge color=\"success\">Active</Badge>\n *\n * @example\n * <Badge variant=\"solid\" size=\"sm\" dot>3 new</Badge>\n *\n * @example\n * <Badge color=\"custom\" style={{ '--badge-color': '#8b5cf6' } as React.CSSProperties}>\n * Custom\n * </Badge>\n */\ninterface BadgeProps\n extends Omit<React.HTMLAttributes<HTMLElement>, 'color'>,\n VariantProps<typeof badgeVariants> {\n /** Semantic intent or category color. Use `\"custom\"` with `--badge-color` CSS variable. */\n color?: BadgeColor\n asChild?: boolean\n startIcon?: IconInput\n endIcon?: IconInput\n dot?: boolean\n /** Pulse the status dot (opt-in; static by default). @default false */\n dotPulse?: boolean\n onDismiss?: () => void\n selected?: boolean\n disabled?: boolean\n maxWidth?: number\n /**\n * Truncate children with ellipsis when they overflow the badge width.\n * Use with a fixed width (e.g. `className=\"w-20\"`) or `maxWidth` for\n * constrained pill badges. Always adds a title tooltip on the full text.\n */\n truncate?: boolean\n circle?: boolean\n}\n\nconst Badge = React.forwardRef<HTMLElement, BadgeProps>(\n (\n {\n className,\n variant,\n color,\n size,\n asChild,\n startIcon,\n endIcon,\n dot,\n dotPulse,\n onDismiss,\n onClick,\n selected,\n disabled,\n maxWidth,\n truncate,\n circle,\n children,\n style,\n ...props\n },\n ref,\n ) => {\n const resolvedVariant = variant ?? 'subtle'\n const resolvedColor = color ?? 'default'\n const resolvedSize = size ?? 'md'\n\n // When both onClick + onDismiss, use div with role=\"button\" to avoid nested <button>\n const Comp = asChild\n ? Slot\n : onClick && onDismiss\n ? 'div'\n : onClick\n ? 'button'\n : 'span'\n\n // Custom color inline styles via CSS variable\n const customStyles =\n resolvedColor === 'custom'\n ? {\n ...(resolvedVariant === 'subtle' && {\n backgroundColor: 'color-mix(in oklch, var(--badge-color) 15%, transparent)',\n color: 'var(--badge-color)',\n borderColor: 'color-mix(in oklch, var(--badge-color) 40%, transparent)',\n }),\n ...(resolvedVariant === 'solid' && {\n backgroundColor: 'var(--badge-color)',\n color: 'var(--badge-fg-color, white)',\n borderColor: 'transparent',\n }),\n ...(resolvedVariant === 'outline' && {\n backgroundColor: 'transparent',\n color: 'var(--badge-color)',\n borderColor: 'color-mix(in oklch, var(--badge-color) 50%, transparent)',\n }),\n ...(resolvedVariant === 'soft' && {\n backgroundColor: 'color-mix(in oklch, var(--badge-color) 12%, transparent)',\n color: 'var(--badge-color)',\n borderColor: 'transparent',\n }),\n }\n : undefined\n\n // Keyboard handler for div[role=\"button\"] (onClick + onDismiss case)\n const handleKeyDown =\n onClick && onDismiss\n ? (e: React.KeyboardEvent<HTMLElement>) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n if (!disabled) onClick(e as unknown as React.MouseEvent<HTMLElement>)\n }\n props.onKeyDown?.(e)\n }\n : props.onKeyDown\n\n const hasLeading = !!(dot || startIcon || (onClick && selected && !startIcon && !dot))\n const hasTrailing = !!(onDismiss || endIcon)\n\n return (\n <MotionPreference>\n <Comp\n ref={ref as React.Ref<never>}\n // `custom` keeps driving colour through inline styles from\n // `--badge-color`; every other value is a palette name.\n data-palette={resolvedColor === 'custom' ? undefined : resolvedColor}\n className={cn(\n badgeVariants({ variant: resolvedVariant, size: resolvedSize }),\n getColorClasses(resolvedVariant, resolvedColor),\n hasLeading && paddingLeftWithIcon[resolvedSize],\n hasTrailing && paddingRightWithTrailing[resolvedSize],\n onClick &&\n 'cursor-pointer hover:brightness-[0.97] focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-1 transition-[color,background-color,border-color,transform,filter] duration-moderate-01 ease-productive-exit hover:duration-fast-02 hover:ease-productive-entrance active:scale-[0.95] active:brightness-[0.92] active:duration-[0ms]',\n selected && 'ring-1 ring-current/20 transition-shadow duration-fast-02',\n disabled && 'opacity-action-disabled pointer-events-none saturate-[0.3]',\n circle && 'justify-center px-0 aspect-square',\n className,\n )}\n style={{ ...style, ...customStyles, ...(maxWidth ? { maxWidth } : undefined) }}\n onClick={disabled ? undefined : (onClick as React.MouseEventHandler<HTMLElement>)}\n onKeyDown={handleKeyDown}\n {...(Comp === 'button' && { type: 'button' as const })}\n {...(Comp === 'button' && disabled ? { disabled: true } : {})}\n {...(Comp === 'div' && onClick ? { role: 'button' as const, tabIndex: 0 } : {})}\n {...props}\n >\n {/* Dot indicator — animated entrance, uses the shared <Dot> (current colour + pulse) */}\n <AnimatePresence>\n {dot && (\n <motion.span\n key=\"dot\"\n initial={{ scale: 0 }}\n animate={{ scale: 1 }}\n exit={{ scale: 0 }}\n transition={springs.snappy}\n className=\"inline-flex shrink-0\"\n >\n <Dot color=\"current\" size=\"sm\" pulse={dotPulse} />\n </motion.span>\n )}\n </AnimatePresence>\n\n {/* Selected check icon — always mounted for interactive badges */}\n {onClick && !startIcon && !dot && (\n <motion.span\n initial={false}\n animate={selected\n ? { opacity: 1, scale: 1, width: 'auto', marginRight: 0 }\n : { opacity: 0, scale: 0.5, width: 0, marginRight: -4 }\n }\n transition={{ type: 'tween', duration: durations.moderate01, ease: [0.2, 0, 0.38, 0.9] }} /* ease-productive-standard */\n className=\"inline-flex shrink-0 overflow-hidden\"\n aria-hidden={!selected}\n >\n <Icon icon={IconCheck} size=\"xs\" />\n </motion.span>\n )}\n\n {/* Start icon */}\n {startIcon && (\n <span className={cn('shrink-0', iconSizeMap[resolvedSize])}>{normalizeIcon(startIcon)}</span>\n )}\n\n {/* Children — with optional truncation */}\n {truncate || maxWidth ? (\n <span\n className=\"truncate\"\n title={typeof children === 'string' ? children : undefined}\n >\n {children}\n </span>\n ) : (\n children\n )}\n\n {/* End icon */}\n {endIcon && (\n <span className={cn('shrink-0', iconSizeMap[resolvedSize])}>{normalizeIcon(endIcon)}</span>\n )}\n\n {/* Dismiss button */}\n {onDismiss && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n onDismiss()\n }}\n className={cn(\n 'shrink-0 rounded-pill text-current/60 hover:text-current hover:bg-current/10 transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-accent-9',\n resolvedSize === 'xs'\n ? 'p-0 -mr-0.5 min-w-ds-05 min-h-ds-05'\n : 'p-px',\n )}\n aria-label={\n `Remove ${typeof children === 'string' ? children : ''}`.trim() || 'Remove'\n }\n >\n <Icon icon={IconX} size=\"xs\" />\n </button>\n )}\n </Comp>\n </MotionPreference>\n )\n },\n)\nBadge.displayName = 'Badge'\n\nimport { BadgeGroup } from './badge-group'\nimport { BadgeIndicator } from './badge-indicator'\n\nconst BadgeCompound = Object.assign(Badge, {\n Indicator: BadgeIndicator,\n Group: BadgeGroup,\n})\n\nexport { BadgeCompound as Badge, type BadgeColor,type BadgeProps, badgeVariants }\n","'use client'\n\nimport * as React from 'react'\n\nimport { Badge, type BadgeProps } from './badge'\nimport { cn } from './lib/utils'\n\nconst GAP_CLASSES = {\n tight: 'gap-1',\n default: 'gap-1.5',\n loose: 'gap-2',\n} as const\n\nexport interface BadgeGroupProps extends React.HTMLAttributes<HTMLDivElement> {\n max?: number\n gap?: keyof typeof GAP_CLASSES\n size?: BadgeProps['size']\n onOverflowClick?: () => void\n children: React.ReactNode\n}\n\nexport const BadgeGroup = React.forwardRef<HTMLDivElement, BadgeGroupProps>(function BadgeGroup(\n { max, gap = 'default', size, onOverflowClick, className, children, ...rest },\n ref,\n) {\n const childArray = React.Children.toArray(children)\n const total = childArray.length\n const hasOverflow = max !== undefined && total > max\n const visible = hasOverflow ? childArray.slice(0, max) : childArray\n const overflowCount = hasOverflow ? total - max! : 0\n\n return (\n <div\n ref={ref}\n className={cn('flex flex-wrap items-center', GAP_CLASSES[gap], className)}\n {...rest}\n >\n {visible}\n {hasOverflow && (\n // With onClick, Badge renders a real, keyboard-reachable <button>; add a\n // label since \"+N\" alone isn't descriptive to a screen reader.\n <Badge\n variant=\"outline\"\n color=\"neutral\"\n size={size ?? 'sm'}\n onClick={onOverflowClick}\n aria-label={onOverflowClick ? `Show ${overflowCount} more` : undefined}\n >\n +{overflowCount}\n </Badge>\n )}\n </div>\n )\n})\n\nBadgeGroup.displayName = 'BadgeGroup'\n"],"mappings":"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"}