@nibapp/ui 0.0.0-stage → 0.1.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 (292) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/LICENSE +21 -0
  3. package/README.md +124 -2
  4. package/TRADEMARKS.md +22 -0
  5. package/dist/_bundled/money/index.d.ts +98 -0
  6. package/dist/_bundled/money/index.js +152 -0
  7. package/dist/_bundled/tokens/index.d.ts +2 -0
  8. package/dist/_bundled/tokens/index.js +2 -0
  9. package/dist/_bundled/tokens/palette.gen.d.ts +51 -0
  10. package/dist/_bundled/tokens/palette.gen.js +206 -0
  11. package/dist/_bundled/tokens/spec.d.ts +291 -0
  12. package/dist/_bundled/tokens/spec.js +221 -0
  13. package/dist/components/accordion.d.ts +27 -0
  14. package/dist/components/accordion.js +66 -0
  15. package/dist/components/alert-dialog.d.ts +59 -0
  16. package/dist/components/alert-dialog.js +82 -0
  17. package/dist/components/alert.d.ts +141 -0
  18. package/dist/components/alert.js +55 -0
  19. package/dist/components/appear.d.ts +21 -0
  20. package/dist/components/appear.js +42 -0
  21. package/dist/components/avatar.d.ts +196 -0
  22. package/dist/components/avatar.js +135 -0
  23. package/dist/components/badge.d.ts +183 -0
  24. package/dist/components/badge.js +68 -0
  25. package/dist/components/block-stack.d.ts +33 -0
  26. package/dist/components/block-stack.js +197 -0
  27. package/dist/components/button.d.ts +327 -0
  28. package/dist/components/button.js +101 -0
  29. package/dist/components/card.d.ts +131 -0
  30. package/dist/components/card.js +100 -0
  31. package/dist/components/checkbox.d.ts +34 -0
  32. package/dist/components/checkbox.js +37 -0
  33. package/dist/components/choice-row.d.ts +104 -0
  34. package/dist/components/choice-row.js +56 -0
  35. package/dist/components/clamped-text.d.ts +17 -0
  36. package/dist/components/clamped-text.js +47 -0
  37. package/dist/components/code-block.d.ts +48 -0
  38. package/dist/components/code-block.js +124 -0
  39. package/dist/components/code-input.d.ts +49 -0
  40. package/dist/components/code-input.js +59 -0
  41. package/dist/components/combobox.d.ts +194 -0
  42. package/dist/components/combobox.js +176 -0
  43. package/dist/components/command-palette.d.ts +57 -0
  44. package/dist/components/command-palette.js +83 -0
  45. package/dist/components/context-menu.d.ts +24 -0
  46. package/dist/components/context-menu.js +30 -0
  47. package/dist/components/copy-button.d.ts +9 -0
  48. package/dist/components/copy-button.js +20 -0
  49. package/dist/components/copy-command.d.ts +18 -0
  50. package/dist/components/copy-command.js +19 -0
  51. package/dist/components/data-table.d.ts +240 -0
  52. package/dist/components/data-table.js +63 -0
  53. package/dist/components/dialog.d.ts +80 -0
  54. package/dist/components/dialog.js +85 -0
  55. package/dist/components/diff-lines.d.ts +20 -0
  56. package/dist/components/diff-lines.js +10 -0
  57. package/dist/components/diff-view.d.ts +128 -0
  58. package/dist/components/diff-view.js +675 -0
  59. package/dist/components/disclosure.d.ts +33 -0
  60. package/dist/components/disclosure.js +51 -0
  61. package/dist/components/drawn-check.d.ts +8 -0
  62. package/dist/components/drawn-check.js +16 -0
  63. package/dist/components/edge-resizer.d.ts +26 -0
  64. package/dist/components/edge-resizer.js +53 -0
  65. package/dist/components/empty-state.d.ts +112 -0
  66. package/dist/components/empty-state.js +56 -0
  67. package/dist/components/expand-toggle.d.ts +19 -0
  68. package/dist/components/expand-toggle.js +10 -0
  69. package/dist/components/field.d.ts +48 -0
  70. package/dist/components/field.js +60 -0
  71. package/dist/components/filter-chip.d.ts +47 -0
  72. package/dist/components/filter-chip.js +33 -0
  73. package/dist/components/group.d.ts +10 -0
  74. package/dist/components/group.js +9 -0
  75. package/dist/components/icon-text.d.ts +11 -0
  76. package/dist/components/icon-text.js +10 -0
  77. package/dist/components/image-sources.d.ts +57 -0
  78. package/dist/components/image-sources.js +58 -0
  79. package/dist/components/image-thumbnail.d.ts +34 -0
  80. package/dist/components/image-thumbnail.js +58 -0
  81. package/dist/components/image-viewer.d.ts +60 -0
  82. package/dist/components/image-viewer.js +411 -0
  83. package/dist/components/inline-edit.d.ts +22 -0
  84. package/dist/components/inline-edit.js +51 -0
  85. package/dist/components/input.d.ts +155 -0
  86. package/dist/components/input.js +109 -0
  87. package/dist/components/item-label.d.ts +11 -0
  88. package/dist/components/item-label.js +5 -0
  89. package/dist/components/item-row.d.ts +23 -0
  90. package/dist/components/item-row.js +19 -0
  91. package/dist/components/kbd.d.ts +55 -0
  92. package/dist/components/kbd.js +86 -0
  93. package/dist/components/link-policy.d.ts +24 -0
  94. package/dist/components/link-policy.js +32 -0
  95. package/dist/components/linked-text.d.ts +15 -0
  96. package/dist/components/linked-text.js +48 -0
  97. package/dist/components/loading-region.d.ts +18 -0
  98. package/dist/components/loading-region.js +17 -0
  99. package/dist/components/markdown.d.ts +34 -0
  100. package/dist/components/markdown.js +307 -0
  101. package/dist/components/menu.d.ts +132 -0
  102. package/dist/components/menu.js +102 -0
  103. package/dist/components/message-bubble.d.ts +57 -0
  104. package/dist/components/message-bubble.js +28 -0
  105. package/dist/components/meta-line.d.ts +28 -0
  106. package/dist/components/meta-line.js +36 -0
  107. package/dist/components/money-input.d.ts +23 -0
  108. package/dist/components/money-input.js +34 -0
  109. package/dist/components/native-select.d.ts +120 -0
  110. package/dist/components/native-select.js +85 -0
  111. package/dist/components/page-trail.d.ts +30 -0
  112. package/dist/components/page-trail.js +43 -0
  113. package/dist/components/page.d.ts +80 -0
  114. package/dist/components/page.js +60 -0
  115. package/dist/components/path-text.d.ts +8 -0
  116. package/dist/components/path-text.js +11 -0
  117. package/dist/components/percent-input.d.ts +20 -0
  118. package/dist/components/percent-input.js +32 -0
  119. package/dist/components/popover.d.ts +52 -0
  120. package/dist/components/popover.js +36 -0
  121. package/dist/components/progress.d.ts +389 -0
  122. package/dist/components/progress.js +142 -0
  123. package/dist/components/radio.d.ts +65 -0
  124. package/dist/components/radio.js +54 -0
  125. package/dist/components/remote-image-gate.d.ts +34 -0
  126. package/dist/components/remote-image-gate.js +60 -0
  127. package/dist/components/resizable.d.ts +23 -0
  128. package/dist/components/resizable.js +30 -0
  129. package/dist/components/rolling-number.d.ts +13 -0
  130. package/dist/components/rolling-number.js +69 -0
  131. package/dist/components/scroll-area.d.ts +17 -0
  132. package/dist/components/scroll-area.js +25 -0
  133. package/dist/components/section-label.d.ts +10 -0
  134. package/dist/components/section-label.js +21 -0
  135. package/dist/components/section-nav.d.ts +22 -0
  136. package/dist/components/section-nav.js +80 -0
  137. package/dist/components/segmented-control.d.ts +125 -0
  138. package/dist/components/segmented-control.js +101 -0
  139. package/dist/components/select.d.ts +120 -0
  140. package/dist/components/select.js +89 -0
  141. package/dist/components/separator.d.ts +10 -0
  142. package/dist/components/separator.js +12 -0
  143. package/dist/components/sheet.d.ts +101 -0
  144. package/dist/components/sheet.js +129 -0
  145. package/dist/components/shortcut-recorder.d.ts +31 -0
  146. package/dist/components/shortcut-recorder.js +67 -0
  147. package/dist/components/skeleton.d.ts +31 -0
  148. package/dist/components/skeleton.js +33 -0
  149. package/dist/components/slider.d.ts +138 -0
  150. package/dist/components/slider.js +98 -0
  151. package/dist/components/spinner.d.ts +37 -0
  152. package/dist/components/spinner.js +16 -0
  153. package/dist/components/switch.d.ts +59 -0
  154. package/dist/components/switch.js +56 -0
  155. package/dist/components/tab-strip.d.ts +29 -0
  156. package/dist/components/tab-strip.js +42 -0
  157. package/dist/components/tabs.d.ts +13 -0
  158. package/dist/components/tabs.js +68 -0
  159. package/dist/components/text-link.d.ts +54 -0
  160. package/dist/components/text-link.js +38 -0
  161. package/dist/components/textarea.d.ts +75 -0
  162. package/dist/components/textarea.js +90 -0
  163. package/dist/components/toast.d.ts +74 -0
  164. package/dist/components/toast.js +128 -0
  165. package/dist/components/tooltip.d.ts +50 -0
  166. package/dist/components/tooltip.js +46 -0
  167. package/dist/components/truncated-text.d.ts +15 -0
  168. package/dist/components/truncated-text.js +19 -0
  169. package/dist/i18n/defaults.d.ts +7 -0
  170. package/dist/i18n/defaults.js +7 -0
  171. package/dist/i18n/format.d.ts +84 -0
  172. package/dist/i18n/format.js +169 -0
  173. package/dist/i18n/index.d.ts +6 -0
  174. package/dist/i18n/index.js +8 -0
  175. package/dist/i18n/locales/ru-formatters.d.ts +3 -0
  176. package/dist/i18n/locales/ru-formatters.js +22 -0
  177. package/dist/i18n/locales/ru.d.ts +5 -0
  178. package/dist/i18n/locales/ru.js +185 -0
  179. package/dist/i18n/plural.d.ts +20 -0
  180. package/dist/i18n/plural.js +26 -0
  181. package/dist/i18n/provider.d.ts +36 -0
  182. package/dist/i18n/provider.js +39 -0
  183. package/dist/i18n/rich.d.ts +14 -0
  184. package/dist/i18n/rich.js +44 -0
  185. package/dist/i18n/ru-plural.d.ts +3 -0
  186. package/dist/i18n/ru-plural.js +4 -0
  187. package/dist/i18n/strings/alert-dialog.d.ts +5 -0
  188. package/dist/i18n/strings/alert-dialog.js +5 -0
  189. package/dist/i18n/strings/block-stack.d.ts +17 -0
  190. package/dist/i18n/strings/block-stack.js +11 -0
  191. package/dist/i18n/strings/code-block.d.ts +18 -0
  192. package/dist/i18n/strings/code-block.js +13 -0
  193. package/dist/i18n/strings/combobox.d.ts +12 -0
  194. package/dist/i18n/strings/combobox.js +10 -0
  195. package/dist/i18n/strings/command-palette.d.ts +17 -0
  196. package/dist/i18n/strings/command-palette.js +11 -0
  197. package/dist/i18n/strings/diff-view.d.ts +92 -0
  198. package/dist/i18n/strings/diff-view.js +68 -0
  199. package/dist/i18n/strings/image-viewer.d.ts +43 -0
  200. package/dist/i18n/strings/image-viewer.js +32 -0
  201. package/dist/i18n/strings/index.d.ts +314 -0
  202. package/dist/i18n/strings/index.js +43 -0
  203. package/dist/i18n/strings/kbd.d.ts +42 -0
  204. package/dist/i18n/strings/kbd.js +39 -0
  205. package/dist/i18n/strings/markdown.d.ts +23 -0
  206. package/dist/i18n/strings/markdown.js +18 -0
  207. package/dist/i18n/strings/money.d.ts +7 -0
  208. package/dist/i18n/strings/money.js +5 -0
  209. package/dist/i18n/strings/primitives.d.ts +25 -0
  210. package/dist/i18n/strings/primitives.js +18 -0
  211. package/dist/i18n/strings/progress.d.ts +4 -0
  212. package/dist/i18n/strings/progress.js +4 -0
  213. package/dist/i18n/strings/shortcut-recorder.d.ts +15 -0
  214. package/dist/i18n/strings/shortcut-recorder.js +11 -0
  215. package/dist/i18n/strings/time.d.ts +41 -0
  216. package/dist/i18n/strings/time.js +20 -0
  217. package/dist/i18n/strings/toast.d.ts +6 -0
  218. package/dist/i18n/strings/toast.js +6 -0
  219. package/dist/i18n/time-zones.d.ts +20 -0
  220. package/dist/i18n/time-zones.js +64 -0
  221. package/dist/index.d.ts +84 -0
  222. package/dist/index.js +88 -0
  223. package/dist/lib/ansi.d.ts +55 -0
  224. package/dist/lib/ansi.js +438 -0
  225. package/dist/lib/clock.d.ts +32 -0
  226. package/dist/lib/clock.js +106 -0
  227. package/dist/lib/cn.d.ts +12 -0
  228. package/dist/lib/cn.js +28 -0
  229. package/dist/lib/control-props.d.ts +21 -0
  230. package/dist/lib/control-props.js +14 -0
  231. package/dist/lib/direction.d.ts +13 -0
  232. package/dist/lib/direction.js +25 -0
  233. package/dist/lib/image-size.d.ts +12 -0
  234. package/dist/lib/image-size.js +66 -0
  235. package/dist/lib/keycaps.d.ts +21 -0
  236. package/dist/lib/keycaps.js +62 -0
  237. package/dist/lib/keyed.d.ts +9 -0
  238. package/dist/lib/keyed.js +14 -0
  239. package/dist/lib/keys.d.ts +49 -0
  240. package/dist/lib/keys.js +159 -0
  241. package/dist/lib/kit-motion.d.ts +9 -0
  242. package/dist/lib/kit-motion.js +12 -0
  243. package/dist/lib/link-check-dialog.d.ts +11 -0
  244. package/dist/lib/link-check-dialog.js +18 -0
  245. package/dist/lib/motion-features.d.ts +1 -0
  246. package/dist/lib/motion-features.js +4 -0
  247. package/dist/lib/motion-provider.d.ts +5 -0
  248. package/dist/lib/motion-provider.js +8 -0
  249. package/dist/lib/motion.d.ts +39 -0
  250. package/dist/lib/motion.js +49 -0
  251. package/dist/lib/number-format.d.ts +114 -0
  252. package/dist/lib/number-format.js +313 -0
  253. package/dist/lib/patch.d.ts +72 -0
  254. package/dist/lib/patch.js +344 -0
  255. package/dist/lib/path.d.ts +34 -0
  256. package/dist/lib/path.js +85 -0
  257. package/dist/lib/search.d.ts +10 -0
  258. package/dist/lib/search.js +20 -0
  259. package/dist/lib/shiki-themes.d.ts +7 -0
  260. package/dist/lib/shiki-themes.js +115 -0
  261. package/dist/lib/shiki.d.ts +101 -0
  262. package/dist/lib/shiki.js +353 -0
  263. package/dist/lib/shortcut-context.d.ts +23 -0
  264. package/dist/lib/shortcut-context.js +29 -0
  265. package/dist/lib/text.d.ts +56 -0
  266. package/dist/lib/text.js +149 -0
  267. package/dist/lib/theme-scope.d.ts +18 -0
  268. package/dist/lib/theme-scope.js +24 -0
  269. package/dist/lib/theme.d.ts +40 -0
  270. package/dist/lib/theme.js +96 -0
  271. package/dist/lib/tree-keys.d.ts +72 -0
  272. package/dist/lib/tree-keys.js +126 -0
  273. package/dist/lib/url.d.ts +17 -0
  274. package/dist/lib/url.js +38 -0
  275. package/dist/lib/use-copy-to-clipboard.d.ts +13 -0
  276. package/dist/lib/use-copy-to-clipboard.js +40 -0
  277. package/dist/lib/use-delayed-loading.d.ts +11 -0
  278. package/dist/lib/use-delayed-loading.js +31 -0
  279. package/dist/lib/use-file-picker.d.ts +26 -0
  280. package/dist/lib/use-file-picker.js +43 -0
  281. package/dist/lib/use-hydrated.d.ts +6 -0
  282. package/dist/lib/use-hydrated.js +11 -0
  283. package/dist/lib/use-latest.d.ts +12 -0
  284. package/dist/lib/use-latest.js +22 -0
  285. package/dist/lib/use-resolved-theme.d.ts +12 -0
  286. package/dist/lib/use-resolved-theme.js +25 -0
  287. package/dist/styles/base.css +393 -0
  288. package/dist/styles/code.css +35 -0
  289. package/dist/styles/fonts.css +12 -0
  290. package/dist/styles/index.css +13 -0
  291. package/dist/styles/tokens.gen.css +494 -0
  292. package/package.json +208 -4
