@devalok/shilp-sutra 0.57.0 → 0.59.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 (258) hide show
  1. package/MIGRATION.md +91 -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/command-palette.js +137 -137
  31. package/dist/composed/command-palette.js.map +1 -1
  32. package/dist/composed/content-card.js +1 -1
  33. package/dist/composed/content-card.js.map +1 -1
  34. package/dist/composed/date-picker/index.js +5 -5
  35. package/dist/composed/date-picker/index.js.map +1 -1
  36. package/dist/composed/empty-state.js +4 -4
  37. package/dist/composed/file-preview/image-preview.d.ts.map +1 -1
  38. package/dist/composed/file-preview/shared.d.ts.map +1 -1
  39. package/dist/composed/file-preview/video-preview.d.ts.map +1 -1
  40. package/dist/composed/file-preview.js +151 -150
  41. package/dist/composed/file-preview.js.map +1 -1
  42. package/dist/composed/filter-bar.d.ts.map +1 -1
  43. package/dist/composed/filter-bar.js +53 -52
  44. package/dist/composed/filter-bar.js.map +1 -1
  45. package/dist/composed/form-section.d.ts.map +1 -1
  46. package/dist/composed/form-section.js +38 -37
  47. package/dist/composed/form-section.js.map +1 -1
  48. package/dist/composed/master-detail.d.ts.map +1 -1
  49. package/dist/composed/master-detail.js +1 -1
  50. package/dist/composed/master-detail.js.map +1 -1
  51. package/dist/composed/multi-select-popover.d.ts.map +1 -1
  52. package/dist/composed/multi-select-popover.js +109 -108
  53. package/dist/composed/multi-select-popover.js.map +1 -1
  54. package/dist/composed/rich-chat-input/attachment-strip.d.ts.map +1 -1
  55. package/dist/composed/rich-chat-input/reply-banner.d.ts.map +1 -1
  56. package/dist/composed/rich-chat-input/schedule-send.d.ts.map +1 -1
  57. package/dist/composed/rich-chat-input.d.ts.map +1 -1
  58. package/dist/composed/rich-chat-input.js +467 -466
  59. package/dist/composed/rich-chat-input.js.map +1 -1
  60. package/dist/composed/rich-text-editor.js +3 -3
  61. package/dist/composed/rich-text-editor.js.map +1 -1
  62. package/dist/composed/schedule-view.js +2 -2
  63. package/dist/composed/schedule-view.js.map +1 -1
  64. package/dist/composed/status-badge.d.ts.map +1 -1
  65. package/dist/composed/status-badge.js +29 -28
  66. package/dist/composed/status-badge.js.map +1 -1
  67. package/dist/motion/index.d.ts +1 -0
  68. package/dist/motion/index.d.ts.map +1 -1
  69. package/dist/motion/index.js +2 -15
  70. package/dist/motion/motion-preference.d.ts +36 -0
  71. package/dist/motion/motion-preference.d.ts.map +1 -0
  72. package/dist/motion/motion-provider.d.ts +2 -1
  73. package/dist/motion/motion-provider.d.ts.map +1 -1
  74. package/dist/motion/primitives-index.js +103 -102
  75. package/dist/motion/primitives-index.js.map +1 -1
  76. package/dist/motion/primitives.d.ts.map +1 -1
  77. package/dist/shell/bottom-navbar.js +19 -19
  78. package/dist/shell/bottom-navbar.js.map +1 -1
  79. package/dist/shell/index.d.ts +1 -1
  80. package/dist/shell/index.d.ts.map +1 -1
  81. package/dist/shell/notification-center.d.ts +17 -0
  82. package/dist/shell/notification-center.d.ts.map +1 -1
  83. package/dist/shell/notification-center.js +109 -104
  84. package/dist/shell/notification-center.js.map +1 -1
  85. package/dist/shell/notification-preferences.js +1 -1
  86. package/dist/shell/notification-preferences.js.map +1 -1
  87. package/dist/shell/top-bar.js +1 -1
  88. package/dist/shell/top-bar.js.map +1 -1
  89. package/dist/tokens/index.css +1 -0
  90. package/dist/tokens/palettes.css +292 -0
  91. package/dist/tokens/semantic.css +72 -8
  92. package/dist/tokens/shilp-sutra.css +1 -0
  93. package/dist/tokens/utilities.css +10 -7
  94. package/dist/ui/accordion.d.ts.map +1 -1
  95. package/dist/ui/accordion.js +27 -26
  96. package/dist/ui/accordion.js.map +1 -1
  97. package/dist/ui/alert-dialog.d.ts.map +1 -1
  98. package/dist/ui/alert-dialog.js +60 -59
  99. package/dist/ui/alert-dialog.js.map +1 -1
  100. package/dist/ui/alert.d.ts.map +1 -1
  101. package/dist/ui/alert.js +55 -113
  102. package/dist/ui/alert.js.map +1 -1
  103. package/dist/ui/autocomplete.js +80 -80
  104. package/dist/ui/autocomplete.js.map +1 -1
  105. package/dist/ui/avatar.d.ts.map +1 -1
  106. package/dist/ui/avatar.js +69 -68
  107. package/dist/ui/avatar.js.map +1 -1
  108. package/dist/ui/badge-indicator.d.ts +4 -8
  109. package/dist/ui/badge-indicator.d.ts.map +1 -1
  110. package/dist/ui/badge-indicator.js +3 -8
  111. package/dist/ui/badge-indicator.js.map +1 -1
  112. package/dist/ui/badge.d.ts +8 -1
  113. package/dist/ui/badge.d.ts.map +1 -1
  114. package/dist/ui/banner.d.ts.map +1 -1
  115. package/dist/ui/banner.js +42 -40
  116. package/dist/ui/banner.js.map +1 -1
  117. package/dist/ui/button-group.d.ts.map +1 -1
  118. package/dist/ui/button-group.js +3 -23
  119. package/dist/ui/button-group.js.map +1 -1
  120. package/dist/ui/button.d.ts +10 -1
  121. package/dist/ui/button.d.ts.map +1 -1
  122. package/dist/ui/button.js +79 -212
  123. package/dist/ui/button.js.map +1 -1
  124. package/dist/ui/card.d.ts.map +1 -1
  125. package/dist/ui/card.js +75 -72
  126. package/dist/ui/card.js.map +1 -1
  127. package/dist/ui/charts/radar-chart.d.ts.map +1 -1
  128. package/dist/ui/charts/radar-chart.js +1 -1
  129. package/dist/ui/charts/radar-chart.js.map +1 -1
  130. package/dist/ui/chat/message-list.d.ts.map +1 -1
  131. package/dist/ui/chat/message.d.ts.map +1 -1
  132. package/dist/ui/chat/typing-indicator.d.ts.map +1 -1
  133. package/dist/ui/code.js +1 -1
  134. package/dist/ui/code.js.map +1 -1
  135. package/dist/ui/color-input.d.ts.map +1 -1
  136. package/dist/ui/color-input.js +151 -150
  137. package/dist/ui/color-input.js.map +1 -1
  138. package/dist/ui/combobox.d.ts +14 -0
  139. package/dist/ui/combobox.d.ts.map +1 -1
  140. package/dist/ui/combobox.js +152 -148
  141. package/dist/ui/combobox.js.map +1 -1
  142. package/dist/ui/context-menu.d.ts.map +1 -1
  143. package/dist/ui/context-menu.js +70 -69
  144. package/dist/ui/context-menu.js.map +1 -1
  145. package/dist/ui/data-table-body.js +2 -2
  146. package/dist/ui/data-table-body.js.map +1 -1
  147. package/dist/ui/data-table-context.js +2 -2
  148. package/dist/ui/data-table-context.js.map +1 -1
  149. package/dist/ui/data-table-header.d.ts.map +1 -1
  150. package/dist/ui/data-table-header.js +41 -40
  151. package/dist/ui/data-table-header.js.map +1 -1
  152. package/dist/ui/data-table-pagination.js +2 -2
  153. package/dist/ui/data-table-pagination.js.map +1 -1
  154. package/dist/ui/dialog.d.ts.map +1 -1
  155. package/dist/ui/dialog.js +61 -60
  156. package/dist/ui/dialog.js.map +1 -1
  157. package/dist/ui/dot.d.ts.map +1 -1
  158. package/dist/ui/dot.js +10 -41
  159. package/dist/ui/dot.js.map +1 -1
  160. package/dist/ui/dropdown-menu.d.ts.map +1 -1
  161. package/dist/ui/dropdown-menu.js +79 -78
  162. package/dist/ui/dropdown-menu.js.map +1 -1
  163. package/dist/ui/file-upload.d.ts.map +1 -1
  164. package/dist/ui/file-upload.js +116 -115
  165. package/dist/ui/file-upload.js.map +1 -1
  166. package/dist/ui/form.d.ts.map +1 -1
  167. package/dist/ui/form.js +27 -26
  168. package/dist/ui/form.js.map +1 -1
  169. package/dist/ui/hover-card.d.ts.map +1 -1
  170. package/dist/ui/hover-card.js +26 -25
  171. package/dist/ui/hover-card.js.map +1 -1
  172. package/dist/ui/icon.js +7 -7
  173. package/dist/ui/index.d.ts +2 -2
  174. package/dist/ui/index.d.ts.map +1 -1
  175. package/dist/ui/index.js +3 -3
  176. package/dist/ui/index.js.map +1 -1
  177. package/dist/ui/input.js +1 -1
  178. package/dist/ui/input.js.map +1 -1
  179. package/dist/ui/menubar.d.ts.map +1 -1
  180. package/dist/ui/menubar.js +73 -72
  181. package/dist/ui/menubar.js.map +1 -1
  182. package/dist/ui/navigation-menu.d.ts.map +1 -1
  183. package/dist/ui/navigation-menu.js +80 -79
  184. package/dist/ui/navigation-menu.js.map +1 -1
  185. package/dist/ui/popover.d.ts.map +1 -1
  186. package/dist/ui/progress.d.ts.map +1 -1
  187. package/dist/ui/progress.js +72 -68
  188. package/dist/ui/progress.js.map +1 -1
  189. package/dist/ui/radio.d.ts.map +1 -1
  190. package/dist/ui/radio.js +32 -31
  191. package/dist/ui/radio.js.map +1 -1
  192. package/dist/ui/search-input.d.ts.map +1 -1
  193. package/dist/ui/search-input.js +28 -27
  194. package/dist/ui/search-input.js.map +1 -1
  195. package/dist/ui/select.d.ts.map +1 -1
  196. package/dist/ui/select.js +64 -63
  197. package/dist/ui/select.js.map +1 -1
  198. package/dist/ui/sidebar.d.ts.map +1 -1
  199. package/dist/ui/sidebar.js +4 -4
  200. package/dist/ui/sidebar.js.map +1 -1
  201. package/dist/ui/slider.d.ts +63 -9
  202. package/dist/ui/slider.d.ts.map +1 -1
  203. package/dist/ui/slider.js +74 -32
  204. package/dist/ui/slider.js.map +1 -1
  205. package/dist/ui/split-button.d.ts +8 -2
  206. package/dist/ui/split-button.d.ts.map +1 -1
  207. package/dist/ui/split-button.js +49 -95
  208. package/dist/ui/split-button.js.map +1 -1
  209. package/dist/ui/stat-card.d.ts.map +1 -1
  210. package/dist/ui/stat-card.js +89 -88
  211. package/dist/ui/stat-card.js.map +1 -1
  212. package/dist/ui/stat-flash.js +16 -16
  213. package/dist/ui/stepper.d.ts.map +1 -1
  214. package/dist/ui/stepper.js +63 -62
  215. package/dist/ui/stepper.js.map +1 -1
  216. package/dist/ui/table.js +3 -3
  217. package/dist/ui/table.js.map +1 -1
  218. package/dist/ui/tabs.d.ts.map +1 -1
  219. package/dist/ui/tabs.js +67 -66
  220. package/dist/ui/tabs.js.map +1 -1
  221. package/dist/ui/textarea.js +1 -1
  222. package/dist/ui/textarea.js.map +1 -1
  223. package/dist/ui/toast.d.ts.map +1 -1
  224. package/dist/ui/toast.js +195 -194
  225. package/dist/ui/toast.js.map +1 -1
  226. package/dist/ui/toggle.d.ts.map +1 -1
  227. package/dist/ui/toggle.js +24 -22
  228. package/dist/ui/toggle.js.map +1 -1
  229. package/dist/ui/tooltip.d.ts.map +1 -1
  230. package/dist/ui/tooltip.js +37 -36
  231. package/dist/ui/tooltip.js.map +1 -1
  232. package/dist/ui/tree-view/tree-item.d.ts.map +1 -1
  233. package/docs/recipes/customize-brand.md +58 -0
  234. package/docs/recipes/install-astro.md +1 -1
  235. package/docs/recipes/install-next-app-router.md +1 -1
  236. package/docs/recipes/install-next-pages.md +22 -1
  237. package/docs/recipes/install-remix.md +1 -1
  238. package/docs/recipes/install-tanstack-start.md +1 -1
  239. package/docs/recipes/install-vite.md +2 -1
  240. package/docs/recipes/server-components.md +7 -4
  241. package/docs/recipes/troubleshoot.md +1 -0
  242. package/llms.txt +1 -1
  243. package/mcp-manifest.json +9 -4
  244. package/package.json +3 -2
  245. package/skill/SKILL.md +1 -1
  246. package/skill/references/components.md +1 -1
  247. package/skill/references/customize-brand.md +58 -0
  248. package/skill/references/server-components.md +7 -4
  249. package/skill/references/setup-astro.md +1 -1
  250. package/skill/references/setup-next-app-router.md +1 -1
  251. package/skill/references/setup-next-pages.md +22 -1
  252. package/skill/references/setup-remix.md +1 -1
  253. package/skill/references/setup-tanstack-start.md +1 -1
  254. package/skill/references/setup-vite.md +2 -1
  255. package/skill/references/troubleshoot.md +1 -0
  256. package/dist/motion/check-motion-provider.d.ts +0 -2
  257. package/dist/motion/check-motion-provider.d.ts.map +0 -1
  258. package/dist/motion/index.js.map +0 -1