@@ -0,0 +1,33 @@
1
+ import { Collapsible } from "@base-ui/react/collapsible";
2
+ import { ChevronRight } from "lucide-react";
3
+ import type { ComponentProps, ReactNode } from "react";
4
+ export interface DisclosureChevronProps extends Omit<ComponentProps<typeof ChevronRight>, "children"> {
5
+ /**
6
+ * Whether what it heads is open. Leave it out inside a Base UI Collapsible or Accordion trigger: the
7
+ * chevron follows that trigger's state.
8
+ */
9
+ readonly open?: boolean | undefined;
10
+ }
11
+ /**
12
+ * The chevron of anything that opens in place (a tool call, a file's diff, a tree node, a quiet
13
+ * fold): forward while closed, turned down while open. It turns by transform on the snappy spring,
14
+ * at once under reduced motion; 14 px unless the caller sizes it, in the colour of the text around it.
15
+ */
16
+ export declare function DisclosureChevron({ open, className, ...props }: DisclosureChevronProps): import("react").JSX.Element;
17
+ /**
18
+ * Progressive disclosure: a quiet text button with a chevron that hides
19
+ * the less common options ("Advanced", "More options"). One level only — an advanced area that needs
20
+ * its own sections is a page. A list of things that each open (files, messages) is an `Accordion`.
21
+ */
22
+ export interface DisclosureProps extends Omit<ComponentProps<typeof Collapsible.Root>, "children" | "render"> {
23
+ /** What's inside, in plain words ("Advanced", "Network and proxy"). */
24
+ readonly label: ReactNode;
25
+ /** Optional one-line hint after the label, e.g. how many options are inside. */
26
+ readonly hint?: ReactNode;
27
+ readonly children: ReactNode;
28
+ /** Classes for the panel's inner column. */
29
+ readonly panelClassName?: string;
30
+ /** `md` (default) heads a group of options; `sm` sits inline under a sentence. */
31
+ readonly size?: "sm" | "md";
32
+ }
33
+ export declare function Disclosure({ label, hint, children, size, className, panelClassName, ...props }: DisclosureProps): import("react").JSX.Element;
@@ -0,0 +1,51 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Collapsible } from "@base-ui/react/collapsible";
4
+ import { ChevronRight } from "lucide-react";
5
+ import { cn, resolveClassName, tv } from "../lib/cn.js";
6
+ const disclosureStyles = tv({
7
+ slots: {
8
+ trigger: [
9
+ // The padding gives the focus ring room and the margin keeps the label in line; the cap counts
10
+ // both, or a label in a column only as wide as itself (an empty state's Details) is cut.
11
+ // A small text control: under a coarse pointer, `touch-target` grows its hit area to 44 px.
12
+ "relative touch-target -mx-1 flex w-fit max-w-[calc(100%+0.5rem)] items-center px-1",
13
+ "text-fg-muted outline-none control-motion hover:text-fg focus-visible:ring-focus",
14
+ ],
15
+ chevron: "",
16
+ panel: "flex flex-col",
17
+ },
18
+ variants: {
19
+ size: {
20
+ /**
21
+ * Inline under a sentence in a toast, banner or row (a failure's Details): caption text, and the
22
+ * chevron in the text's colour, which holds on danger and warning tints too.
23
+ */
24
+ sm: { trigger: "gap-1 rounded-sm text-xs", chevron: "size-3", panel: "gap-1 pt-1" },
25
+ md: {
26
+ trigger: "gap-1.5 rounded-md py-0.5 text-sm font-medium",
27
+ chevron: "size-3.5 text-fg-subtle",
28
+ panel: "gap-4 pt-3",
29
+ },
30
+ },
31
+ },
32
+ defaultVariants: { size: "md" },
33
+ });
34
+ /**
35
+ * The chevron of anything that opens in place (a tool call, a file's diff, a tree node, a quiet
36
+ * fold): forward while closed, turned down while open. It turns by transform on the snappy spring,
37
+ * at once under reduced motion; 14 px unless the caller sizes it, in the colour of the text around it.
38
+ */
39
+ export function DisclosureChevron({ open, className, ...props }) {
40
+ return (_jsx(ChevronRight, { "aria-hidden": true, "data-slot": "disclosure-chevron", className: cn("size-3.5 shrink-0 transition-transform duration-(--duration-spring-snappy) ease-(--ease-spring-snappy)", "motion-reduce:transition-none",
41
+ // Mirrored right to left (base.css), so it turns the other way to point down.
42
+ open === undefined
43
+ ? "in-data-panel-open:rotate-90 rtl:in-data-panel-open:-rotate-90"
44
+ : open && "rotate-90 rtl:-rotate-90", className), ...props }));
45
+ }
46
+ export function Disclosure({ label, hint, children, size, className, panelClassName, ...props }) {
47
+ const styles = disclosureStyles({ size });
48
+ return (_jsxs(Collapsible.Root, { "data-slot": "disclosure", className: (state) => cn("flex flex-col", resolveClassName(className, state)), ...props, children: [_jsxs(Collapsible.Trigger, { "data-slot": "disclosure-trigger", className: styles.trigger(), children: [_jsx(DisclosureChevron, { className: styles.chevron() }), _jsx("span", { className: "min-w-0 truncate", children: label }), hint !== undefined && _jsx("span", { className: "shrink-0 font-normal text-fg-subtle", children: hint })] }), _jsx(Collapsible.Panel, { "data-slot": "disclosure-panel", className: cn(
49
+ // Opens at its full height at once (no layout motion); only the content fades in.
50
+ "transition-opacity duration-(--duration-fast) ease-standard", "data-starting-style:opacity-0 data-ending-style:opacity-0 motion-reduce:transition-none"), children: _jsx("div", { className: styles.panel({ className: panelClassName }), children: children }) })] }));
51
+ }
@@ -0,0 +1,8 @@
1
+ import type { ComponentProps } from "react";
2
+ export type DrawnCheckProps = Omit<ComponentProps<"svg">, "children">;
3
+ /**
4
+ * The shared "done" glyph: a check whose stroke draws itself on the smooth spring when something
5
+ * just finished (a tool, a copy, a retry, an approval). Under Reduce Motion it's simply there.
6
+ * Sized like a 16 px icon; colour follows `currentColor`.
7
+ */
8
+ export declare function DrawnCheck({ className, ...props }: DrawnCheckProps): import("react").JSX.Element;
@@ -0,0 +1,16 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useReducedMotion } from "motion/react";
4
+ import * as m from "motion/react-m";
5
+ import { cn } from "../lib/cn.js";
6
+ import { KitMotion } from "../lib/kit-motion.js";
7
+ import { spring } from "../lib/motion.js";
8
+ /**
9
+ * The shared "done" glyph: a check whose stroke draws itself on the smooth spring when something
10
+ * just finished (a tool, a copy, a retry, an approval). Under Reduce Motion it's simply there.
11
+ * Sized like a 16 px icon; colour follows `currentColor`.
12
+ */
13
+ export function DrawnCheck({ className, ...props }) {
14
+ const animate = useReducedMotion() !== true;
15
+ return (_jsx("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, "data-slot": "drawn-check", className: cn("size-4 shrink-0", className), ...props, children: _jsx(KitMotion, { children: _jsx(m.path, { d: "M3.5 8.5l3 3 6-7", stroke: "currentColor", strokeWidth: 1.75, strokeLinecap: "round", strokeLinejoin: "round", initial: { pathLength: animate ? 0 : 1 }, animate: { pathLength: 1 }, transition: animate ? { ...spring.smooth, delay: 0.04 } : { duration: 0 } }) }) }));
16
+ }
@@ -0,0 +1,26 @@
1
+ export interface EdgeResizerProps {
2
+ /**
3
+ * The box's edge the handle sits on, in the writing direction (`end` is the right edge left to
4
+ * right, the left edge right to left): dragging away from the box makes it wider.
5
+ */
6
+ readonly edge: "start" | "end";
7
+ /** Accessible name, e.g. "Resize sidebar". */
8
+ readonly label: string;
9
+ /** Current width, px. */
10
+ readonly value: number;
11
+ readonly min: number;
12
+ readonly max: number;
13
+ /** Called with the wanted width; the owner clamps and stores it. */
14
+ readonly onResize: (width: number) => void;
15
+ /** Double-click: back to the default width. */
16
+ readonly onReset: () => void;
17
+ readonly className?: string;
18
+ }
19
+ /**
20
+ * A vertical splitter on the edge of a fixed-width box (a sidebar, a floating panel), for
21
+ * boxes that aren't panels of a ResizablePanelGroup. A focusable `separator` with its value, so it
22
+ * works from the keyboard (←/→, Home/End) and reads as a splitter, like ResizableHandle. Pointer and
23
+ * arrow keys move the handle on screen, so in a right-to-left layout the same drag widens the box
24
+ * from its other side.
25
+ */
26
+ export declare function EdgeResizer({ edge, label, value, min, max, onResize, onReset, className, }: EdgeResizerProps): import("react").JSX.Element;
@@ -0,0 +1,53 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useRef } from "react";
4
+ import { cn } from "../lib/cn.js";
5
+ import { useInlineSign } from "../lib/direction.js";
6
+ const KEY_STEP = 16;
7
+ /**
8
+ * A vertical splitter on the edge of a fixed-width box (a sidebar, a floating panel), for
9
+ * boxes that aren't panels of a ResizablePanelGroup. A focusable `separator` with its value, so it
10
+ * works from the keyboard (←/→, Home/End) and reads as a splitter, like ResizableHandle. Pointer and
11
+ * arrow keys move the handle on screen, so in a right-to-left layout the same drag widens the box
12
+ * from its other side.
13
+ */
14
+ export function EdgeResizer({ edge, label, value, min, max, onResize, onReset, className, }) {
15
+ const start = useRef(null);
16
+ const inline = useInlineSign();
17
+ // +1 when the handle is on the box's right edge on screen: moving it right widens the box. The
18
+ // end edge is the right one left to right and the left one right to left.
19
+ const sign = (edge === "end" ? 1 : -1) * inline;
20
+ const onPointerDown = (event) => {
21
+ if (event.button !== 0) {
22
+ return;
23
+ }
24
+ event.preventDefault();
25
+ event.currentTarget.setPointerCapture(event.pointerId);
26
+ start.current = { x: event.clientX, width: value };
27
+ };
28
+ const onPointerMove = (event) => {
29
+ if (start.current !== null) {
30
+ onResize(start.current.width + (event.clientX - start.current.x) * sign);
31
+ }
32
+ };
33
+ const onPointerUp = () => {
34
+ start.current = null;
35
+ };
36
+ const onKeyDown = (event) => {
37
+ const step = (event.shiftKey ? KEY_STEP * 4 : KEY_STEP) * sign;
38
+ const next = event.key === "ArrowRight"
39
+ ? value + step
40
+ : event.key === "ArrowLeft"
41
+ ? value - step
42
+ : event.key === "Home"
43
+ ? min
44
+ : event.key === "End"
45
+ ? max
46
+ : undefined;
47
+ if (next !== undefined) {
48
+ event.preventDefault();
49
+ onResize(next);
50
+ }
51
+ };
52
+ return (_jsx("div", { role: "separator", "aria-orientation": "vertical", "aria-label": label, "aria-valuenow": Math.round(value), "aria-valuemin": min, "aria-valuemax": max, tabIndex: 0, "data-slot": "edge-resizer", className: cn("group/resizer absolute inset-y-0 z-20 flex w-2 cursor-col-resize touch-none justify-center outline-none", edge === "end" ? "-end-1" : "-start-1", className), onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: onPointerUp, onPointerCancel: onPointerUp, onKeyDown: onKeyDown, onDoubleClick: onReset, children: _jsx("span", { "aria-hidden": true, className: cn("h-full w-[3px] scale-x-[0.34] rounded-full bg-transparent", "transition-[background-color,scale] duration-(--duration-spring-snappy) ease-(--ease-spring-snappy) motion-reduce:transition-none", "group-hover/resizer:scale-x-100 group-hover/resizer:bg-border-strong", "group-active/resizer:scale-x-100 group-active/resizer:bg-fg-subtle", "group-focus-visible/resizer:scale-x-100 group-focus-visible/resizer:bg-focus") }) }));
53
+ }
@@ -0,0 +1,112 @@
1
+ import { type ComponentProps, type ReactNode } from "react";
2
+ import { type VariantProps } from "../lib/cn.js";
3
+ declare const emptyState: import("tailwind-variants").TVReturnType<{
4
+ size: {
5
+ compact: {
6
+ root: string;
7
+ icon: string;
8
+ title: string;
9
+ description: string;
10
+ };
11
+ spacious: {
12
+ root: string;
13
+ icon: string;
14
+ title: string;
15
+ description: string;
16
+ };
17
+ };
18
+ }, {
19
+ root: string;
20
+ icon: string[];
21
+ illustration: string;
22
+ title: string;
23
+ description: string;
24
+ actions: string;
25
+ }, undefined, {
26
+ size: {
27
+ compact: {
28
+ root: string;
29
+ icon: string;
30
+ title: string;
31
+ description: string;
32
+ };
33
+ spacious: {
34
+ root: string;
35
+ icon: string;
36
+ title: string;
37
+ description: string;
38
+ };
39
+ };
40
+ }, {
41
+ root: string;
42
+ icon: string[];
43
+ illustration: string;
44
+ title: string;
45
+ description: string;
46
+ actions: string;
47
+ }, import("tailwind-variants").TVReturnTypeLike<{
48
+ size: {
49
+ compact: {
50
+ root: string;
51
+ icon: string;
52
+ title: string;
53
+ description: string;
54
+ };
55
+ spacious: {
56
+ root: string;
57
+ icon: string;
58
+ title: string;
59
+ description: string;
60
+ };
61
+ };
62
+ }, {
63
+ root: string;
64
+ icon: string[];
65
+ illustration: string;
66
+ title: string;
67
+ description: string;
68
+ actions: string;
69
+ }>>;
70
+ /** What an empty place is about, which picks its default scene: nothing yet, a failure, no connection, done. */
71
+ export type EmptyStateIntent = "empty" | "error" | "offline" | "success";
72
+ /** The size an empty state draws at, which a scene is drawn for. */
73
+ export type EmptyStateSize = NonNullable<VariantProps<typeof emptyState>["size"]>;
74
+ /** A host's art per intent, drawn at the empty state's size. */
75
+ export type EmptyStateScenes = Partial<Record<EmptyStateIntent, (size: EmptyStateSize) => ReactNode>>;
76
+ /**
77
+ * Gives every `EmptyState` below a default scene per intent: the art it shows when it's given neither
78
+ * an `illustration` nor an `icon`. The kit has no art of its own, so without a provider those show none.
79
+ */
80
+ export declare function EmptyStateScenesProvider({ scenes, children, }: {
81
+ readonly scenes: EmptyStateScenes;
82
+ readonly children: ReactNode;
83
+ }): import("react").JSX.Element;
84
+ export interface EmptyStateProps extends Omit<ComponentProps<"div">, "title">, VariantProps<typeof emptyState> {
85
+ /** Small glyph shown in a raised tile. */
86
+ icon?: ReactNode;
87
+ /** Larger artwork (e.g. the mascot); replaces the icon tile. */
88
+ illustration?: ReactNode;
89
+ /**
90
+ * What this place is about (default `empty`): with neither `illustration` nor `icon`, it shows the
91
+ * scene `EmptyStateScenesProvider` gives for it.
92
+ */
93
+ intent?: EmptyStateIntent;
94
+ /**
95
+ * Set in the serif. Put one word in `<em>` for its italic accent; a translated string marks it with
96
+ * `richText` and an `em` element, so each language picks its own word.
97
+ */
98
+ title: ReactNode;
99
+ description?: ReactNode;
100
+ /** Buttons — usually one primary and one secondary at most. */
101
+ actions?: ReactNode;
102
+ /**
103
+ * The title's heading level: 3 inside a page or panel that has its own heading, 1 when the empty
104
+ * state is the whole page (a not-found or crash screen).
105
+ */
106
+ level?: 1 | 2 | 3;
107
+ /** Play the staggered entrance. Disable for states that flicker in and out. */
108
+ animated?: boolean;
109
+ }
110
+ /** Calm placeholder for empty lists, blank sessions and zero-result searches. */
111
+ export declare function EmptyState({ icon, illustration, intent, title, description, actions, level, size, animated, className, ...props }: EmptyStateProps): import("react").JSX.Element;
112
+ export {};
@@ -0,0 +1,56 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as m from "motion/react-m";
4
+ import { createContext, use } from "react";
5
+ import { tv } from "../lib/cn.js";
6
+ import { KitMotion } from "../lib/kit-motion.js";
7
+ import { rise, stagger } from "../lib/motion.js";
8
+ const emptyState = tv({
9
+ slots: {
10
+ root: "flex w-full flex-col items-center justify-center text-center",
11
+ icon: [
12
+ "mb-1 grid shrink-0 place-items-center border border-border bg-surface text-fg-muted",
13
+ "[box-shadow:var(--shadow-xs),var(--shadow-inset-edge)]",
14
+ ],
15
+ illustration: "mb-1 flex justify-center text-fg-subtle",
16
+ // The editorial serif (BRAND §5), at its regular weight; one word in <em> is the italic accent.
17
+ title: "font-serif font-normal tracking-tight text-fg text-balance [&_em]:italic",
18
+ description: "max-w-sm text-fg-muted text-pretty",
19
+ actions: "mt-2 flex flex-wrap items-center justify-center gap-2",
20
+ },
21
+ variants: {
22
+ size: {
23
+ compact: {
24
+ root: "gap-1.5 px-4 py-8",
25
+ icon: "size-9 rounded-lg [&_svg]:size-4",
26
+ title: "text-lg",
27
+ description: "text-sm",
28
+ },
29
+ spacious: {
30
+ root: "gap-2 px-6 py-16",
31
+ icon: "size-12 rounded-xl [&_svg]:size-5",
32
+ title: "text-2xl",
33
+ description: "text-base",
34
+ },
35
+ },
36
+ },
37
+ defaultVariants: { size: "spacious" },
38
+ });
39
+ const container = stagger(0.05, 0.04);
40
+ const HEADINGS = { 1: m.h1, 2: m.h2, 3: m.h3 };
41
+ const ScenesContext = createContext({});
42
+ /**
43
+ * Gives every `EmptyState` below a default scene per intent: the art it shows when it's given neither
44
+ * an `illustration` nor an `icon`. The kit has no art of its own, so without a provider those show none.
45
+ */
46
+ export function EmptyStateScenesProvider({ scenes, children, }) {
47
+ return _jsx(ScenesContext, { value: scenes, children: children });
48
+ }
49
+ /** Calm placeholder for empty lists, blank sessions and zero-result searches. */
50
+ export function EmptyState({ icon, illustration, intent = "empty", title, description, actions, level = 3, size, animated = true, className, ...props }) {
51
+ const styles = emptyState({ size });
52
+ const scenes = use(ScenesContext);
53
+ const art = illustration ?? (icon === undefined ? scenes[intent]?.(size ?? "spacious") : undefined);
54
+ const Heading = HEADINGS[level];
55
+ return (_jsx("div", { "data-slot": "empty-state", "data-intent": intent, className: styles.root({ className }), ...props, children: _jsx(KitMotion, { children: _jsxs(m.div, { className: "contents", variants: container, initial: animated ? "initial" : false, animate: "animate", children: [art ? (_jsx(m.div, { variants: rise, className: styles.illustration(), children: art })) : (icon && (_jsx(m.div, { variants: rise, "aria-hidden": true, className: styles.icon(), children: icon }))), _jsx(Heading, { variants: rise, className: styles.title(), children: title }), description && (_jsx(m.div, { variants: rise, className: styles.description(), children: description })), actions && (_jsx(m.div, { variants: rise, className: styles.actions(), children: actions }))] }) }) }));
56
+ }
@@ -0,0 +1,19 @@
1
+ import type { ReactNode } from "react";
2
+ export interface ExpandToggleProps {
3
+ readonly expanded: boolean;
4
+ readonly onToggle: () => void;
5
+ /** The words for the state it switches to ("Show more", "Show all", "Show less"). */
6
+ readonly children: ReactNode;
7
+ /** What expanding reveals ("248 lines"), shown after the words while collapsed. */
8
+ readonly detail?: string | undefined;
9
+ /** The id of the box it opens and closes. */
10
+ readonly controls?: string | undefined;
11
+ /** Opening is under way (a transition): the button says so. */
12
+ readonly pending?: boolean | undefined;
13
+ readonly className?: string | undefined;
14
+ }
15
+ /**
16
+ * The quiet "Show more / Show less" under clamped or capped content: one button, a chevron that
17
+ * turns, `aria-expanded` (and `aria-controls` when it names the box).
18
+ */
19
+ export declare function ExpandToggle({ expanded, onToggle, children, detail, controls, pending, className, }: ExpandToggleProps): import("react").JSX.Element;
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ChevronDown } from "lucide-react";
3
+ import { cn } from "../lib/cn.js";
4
+ /**
5
+ * The quiet "Show more / Show less" under clamped or capped content: one button, a chevron that
6
+ * turns, `aria-expanded` (and `aria-controls` when it names the box).
7
+ */
8
+ export function ExpandToggle({ expanded, onToggle, children, detail, controls, pending, className, }) {
9
+ return (_jsxs("button", { type: "button", "data-slot": "expand-toggle", "aria-expanded": expanded, "aria-controls": controls, "aria-busy": pending === true || undefined, onClick: onToggle, className: cn("inline-flex h-6 shrink-0 items-center gap-1 rounded-sm font-sans text-xs text-fg-subtle outline-none", "transition-colors duration-(--duration-fast) hover:text-fg focus-visible:ring-focus", className), children: [children, !expanded && detail !== undefined && _jsxs("span", { className: "text-fg-subtle tabular", children: ["\u00B7 ", detail] }), _jsx(ChevronDown, { "aria-hidden": true, className: cn("size-3 transition-transform duration-(--duration-fast) motion-reduce:transition-none", expanded && "rotate-180") })] }));
10
+ }
@@ -0,0 +1,48 @@
1
+ import { Field as BaseField } from "@base-ui/react/field";
2
+ import { Fieldset as BaseFieldset } from "@base-ui/react/fieldset";
3
+ import type { ComponentProps, ReactNode } from "react";
4
+ /** Groups a control with its label, description and error. Every input in the kit works inside it. */
5
+ export declare function FieldRoot({ className, ...props }: ComponentProps<typeof BaseField.Root>): import("react").JSX.Element;
6
+ export declare function FieldLabel({ className, ...props }: ComponentProps<typeof BaseField.Label>): import("react").JSX.Element;
7
+ export declare function FieldDescription({ className, ...props }: ComponentProps<typeof BaseField.Description>): import("react").JSX.Element;
8
+ /**
9
+ * Validation message. Without `match` it renders the browser's message whenever the field is
10
+ * invalid; pass `match={true}` to drive visibility from your own state.
11
+ */
12
+ export declare function FieldError({ className, children, ...props }: ComponentProps<typeof BaseField.Error>): import("react").JSX.Element;
13
+ /** One option inside a checkbox or radio group that sits within a Field. */
14
+ export declare function FieldItem({ className, ...props }: ComponentProps<typeof BaseField.Item>): import("react").JSX.Element;
15
+ /** A titled group of fields (or of checkboxes/radios via `render={<CheckboxGroup />}`). */
16
+ export declare function Fieldset({ className, ...props }: ComponentProps<typeof BaseFieldset.Root>): import("react").JSX.Element;
17
+ export declare function FieldsetLegend({ className, ...props }: ComponentProps<typeof BaseFieldset.Legend>): import("react").JSX.Element;
18
+ /**
19
+ * Under a form's fields, the controls that complete it (a role, Send): on one line while the width
20
+ * holds them, wrapping onto the next when it doesn't, and lined up on their controls. A field in the
21
+ * row keeps its label and no description, so a hint can't push the others out of line: the hint
22
+ * belongs to the full-width field above.
23
+ */
24
+ export declare function FieldActions({ className, ...props }: ComponentProps<"div">): import("react").JSX.Element;
25
+ export interface FieldProps extends Omit<ComponentProps<typeof BaseField.Root>, "children"> {
26
+ label?: ReactNode;
27
+ /** Muted trailing text in the label row — "Optional", a character count… */
28
+ hint?: ReactNode;
29
+ description?: ReactNode;
30
+ /**
31
+ * Forces the field invalid and shows this message. Omit to show the browser's validation
32
+ * message after Base UI validates the control.
33
+ */
34
+ error?: ReactNode;
35
+ /**
36
+ * `vertical` stacks label → control → description. `horizontal` puts the copy on the left and
37
+ * the control on the right — the settings-row layout for switches and compact selects.
38
+ */
39
+ orientation?: "vertical" | "horizontal";
40
+ /**
41
+ * Set to `false` for button controls (Select, Combobox trigger) so clicking the label focuses
42
+ * rather than presses the trigger.
43
+ */
44
+ nativeLabel?: boolean;
45
+ children: ReactNode;
46
+ }
47
+ /** Label + control + description + error in one. */
48
+ export declare function Field({ label, hint, description, error, orientation, nativeLabel, invalid, className, children, ...props }: FieldProps): import("react").JSX.Element;
@@ -0,0 +1,60 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Field as BaseField } from "@base-ui/react/field";
4
+ import { Fieldset as BaseFieldset } from "@base-ui/react/fieldset";
5
+ import { CircleAlert } from "lucide-react";
6
+ import { cn, resolveClassName, withBase } from "../lib/cn.js";
7
+ /* ------------------------------------------------------------------------------------------ */
8
+ /* Parts */
9
+ /* ------------------------------------------------------------------------------------------ */
10
+ /** Groups a control with its label, description and error. Every input in the kit works inside it. */
11
+ export function FieldRoot({ className, ...props }) {
12
+ return (_jsx(BaseField.Root, { "data-slot": "field", className: withBase("flex min-w-0 flex-col gap-1.5", className), ...props }));
13
+ }
14
+ export function FieldLabel({ className, ...props }) {
15
+ return (_jsx(BaseField.Label, { "data-slot": "field-label", className: withBase("inline-flex w-fit select-none items-center gap-1.5 text-sm font-medium text-fg data-disabled:opacity-45", className), ...props }));
16
+ }
17
+ export function FieldDescription({ className, ...props }) {
18
+ return (_jsx(BaseField.Description, { "data-slot": "field-description",
19
+ // Stays readable when the field is disabled: it is often what says why.
20
+ className: withBase("text-sm text-fg-subtle", className), ...props }));
21
+ }
22
+ /**
23
+ * Validation message. Without `match` it renders the browser's message whenever the field is
24
+ * invalid; pass `match={true}` to drive visibility from your own state.
25
+ */
26
+ export function FieldError({ className, children, ...props }) {
27
+ return (_jsxs(BaseField.Error, { "data-slot": "field-error", className: withBase(cn("flex items-start gap-1.5 text-sm text-danger-fg", "transition-[opacity,translate] duration-(--duration-spring-snappy) ease-(--ease-spring-snappy)", "data-starting-style:-translate-y-0.5 data-starting-style:opacity-0", "data-ending-style:-translate-y-0.5 data-ending-style:opacity-0 motion-reduce:transition-none"), className), ...props, children: [_jsx(CircleAlert, { "aria-hidden": true, className: "mt-[3px] size-3.5 shrink-0" }), _jsx("span", { className: "min-w-0", children: children ?? _jsx(BaseField.Validity, { children: (validity) => validity.error }) })] }));
28
+ }
29
+ /** One option inside a checkbox or radio group that sits within a Field. */
30
+ export function FieldItem({ className, ...props }) {
31
+ return (_jsx(BaseField.Item, { "data-slot": "field-item", className: withBase("flex min-w-0 flex-col", className), ...props }));
32
+ }
33
+ /** A titled group of fields (or of checkboxes/radios via `render={<CheckboxGroup />}`). */
34
+ export function Fieldset({ className, ...props }) {
35
+ return (_jsx(BaseFieldset.Root, { "data-slot": "fieldset", className: withBase("m-0 flex min-w-0 flex-col gap-3 border-0 p-0", className), ...props }));
36
+ }
37
+ export function FieldsetLegend({ className, ...props }) {
38
+ return (_jsx(BaseFieldset.Legend, { "data-slot": "fieldset-legend", className: withBase("p-0 text-base font-medium text-fg data-disabled:opacity-45", className), ...props }));
39
+ }
40
+ /**
41
+ * Under a form's fields, the controls that complete it (a role, Send): on one line while the width
42
+ * holds them, wrapping onto the next when it doesn't, and lined up on their controls. A field in the
43
+ * row keeps its label and no description, so a hint can't push the others out of line: the hint
44
+ * belongs to the full-width field above.
45
+ */
46
+ export function FieldActions({ className, ...props }) {
47
+ return (_jsx("div", { "data-slot": "field-actions", className: cn("flex flex-wrap items-end gap-3", className), ...props }));
48
+ }
49
+ /** Label + control + description + error in one. */
50
+ export function Field({ label, hint, description, error, orientation = "vertical", nativeLabel = true, invalid, className, children, ...props }) {
51
+ const hasError = error !== undefined && error !== null && error !== false;
52
+ const labelNode = label === undefined ? null : (_jsx(FieldLabel, { ...(nativeLabel ? {} : { nativeLabel: false, render: _jsx("div", {}) }), children: label }));
53
+ const labelRow = hint === undefined ? (labelNode) : (_jsxs("div", { className: "flex items-baseline justify-between gap-3", children: [labelNode, _jsx("span", { className: "text-xs text-fg-subtle tabular", children: hint })] }));
54
+ const errorNode = hasError ? _jsx(FieldError, { match: true, children: error }) : _jsx(FieldError, {});
55
+ const descriptionNode = description === undefined ? null : _jsx(FieldDescription, { children: description });
56
+ if (orientation === "horizontal") {
57
+ return (_jsxs(FieldRoot, { invalid: hasError || invalid, className: (state) => cn("flex-row items-center justify-between gap-6", resolveClassName(className, state)), ...props, children: [_jsxs("div", { className: "flex min-w-0 flex-col gap-0.5", children: [labelRow, descriptionNode, errorNode] }), _jsx("div", { className: "flex shrink-0 items-center", children: children })] }));
58
+ }
59
+ return (_jsxs(FieldRoot, { invalid: hasError || invalid, className: className, ...props, children: [labelRow, children, descriptionNode, errorNode] }));
60
+ }
@@ -0,0 +1,47 @@
1
+ import { Toggle as BaseToggle } from "@base-ui/react/toggle";
2
+ import type { ComponentProps, ReactNode } from "react";
3
+ import { type VariantProps } from "../lib/cn.js";
4
+ declare const filterChipStyles: import("tailwind-variants").TVReturnType<{
5
+ size: {
6
+ /** Inside a picker or a popup. */
7
+ sm: "h-6 text-xs [&_svg:not([class*='size-'])]:size-3.5";
8
+ md: "h-7 text-sm [&_svg:not([class*='size-'])]:size-4";
9
+ };
10
+ /** On: ink, like the primary button. Off: a quiet surface with a faint hairline. */
11
+ pressed: {
12
+ true: "border-transparent bg-primary text-primary-fg";
13
+ false: "border-border-subtle bg-surface text-fg-muted hover:border-border hover:text-fg";
14
+ };
15
+ }, undefined, string[], {
16
+ size: {
17
+ /** Inside a picker or a popup. */
18
+ sm: "h-6 text-xs [&_svg:not([class*='size-'])]:size-3.5";
19
+ md: "h-7 text-sm [&_svg:not([class*='size-'])]:size-4";
20
+ };
21
+ /** On: ink, like the primary button. Off: a quiet surface with a faint hairline. */
22
+ pressed: {
23
+ true: "border-transparent bg-primary text-primary-fg";
24
+ false: "border-border-subtle bg-surface text-fg-muted hover:border-border hover:text-fg";
25
+ };
26
+ }, undefined, import("tailwind-variants").TVReturnTypeLike<{
27
+ size: {
28
+ /** Inside a picker or a popup. */
29
+ sm: "h-6 text-xs [&_svg:not([class*='size-'])]:size-3.5";
30
+ md: "h-7 text-sm [&_svg:not([class*='size-'])]:size-4";
31
+ };
32
+ /** On: ink, like the primary button. Off: a quiet surface with a faint hairline. */
33
+ pressed: {
34
+ true: "border-transparent bg-primary text-primary-fg";
35
+ false: "border-border-subtle bg-surface text-fg-muted hover:border-border hover:text-fg";
36
+ };
37
+ }, undefined>>;
38
+ export interface FilterChipProps extends ComponentProps<typeof BaseToggle>, Omit<VariantProps<typeof filterChipStyles>, "pressed"> {
39
+ /** Icon or logo before the label. */
40
+ leading?: ReactNode;
41
+ }
42
+ /**
43
+ * One filter of a list ("All", "Skills", a provider) as an on/off pill: `aria-pressed`, ink when on.
44
+ * A row of them narrows a list; one choice of a few that always has one is a SegmentedControl.
45
+ */
46
+ export declare function FilterChip({ size, leading, className, children, ...props }: FilterChipProps): import("react").JSX.Element;
47
+ export {};
@@ -0,0 +1,33 @@
1
+ "use client";
2
+ import { jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Toggle as BaseToggle } from "@base-ui/react/toggle";
4
+ import { cn, resolveClassName, tv } from "../lib/cn.js";
5
+ const filterChipStyles = tv({
6
+ base: [
7
+ "relative inline-flex shrink-0 select-none items-center justify-center gap-1.5 whitespace-nowrap rounded-full border px-3 touch-target",
8
+ "outline-none transition-[background-color,border-color,color] duration-(--duration-fast) ease-(--ease-standard)",
9
+ "focus-visible:ring-focus",
10
+ "data-disabled:pointer-events-none data-disabled:opacity-45",
11
+ "[&_svg]:shrink-0",
12
+ ],
13
+ variants: {
14
+ size: {
15
+ /** Inside a picker or a popup. */
16
+ sm: "h-6 text-xs [&_svg:not([class*='size-'])]:size-3.5",
17
+ md: "h-7 text-sm [&_svg:not([class*='size-'])]:size-4",
18
+ },
19
+ /** On: ink, like the primary button. Off: a quiet surface with a faint hairline. */
20
+ pressed: {
21
+ true: "border-transparent bg-primary text-primary-fg",
22
+ false: "border-border-subtle bg-surface text-fg-muted hover:border-border hover:text-fg",
23
+ },
24
+ },
25
+ defaultVariants: { size: "md", pressed: false },
26
+ });
27
+ /**
28
+ * One filter of a list ("All", "Skills", a provider) as an on/off pill: `aria-pressed`, ink when on.
29
+ * A row of them narrows a list; one choice of a few that always has one is a SegmentedControl.
30
+ */
31
+ export function FilterChip({ size, leading, className, children, ...props }) {
32
+ return (_jsxs(BaseToggle, { "data-slot": "filter-chip", className: (state) => cn(filterChipStyles({ size, pressed: state.pressed }), resolveClassName(className, state)), ...props, children: [leading, children] }));
33
+ }
@@ -0,0 +1,10 @@
1
+ import type { ComponentProps } from "react";
2
+ export interface GroupProps extends Omit<ComponentProps<"div">, "role"> {
3
+ /** What the group is, for assistive tech (`aria-label`). */
4
+ readonly label?: string | undefined;
5
+ }
6
+ /**
7
+ * A labelled group (`role="group"`) that isn't a form: a row of toggle buttons, a stack of avatars,
8
+ * a run of status text. A `<fieldset>` is for form controls and brings its own border and legend.
9
+ */
10
+ export declare function Group({ label, ...props }: GroupProps): import("react").JSX.Element;
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * A labelled group (`role="group"`) that isn't a form: a row of toggle buttons, a stack of avatars,
4
+ * a run of status text. A `<fieldset>` is for form controls and brings its own border and legend.
5
+ */
6
+ export function Group({ label, ...props }) {
7
+ // biome-ignore lint/a11y/useSemanticElements: this is the one non-form group; <fieldset> is for form controls.
8
+ return _jsx("div", { role: "group", "aria-label": label, ...props });
9
+ }
@@ -0,0 +1,11 @@
1
+ import type { ComponentProps, ReactNode } from "react";
2
+ export interface IconTextProps extends ComponentProps<"span"> {
3
+ /** Decorative: the words say what it marks. */
4
+ icon: ReactNode;
5
+ }
6
+ /**
7
+ * An icon and the words it marks, kept together in any language and at any width: the icon sits on
8
+ * the text's first line and the text wraps beside it, never under an icon left alone on a line. The
9
+ * icon's box is one line of the text's height; gap, colour and size come from `className`.
10
+ */
11
+ export declare function IconText({ icon, className, children, ...props }: IconTextProps): import("react").JSX.Element;
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../lib/cn.js";
3
+ /**
4
+ * An icon and the words it marks, kept together in any language and at any width: the icon sits on
5
+ * the text's first line and the text wraps beside it, never under an icon left alone on a line. The
6
+ * icon's box is one line of the text's height; gap, colour and size come from `className`.
7
+ */
8
+ export function IconText({ icon, className, children, ...props }) {
9
+ return (_jsxs("span", { "data-slot": "icon-text", className: cn("flex min-w-0 items-start gap-2", className), ...props, children: [_jsx("span", { "aria-hidden": true, className: "flex h-lh shrink-0 items-center", children: icon }), _jsx("span", { className: "min-w-0", children: children })] }));
10
+ }