package/MIGRATION.md CHANGED
@@ -4,6 +4,97 @@ 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.59.0 — additive (no migration required)
8
+
9
+ Nothing breaks at the TypeScript level; this release is additive.
10
+
11
+ > 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.
12
+
13
+ ## v0.58.0 — nothing errors, several things look different
14
+
15
+ No API is removed and no prop type narrows, so `tsc` and your build stay green
16
+ on upgrade day. The reason this section exists anyway is that a number of
17
+ components **change appearance**, and two of them start rendering something that
18
+ previously rendered nothing at all. If you screenshot-test, expect diffs.
19
+
20
+ ### Two features that were silently no-ops now work
21
+
22
+ Both were painted in a colour identical to their own background in light mode,
23
+ so they emitted markup and showed nothing:
24
+
25
+ | | before | after |
26
+ |---|---|---|
27
+ | `<Table striped>` | 1.00:1 — no stripes rendered at all | visible zebra |
28
+ | `<Progress>` groove | 1.00:1 — no track behind the bar | visible track |
29
+
30
+ If you worked around either of these — a local `nth-child` stripe rule, a
31
+ wrapper drawing a track behind Progress — remove the workaround, or you will now
32
+ have two.
33
+
34
+ ### Selected rows are more prominent, and correctly so
35
+
36
+ Selection fills moved from accent step 2 to step 4. In dark, steps 1–2 sit
37
+ *below* the panel in lightness while the grey hover sits above it, so a selected
38
+ row receded exactly as a hovered row advanced: the sidebar measured 1.03:1
39
+ selected against 1.30:1 hovered. The wrong row was louder, in the opposite
40
+ direction.
41
+
42
+ Affects Sidebar, MasterDetail, MultiSelectPopover, NotificationCenter, Combobox,
43
+ Autocomplete, Table, BottomNavbar, ScheduleView, TreeView and Toggle. Nothing to
44
+ do unless you override those fills yourself, in which case re-check them against
45
+ your hover state.
46
+
47
+ ### `border-card-strong` is now actually stronger than `border-card`
48
+
49
+ They resolved to the same value. Both pointed at `--color-surface-border`, so
50
+ roughly thirteen call sites asking for the heavier edge silently got the faint
51
+ one. `border-card-strong` now resolves to `--color-surface-border-strong`:
52
+
53
+ | | light | dark |
54
+ |---|---|---|
55
+ | before | 1.22:1 | 1.33:1 |
56
+ | after | 1.38:1 | 1.63:1 |
57
+
58
+ If you picked `border-card-strong` *because* it looked identical to
59
+ `border-card`, switch those to `border-card`.
60
+
61
+ ### The Sidebar's divider was a full-contrast line, and is now a hairline
62
+
63
+ `Sidebar` had a `border-r` with no colour utility. Tailwind 4 leaves an
64
+ uncoloured border at `currentColor` rather than defaulting it to grey, so it
65
+ inherited the text colour and drew at **12.69:1 in light and 15.44:1 in dark**
66
+ where `surface-border` (1.23:1 / 1.47:1) was intended.
67
+
68
+ This is the one place an upgrade might *remove* something you had come to rely
69
+ on. If you overrode the sidebar edge downstream to tame it, drop the override.
70
+
71
+ ### Raised surfaces gain an edge in dark
72
+
73
+ `--shadow-edge-ring` is deliberately swapped to a light ring under `.dark`,
74
+ because a dark ring cannot be seen on a dark ground. Only the larger shadows
75
+ consumed it, so `shadow-raised` and `shadow-raised-hover` kept a near-black ring
76
+ measuring 1.01:1 — no edge at all. `Sidebar variant="floating"`, `Card
77
+ variant="elevated"`, `Menubar` and the keyboard caps had no visible boundary in
78
+ dark. They now measure 1.21:1, still quieter than a floating overlay's 1.42:1.
79
+
80
+ ### Colour is a role now, not a variant
81
+
82
+ The palette layer landed in the same release. Twelve components stopped encoding
83
+ colour as a cross-product of variants and now resolve it through
84
+ `[data-palette]`. Existing `color="…"` values all still type-check — the union
85
+ widened rather than narrowed — and the visual result is unchanged for the six
86
+ built-in palettes. The gain is that a colour we do not ship can now be
87
+ registered in CSS.
88
+
89
+ ### Also in this release
90
+
91
+ Segmented control and Tabs: the selected pill was *darker* than the groove it
92
+ sat in, in dark (1.028:1, inverted). Read-only Input and Textarea, DataTable
93
+ pinned columns and expanded rows, code blocks in RichTextEditor, ContentCard's
94
+ default variant and AI message bubbles all stopped painting themselves in their
95
+ own background colour. Full measurements in
96
+ `docs/audits/2026-08-28-surface-model-audit.md`.
97
+
7
98
  ## v0.57.0 — one token removed, one renamed, and a colour change you will see
8
99
 
9
100
  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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"}