@brijbyte/agentic-ui 0.0.2 → 0.0.4

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 (227) hide show
  1. package/README.md +132 -35
  2. package/dist/accordion/accordion.d.ts +15 -1
  3. package/dist/accordion/accordion.d.ts.map +1 -1
  4. package/dist/accordion/accordion.js +6 -1
  5. package/dist/accordion/accordion.js.map +1 -1
  6. package/dist/accordion/parts.d.ts +1 -1
  7. package/dist/accordion/parts.js +2 -2
  8. package/dist/alert-dialog/alert-dialog.d.ts +12 -1
  9. package/dist/alert-dialog/alert-dialog.d.ts.map +1 -1
  10. package/dist/alert-dialog/alert-dialog.js +6 -1
  11. package/dist/alert-dialog/alert-dialog.js.map +1 -1
  12. package/dist/alert-dialog/parts.d.ts +1 -1
  13. package/dist/alert-dialog/parts.js +2 -2
  14. package/dist/badge/badge.d.ts +7 -1
  15. package/dist/badge/badge.d.ts.map +1 -1
  16. package/dist/badge/badge.js +4 -0
  17. package/dist/badge/badge.js.map +1 -1
  18. package/dist/button/button.css +22 -8
  19. package/dist/button/button.d.ts +19 -8
  20. package/dist/button/button.d.ts.map +1 -1
  21. package/dist/button/button.js +6 -1
  22. package/dist/button/button.js.map +1 -1
  23. package/dist/button/button.module.js.map +1 -1
  24. package/dist/card/card.d.ts +11 -1
  25. package/dist/card/card.d.ts.map +1 -1
  26. package/dist/card/card.js +7 -0
  27. package/dist/card/card.js.map +1 -1
  28. package/dist/checkbox/checkbox.d.ts +14 -1
  29. package/dist/checkbox/checkbox.d.ts.map +1 -1
  30. package/dist/checkbox/checkbox.js +5 -1
  31. package/dist/checkbox/checkbox.js.map +1 -1
  32. package/dist/checkbox/parts.js +1 -1
  33. package/dist/collapsible/collapsible.d.ts +12 -1
  34. package/dist/collapsible/collapsible.d.ts.map +1 -1
  35. package/dist/collapsible/collapsible.js +5 -0
  36. package/dist/collapsible/collapsible.js.map +1 -1
  37. package/dist/collapsible/parts.js +1 -1
  38. package/dist/context-menu/context-menu.d.ts +6 -1
  39. package/dist/context-menu/context-menu.d.ts.map +1 -1
  40. package/dist/context-menu/context-menu.js +4 -0
  41. package/dist/context-menu/context-menu.js.map +1 -1
  42. package/dist/context-menu/parts.js +1 -1
  43. package/dist/dialog/dialog.d.ts +14 -2
  44. package/dist/dialog/dialog.d.ts.map +1 -1
  45. package/dist/dialog/dialog.js +6 -0
  46. package/dist/dialog/dialog.js.map +1 -1
  47. package/dist/dialog/parts.js +1 -1
  48. package/dist/drawer/drawer.d.ts +12 -1
  49. package/dist/drawer/drawer.d.ts.map +1 -1
  50. package/dist/drawer/drawer.js +5 -0
  51. package/dist/drawer/drawer.js.map +1 -1
  52. package/dist/drawer/parts.d.ts +1 -1
  53. package/dist/drawer/parts.js +1 -1
  54. package/dist/index.css +1773 -1316
  55. package/dist/index.d.ts +29 -20
  56. package/dist/index.js +50 -37
  57. package/dist/input/input.d.ts +8 -0
  58. package/dist/input/input.d.ts.map +1 -1
  59. package/dist/input/input.js +6 -1
  60. package/dist/input/input.js.map +1 -1
  61. package/dist/menu/menu.css +3 -8
  62. package/dist/menu/menu.d.ts +12 -5
  63. package/dist/menu/menu.d.ts.map +1 -1
  64. package/dist/menu/menu.js +10 -24
  65. package/dist/menu/menu.js.map +1 -1
  66. package/dist/menu/menu.module.js +1 -1
  67. package/dist/menu/menu.module.js.map +1 -1
  68. package/dist/menu/menuitemshortcut.js +1 -1
  69. package/dist/menu/parts.js +1 -1
  70. package/dist/meter/circular-meter.d.ts +48 -0
  71. package/dist/meter/circular-meter.d.ts.map +1 -0
  72. package/dist/meter/circular-meter.js +86 -0
  73. package/dist/meter/circular-meter.js.map +1 -0
  74. package/dist/meter/index.d.ts +4 -0
  75. package/dist/meter/index.js +5 -0
  76. package/dist/meter/meter.css +152 -0
  77. package/dist/meter/meter.d.ts +58 -0
  78. package/dist/meter/meter.d.ts.map +1 -0
  79. package/dist/meter/meter.js +50 -0
  80. package/dist/meter/meter.js.map +1 -0
  81. package/dist/meter/meter.module.css.d.ts +2 -0
  82. package/dist/meter/meter.module.js +27 -0
  83. package/dist/meter/meter.module.js.map +1 -0
  84. package/dist/meter/meterState.js +18 -0
  85. package/dist/meter/meterState.js.map +1 -0
  86. package/dist/meter/parts.d.ts +31 -0
  87. package/dist/meter/parts.d.ts.map +1 -0
  88. package/dist/meter/parts.js +56 -0
  89. package/dist/meter/parts.js.map +1 -0
  90. package/dist/number-field/number-field.d.ts +17 -1
  91. package/dist/number-field/number-field.d.ts.map +1 -1
  92. package/dist/number-field/number-field.js +5 -1
  93. package/dist/number-field/number-field.js.map +1 -1
  94. package/dist/number-field/parts.js +1 -1
  95. package/dist/popover/index.d.ts +3 -0
  96. package/dist/popover/index.js +4 -0
  97. package/dist/popover/parts.d.ts +43 -0
  98. package/dist/popover/parts.d.ts.map +1 -0
  99. package/dist/popover/parts.js +96 -0
  100. package/dist/popover/parts.js.map +1 -0
  101. package/dist/popover/popover.css +173 -0
  102. package/dist/popover/popover.d.ts +49 -0
  103. package/dist/popover/popover.d.ts.map +1 -0
  104. package/dist/popover/popover.js +68 -0
  105. package/dist/popover/popover.js.map +1 -0
  106. package/dist/popover/popover.module.css.d.ts +2 -0
  107. package/dist/popover/popover.module.js +16 -0
  108. package/dist/popover/popover.module.js.map +1 -0
  109. package/dist/progress/parts.js +1 -1
  110. package/dist/progress/progress.d.ts +11 -0
  111. package/dist/progress/progress.d.ts.map +1 -1
  112. package/dist/progress/progress.js +5 -0
  113. package/dist/progress/progress.js.map +1 -1
  114. package/dist/radio/index.d.ts +3 -0
  115. package/dist/radio/index.js +4 -0
  116. package/dist/radio/parts.d.ts +18 -0
  117. package/dist/radio/parts.d.ts.map +1 -0
  118. package/dist/radio/parts.js +42 -0
  119. package/dist/radio/parts.js.map +1 -0
  120. package/dist/radio/radio.css +84 -0
  121. package/dist/radio/radio.d.ts +31 -0
  122. package/dist/radio/radio.d.ts.map +1 -0
  123. package/dist/radio/radio.js +33 -0
  124. package/dist/radio/radio.js.map +1 -0
  125. package/dist/radio/radio.module.css.d.ts +2 -0
  126. package/dist/radio/radio.module.js +11 -0
  127. package/dist/radio/radio.module.js.map +1 -0
  128. package/dist/radio-group/index.d.ts +3 -0
  129. package/dist/radio-group/index.js +4 -0
  130. package/dist/radio-group/parts.d.ts +13 -0
  131. package/dist/radio-group/parts.d.ts.map +1 -0
  132. package/dist/radio-group/parts.js +31 -0
  133. package/dist/radio-group/parts.js.map +1 -0
  134. package/dist/radio-group/radio-group.css +17 -0
  135. package/dist/radio-group/radio-group.d.ts +37 -0
  136. package/dist/radio-group/radio-group.d.ts.map +1 -0
  137. package/dist/radio-group/radio-group.js +28 -0
  138. package/dist/radio-group/radio-group.js.map +1 -0
  139. package/dist/radio-group/radio-group.module.css.d.ts +2 -0
  140. package/dist/radio-group/radio-group.module.js +9 -0
  141. package/dist/radio-group/radio-group.module.js.map +1 -0
  142. package/dist/select/parts.js +1 -1
  143. package/dist/select/select.d.ts +15 -2
  144. package/dist/select/select.d.ts.map +1 -1
  145. package/dist/select/select.js +5 -1
  146. package/dist/select/select.js.map +1 -1
  147. package/dist/separator/separator.d.ts +4 -0
  148. package/dist/separator/separator.d.ts.map +1 -1
  149. package/dist/separator/separator.js +5 -1
  150. package/dist/separator/separator.js.map +1 -1
  151. package/dist/shared/PopupArrow.js +22 -0
  152. package/dist/shared/PopupArrow.js.map +1 -0
  153. package/dist/slider/parts.js +1 -1
  154. package/dist/slider/slider.d.ts +19 -1
  155. package/dist/slider/slider.d.ts.map +1 -1
  156. package/dist/slider/slider.js +6 -0
  157. package/dist/slider/slider.js.map +1 -1
  158. package/dist/styles/tokens.css +21 -8
  159. package/dist/switch/parts.js +1 -1
  160. package/dist/switch/switch.css +11 -2
  161. package/dist/switch/switch.d.ts +13 -1
  162. package/dist/switch/switch.d.ts.map +1 -1
  163. package/dist/switch/switch.js +5 -1
  164. package/dist/switch/switch.js.map +1 -1
  165. package/dist/switch/switch.module.js.map +1 -1
  166. package/dist/tabs/parts.js +1 -1
  167. package/dist/tabs/tabs.d.ts +9 -2
  168. package/dist/tabs/tabs.d.ts.map +1 -1
  169. package/dist/tabs/tabs.js +4 -0
  170. package/dist/tabs/tabs.js.map +1 -1
  171. package/dist/toast/parts.js +1 -1
  172. package/dist/toast/toast.d.ts +12 -1
  173. package/dist/toast/toast.d.ts.map +1 -1
  174. package/dist/toast/toast.js +8 -0
  175. package/dist/toast/toast.js.map +1 -1
  176. package/dist/tokens.css +23 -11
  177. package/dist/tooltip/parts.js +1 -1
  178. package/dist/tooltip/tooltip.d.ts +10 -1
  179. package/dist/tooltip/tooltip.d.ts.map +1 -1
  180. package/dist/tooltip/tooltip.js +4 -0
  181. package/dist/tooltip/tooltip.js.map +1 -1
  182. package/package.json +23 -2
  183. package/src/accordion/accordion.tsx +14 -0
  184. package/src/alert-dialog/alert-dialog.tsx +11 -0
  185. package/src/badge/badge.tsx +6 -0
  186. package/src/button/button.module.css +29 -13
  187. package/src/button/button.tsx +19 -8
  188. package/src/card/card.tsx +10 -0
  189. package/src/checkbox/checkbox.tsx +13 -0
  190. package/src/collapsible/collapsible.tsx +11 -0
  191. package/src/context-menu/context-menu.tsx +5 -0
  192. package/src/dialog/dialog.tsx +13 -1
  193. package/src/drawer/drawer.tsx +11 -0
  194. package/src/index.ts +25 -233
  195. package/src/input/input.tsx +8 -0
  196. package/src/menu/menu.module.css +3 -10
  197. package/src/menu/menu.tsx +13 -26
  198. package/src/meter/circular-meter.tsx +114 -0
  199. package/src/meter/index.ts +9 -0
  200. package/src/meter/meter.module.css +162 -0
  201. package/src/meter/meter.tsx +86 -0
  202. package/src/meter/meterState.ts +29 -0
  203. package/src/meter/parts.tsx +72 -0
  204. package/src/number-field/number-field.tsx +16 -0
  205. package/src/popover/index.ts +14 -0
  206. package/src/popover/parts.tsx +120 -0
  207. package/src/popover/popover.module.css +189 -0
  208. package/src/popover/popover.tsx +80 -0
  209. package/src/progress/progress.tsx +11 -0
  210. package/src/radio/index.ts +6 -0
  211. package/src/radio/parts.tsx +43 -0
  212. package/src/radio/radio.module.css +96 -0
  213. package/src/radio/radio.tsx +37 -0
  214. package/src/radio-group/index.ts +5 -0
  215. package/src/radio-group/parts.tsx +32 -0
  216. package/src/radio-group/radio-group.module.css +17 -0
  217. package/src/radio-group/radio-group.tsx +63 -0
  218. package/src/select/select.tsx +14 -1
  219. package/src/separator/separator.tsx +4 -0
  220. package/src/shared/PopupArrow.tsx +41 -0
  221. package/src/slider/slider.tsx +18 -0
  222. package/src/styles/tokens.css +23 -11
  223. package/src/switch/switch.module.css +11 -2
  224. package/src/switch/switch.tsx +12 -0
  225. package/src/tabs/tabs.tsx +8 -1
  226. package/src/toast/toast.tsx +11 -0
  227. package/src/tooltip/tooltip.tsx +9 -0
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Styled primitives for Radio (single item).
3
+ *
4
+ * @example
5
+ * ```tsx
6
+ * import { RadioGroup } from '@base-ui/react/radio-group';
7
+ * import { RadioRoot, RadioIndicator } from '@brijbyte/agentic-ui/radio';
8
+ *
9
+ * <RadioGroup defaultValue="a">
10
+ * <label htmlFor="opt-a">
11
+ * <RadioRoot id="opt-a" value="a">
12
+ * <RadioIndicator />
13
+ * </RadioRoot>
14
+ * Option A
15
+ * </label>
16
+ * </RadioGroup>
17
+ * ```
18
+ */
19
+ import { forwardRef } from "react";
20
+ import type { ComponentRef, ComponentPropsWithoutRef } from "react";
21
+ import { Radio as BaseRadio } from "@base-ui/react/radio";
22
+ import styles from "./radio.module.css";
23
+
24
+ type BaseRootProps = ComponentPropsWithoutRef<typeof BaseRadio.Root>;
25
+ type BaseIndicatorProps = ComponentPropsWithoutRef<typeof BaseRadio.Indicator>;
26
+
27
+ export interface RadioRootProps extends Omit<BaseRootProps, "className"> {
28
+ className?: string;
29
+ }
30
+ export interface RadioIndicatorProps extends Omit<BaseIndicatorProps, "className"> {
31
+ className?: string;
32
+ }
33
+
34
+ export const RadioRoot = forwardRef<ComponentRef<typeof BaseRadio.Root>, RadioRootProps>(function RadioRoot({ className, ...props }, ref) {
35
+ return <BaseRadio.Root ref={ref} className={`${styles.radio} ${className ?? ""}`} {...props} />;
36
+ });
37
+
38
+ export const RadioIndicator = forwardRef<ComponentRef<typeof BaseRadio.Indicator>, RadioIndicatorProps>(function RadioIndicator(
39
+ { className, ...props },
40
+ ref,
41
+ ) {
42
+ return <BaseRadio.Indicator ref={ref} keepMounted className={`${styles.indicator} ${className ?? ""}`} {...props} />;
43
+ });
@@ -0,0 +1,96 @@
1
+ @layer components {
2
+ /* ── Single radio item layout ───────────────────────────────────── */
3
+
4
+ .item {
5
+ display: flex;
6
+ align-items: center;
7
+ gap: var(--space-2);
8
+ cursor: pointer;
9
+ user-select: none;
10
+ }
11
+
12
+ .item:has([data-disabled]) {
13
+ cursor: not-allowed;
14
+ }
15
+
16
+ .item:has([data-disabled]) .label {
17
+ opacity: 0.44;
18
+ }
19
+
20
+ /* ── Radio button (the circle) ──────────────────────────────────── */
21
+
22
+ .radio {
23
+ flex-shrink: 0;
24
+ width: 16px;
25
+ height: 16px;
26
+ border-radius: var(--radius-full);
27
+ border: var(--border-width-base) solid var(--color-line-strong);
28
+ background-color: var(--color-surface-1);
29
+ display: flex;
30
+ align-items: center;
31
+ justify-content: center;
32
+ transition:
33
+ background-color var(--duration-fast) var(--easing-standard),
34
+ border-color var(--duration-fast) var(--easing-standard),
35
+ box-shadow var(--duration-fast) var(--easing-standard);
36
+ outline: none;
37
+ position: relative;
38
+ /* Reset button/span defaults */
39
+ padding: 0;
40
+ margin: 0;
41
+ }
42
+
43
+ .radio:focus-visible {
44
+ box-shadow: var(--shadow-focus);
45
+ border-color: var(--color-accent);
46
+ }
47
+
48
+ .radio:hover:not([data-disabled]) {
49
+ border-color: var(--color-accent);
50
+ }
51
+
52
+ .radio[data-checked] {
53
+ background-color: var(--color-accent);
54
+ border-color: var(--color-accent);
55
+ }
56
+
57
+ .radio[data-checked]:hover:not([data-disabled]) {
58
+ background-color: var(--color-accent-hover);
59
+ border-color: var(--color-accent-hover);
60
+ }
61
+
62
+ .radio[data-disabled] {
63
+ opacity: 0.44;
64
+ cursor: not-allowed;
65
+ pointer-events: none;
66
+ }
67
+
68
+ /* ── Indicator (inner dot) ──────────────────────────────────────── */
69
+
70
+ .indicator {
71
+ width: 6px;
72
+ height: 6px;
73
+ border-radius: var(--radius-full);
74
+ background-color: var(--color-on-accent);
75
+ transform: scale(0);
76
+ transition:
77
+ transform var(--duration-fast) var(--easing-spring),
78
+ opacity var(--duration-fast) var(--easing-standard);
79
+ opacity: 0;
80
+ }
81
+
82
+ /* data-checked is on the Root; the Indicator inherits it */
83
+ .radio[data-checked] .indicator {
84
+ transform: scale(1);
85
+ opacity: 1;
86
+ }
87
+
88
+ /* ── Label text ─────────────────────────────────────────────────── */
89
+
90
+ .label {
91
+ font-family: var(--font-mono);
92
+ font-size: var(--font-size-sm);
93
+ color: var(--color-primary);
94
+ line-height: var(--line-height-normal);
95
+ }
96
+ }
@@ -0,0 +1,37 @@
1
+ import { useId } from "react";
2
+ import type { ReactNode } from "react";
3
+ import { Radio as BaseRadio } from "@base-ui/react/radio";
4
+ import styles from "./radio.module.css";
5
+
6
+ export interface RadioProps {
7
+ /** Value submitted with the form and matched against the group's value. */
8
+ value: string;
9
+ /** Label rendered next to the button. */
10
+ children?: ReactNode;
11
+ disabled?: boolean;
12
+ readOnly?: boolean;
13
+ required?: boolean;
14
+ /** Override the auto-generated element id. */
15
+ id?: string;
16
+ className?: string;
17
+ }
18
+
19
+ /**
20
+ * A single radio button with an optional label.
21
+ * Must be placed inside `<RadioGroup>` or a raw base-ui RadioGroup.
22
+ */
23
+ export function Radio({ value, children, id, className, ...props }: RadioProps) {
24
+ // eslint-disable-next-line react-hooks/rules-of-hooks
25
+ const radioId = id ?? useId();
26
+
27
+ return (
28
+ <label className={`${styles.item} ${className ?? ""}`} htmlFor={radioId}>
29
+ <BaseRadio.Root id={radioId} value={value} className={styles.radio} {...props}>
30
+ <BaseRadio.Indicator keepMounted className={styles.indicator} />
31
+ </BaseRadio.Root>
32
+ {children && <span className={styles.label}>{children}</span>}
33
+ </label>
34
+ );
35
+ }
36
+
37
+ export { styles as RadioStyles };
@@ -0,0 +1,5 @@
1
+ export { RadioGroup, RadioGroupStyles } from "./radio-group";
2
+ export type { RadioGroupProps, RadioOption } from "./radio-group";
3
+
4
+ export { RadioGroupRoot } from "./parts";
5
+ export type { RadioGroupRootProps } from "./parts";
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Styled primitive for RadioGroup.
3
+ *
4
+ * @example
5
+ * ```tsx
6
+ * import { Radio } from '@brijbyte/agentic-ui/radio';
7
+ * import { RadioGroupRoot } from '@brijbyte/agentic-ui/radio-group';
8
+ *
9
+ * <RadioGroupRoot defaultValue="b" name="plan">
10
+ * <Radio value="a">Hobby</Radio>
11
+ * <Radio value="b">Pro</Radio>
12
+ * <Radio value="c">Enterprise</Radio>
13
+ * </RadioGroupRoot>
14
+ * ```
15
+ */
16
+ import { forwardRef } from "react";
17
+ import type { ComponentRef, ComponentPropsWithoutRef } from "react";
18
+ import { RadioGroup as BaseRadioGroup } from "@base-ui/react/radio-group";
19
+ import styles from "./radio-group.module.css";
20
+
21
+ type BaseGroupProps = ComponentPropsWithoutRef<typeof BaseRadioGroup>;
22
+
23
+ export interface RadioGroupRootProps extends Omit<BaseGroupProps, "className"> {
24
+ className?: string;
25
+ }
26
+
27
+ export const RadioGroupRoot = forwardRef<ComponentRef<typeof BaseRadioGroup>, RadioGroupRootProps>(function RadioGroupRoot(
28
+ { className, ...props },
29
+ ref,
30
+ ) {
31
+ return <BaseRadioGroup ref={ref} className={`${styles.group} ${className ?? ""}`} {...props} />;
32
+ });
@@ -0,0 +1,17 @@
1
+ @layer components {
2
+ .group {
3
+ display: flex;
4
+ flex-direction: column;
5
+ gap: var(--space-2);
6
+ }
7
+
8
+ .group-label {
9
+ font-family: var(--font-mono);
10
+ font-size: var(--font-size-xs);
11
+ font-weight: var(--font-weight-semibold);
12
+ color: var(--color-tertiary);
13
+ text-transform: uppercase;
14
+ letter-spacing: var(--letter-spacing-wide);
15
+ margin-bottom: var(--space-1);
16
+ }
17
+ }
@@ -0,0 +1,63 @@
1
+ import { useId } from "react";
2
+ import type { ReactNode } from "react";
3
+ import { RadioGroup as BaseRadioGroup } from "@base-ui/react/radio-group";
4
+ import { Radio } from "../radio/radio";
5
+ import styles from "./radio-group.module.css";
6
+
7
+ export interface RadioOption {
8
+ value: string;
9
+ label: ReactNode;
10
+ disabled?: boolean;
11
+ }
12
+
13
+ export interface RadioGroupProps {
14
+ /** The options to render. Each becomes one `<Radio>` item. */
15
+ options: RadioOption[];
16
+ /** Accessible group label rendered above the options. */
17
+ label?: ReactNode;
18
+ /** Controlled selected value. */
19
+ value?: string;
20
+ /** Initial selected value (uncontrolled). */
21
+ defaultValue?: string;
22
+ onValueChange?: (value: string, eventDetails: unknown) => void;
23
+ disabled?: boolean;
24
+ readOnly?: boolean;
25
+ required?: boolean;
26
+ /** HTML name for form submission. */
27
+ name?: string;
28
+ className?: string;
29
+ }
30
+
31
+ /**
32
+ * A labelled group of radio buttons. Renders a base-ui RadioGroup with one
33
+ * `<Radio>` per option.
34
+ *
35
+ * For full control over layout or behaviour use `RadioGroupRoot` with
36
+ * individual `<Radio>` or `<RadioRoot>` elements.
37
+ */
38
+ export { styles as RadioGroupStyles };
39
+
40
+ export function RadioGroup({ options, label, className, onValueChange, ...props }: RadioGroupProps) {
41
+ // eslint-disable-next-line react-hooks/rules-of-hooks
42
+ const labelId = useId();
43
+
44
+ return (
45
+ <BaseRadioGroup
46
+ aria-labelledby={label ? labelId : undefined}
47
+ className={`${styles.group} ${className ?? ""}`}
48
+ onValueChange={onValueChange as never}
49
+ {...props}
50
+ >
51
+ {label && (
52
+ <div id={labelId} className={styles["group-label"]}>
53
+ {label}
54
+ </div>
55
+ )}
56
+ {options.map((opt) => (
57
+ <Radio key={opt.value} value={opt.value} {...(opt.disabled !== undefined && { disabled: opt.disabled })}>
58
+ {opt.label}
59
+ </Radio>
60
+ ))}
61
+ </BaseRadioGroup>
62
+ );
63
+ }
@@ -14,17 +14,26 @@ export interface SelectGroup {
14
14
  }
15
15
 
16
16
  export interface SelectProps {
17
+ /** Currently selected value (controlled). */
17
18
  value?: string;
19
+ /** Initially selected value (uncontrolled). */
18
20
  defaultValue?: string;
19
- /** `eventDetails` is the base-ui event details object. Value may be null when cleared. */
21
+ /** Called when the selection changes. `eventDetails` is the base-ui event details object. Value may be null when cleared. */
20
22
  onValueChange?: (value: string | null, eventDetails: unknown) => void;
23
+ /** Prevent interaction. */
21
24
  disabled?: boolean;
25
+ /** Mark the field as required for form validation. */
22
26
  required?: boolean;
27
+ /** HTML name attribute for form submission. */
23
28
  name?: string;
29
+ /** Text shown when no value is selected. */
24
30
  placeholder?: string;
31
+ /** Flat list of options (use `groups` for grouped options). */
25
32
  options?: SelectOption[];
33
+ /** Grouped options with optional section labels. */
26
34
  groups?: SelectGroup[];
27
35
  className?: string;
36
+ /** Override the auto-generated element id. */
28
37
  id?: string;
29
38
  }
30
39
 
@@ -55,6 +64,10 @@ function SelectItem({ option }: { option: SelectOption }) {
55
64
  );
56
65
  }
57
66
 
67
+ /**
68
+ * Dropdown select with keyboard navigation, grouping, and search. Pass
69
+ * `options` for a flat list or `groups` for sections with optional labels.
70
+ */
58
71
  export function Select({ placeholder = "Select…", options, groups, className, onValueChange, value, defaultValue, ...props }: SelectProps) {
59
72
  const allOptions = useMemo(() => [...(options ?? []), ...(groups?.flatMap((g) => g.options) ?? [])], [options, groups]);
60
73
 
@@ -11,6 +11,10 @@ export interface SeparatorProps extends Omit<BaseSeparatorProps, "className"> {
11
11
  className?: string;
12
12
  }
13
13
 
14
+ /**
15
+ * A semantic divider accessible to screen readers. Renders a horizontal
16
+ * line by default; switch to vertical for inline layouts.
17
+ */
14
18
  export const Separator = forwardRef<ComponentRef<typeof BaseSeparator>, SeparatorProps>(function Separator(
15
19
  { className, orientation = "horizontal", ...props },
16
20
  ref,
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Shared arrow SVG used by Menu and Popover.
3
+ *
4
+ * Two paths:
5
+ * 1. Fill — the arrow body, matches the popup surface colour.
6
+ * 2. Seam — a thin strip in the border colour at the base, covering
7
+ * the popup border at the junction so it doesn't bleed through.
8
+ *
9
+ * All CSS classes come from the importing component's own CSS module
10
+ * so the rules are bundled into the component's CSS file, not a
11
+ * separate shared chunk that consumers would need to import separately.
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * <BaseMenu.Arrow className={styles.arrow}>
16
+ * <PopupArrow fillClass={styles["arrow-fill"]!} seamClass={styles["arrow-seam"]!} />
17
+ * </BaseMenu.Arrow>
18
+ * ```
19
+ */
20
+
21
+ interface PopupArrowProps {
22
+ fillClass: string;
23
+ seamClass: string;
24
+ }
25
+
26
+ export function PopupArrow({ fillClass, seamClass }: PopupArrowProps) {
27
+ return (
28
+ <svg width="20" height="10" viewBox="0 0 20 10" fill="none" aria-hidden>
29
+ {/* Body — surface colour */}
30
+ <path
31
+ d="M9.66437 2.60207L4.80758 6.97318C4.07308 7.63423 3.11989 8 2.13172 8H0V10H20V8H18.5349C17.5468 8 16.5936 7.63423 15.8591 6.97318L11.0023 2.60207C10.622 2.2598 10.0447 2.25979 9.66437 2.60207Z"
32
+ className={fillClass}
33
+ />
34
+ {/* Seam — border colour strip at the base, covers the popup border at the junction */}
35
+ <path
36
+ d="M10.3333 3.34539L5.47654 7.71648C4.55842 8.54279 3.36693 9 2.13172 9H0V8H2.13172C3.11989 8 4.07308 7.63423 4.80758 6.97318L9.66437 2.60207C10.0447 2.25979 10.622 2.2598 11.0023 2.60207L15.8591 6.97318C16.5936 7.63423 17.5468 8 18.5349 8H20V9H18.5349C17.2998 9 16.1083 8.54278 15.1901 7.71648L10.3333 3.34539Z"
37
+ className={seamClass}
38
+ />
39
+ </svg>
40
+ );
41
+ }
@@ -3,17 +3,29 @@ import { Slider as BaseSlider } from "@base-ui/react/slider";
3
3
  import styles from "./slider.module.css";
4
4
 
5
5
  export interface SliderProps {
6
+ /** Current value (controlled). Pass an array for range sliders. */
6
7
  value?: number | number[];
8
+ /** Initial value (uncontrolled). Pass an array for range sliders. */
7
9
  defaultValue?: number | number[];
10
+ /** Called on every value change while dragging. */
8
11
  onValueChange?: (value: number | number[], eventDetails: unknown) => void;
12
+ /** Called once when the user finishes dragging. */
9
13
  onValueCommitted?: (value: number | number[], eventDetails: unknown) => void;
14
+ /** Minimum allowed value. @default 0 */
10
15
  min?: number;
16
+ /** Maximum allowed value. @default 100 */
11
17
  max?: number;
18
+ /** Step increment. @default 1 */
12
19
  step?: number;
20
+ /** Step used for Page Up / Page Down keyboard shortcuts. @default 10 */
13
21
  largeStep?: number;
22
+ /** Prevent interaction. */
14
23
  disabled?: boolean;
24
+ /** HTML name attribute for form submission. */
15
25
  name?: string;
26
+ /** Axis direction. @default "horizontal" */
16
27
  orientation?: "horizontal" | "vertical";
28
+ /** `Intl.NumberFormatOptions` for value display formatting. */
17
29
  format?: Intl.NumberFormatOptions;
18
30
  /** Visible label rendered above the track. */
19
31
  label?: ReactNode;
@@ -24,6 +36,12 @@ export interface SliderProps {
24
36
  className?: string;
25
37
  }
26
38
 
39
+ /**
40
+ * An accessible range input with full keyboard and touch support. Pass a
41
+ * single number for a standard slider or an array for a range slider with
42
+ * two thumbs. Supports steps, large steps (Page Up/Down), and
43
+ * `Intl.NumberFormat` display formatting.
44
+ */
27
45
  export function Slider({
28
46
  label,
29
47
  showValue = false,
@@ -5,9 +5,6 @@
5
5
  * Monospace-first, semantic color tokens, supports light & dark modes.
6
6
  */
7
7
 
8
- /* Establish layer order — must come before any @layer usage */
9
- @layer theme, base, components, utilities;
10
-
11
8
  @layer theme {
12
9
  :root {
13
10
  /* ─── Typography ──────────────────────────────────────────────── */
@@ -146,7 +143,7 @@
146
143
 
147
144
  /* Text */
148
145
  --color-primary: rgba(0, 0, 0, 0.88);
149
- --color-secondary: rgba(0, 0, 0, 0.55);
146
+ --color-secondary: rgba(0, 0, 0, 0.6); /* raised from 0.55 — AA 5.01:1 on hover bg */
150
147
  --color-tertiary: rgba(0, 0, 0, 0.36);
151
148
  --color-disabled: rgba(0, 0, 0, 0.24);
152
149
  --color-inverse: rgba(255, 255, 255, 0.95);
@@ -170,6 +167,8 @@
170
167
  --color-accent: #0078d4;
171
168
  --color-accent-hover: #006bbf;
172
169
  --color-accent-pressed: #005ea8;
170
+ --color-accent-solid: #0078d4; /* solid button bg — same as accent in light (4.83:1 vs white) */
171
+ --color-accent-text: #005ea8; /* text/icon on tinted surfaces — AA 5.09:1 on accent-tint bg */
173
172
  --color-accent-tint: rgba(0, 120, 212, 0.1);
174
173
  --color-accent-tint-hover: rgba(0, 120, 212, 0.16);
175
174
 
@@ -190,20 +189,23 @@
190
189
  /* Status — Success */
191
190
  --color-success-bg: #f0fdf4;
192
191
  --color-success-border: #bbf7d0;
193
- --color-success-text: #15803d;
194
- --color-success-solid: #16a34a;
192
+ --color-success-text: #147a39; /* darkened from #15803d — AA 4.73:1 on canvas */
193
+ --color-success-solid: #15803d; /* darkened from #16a34a — AA 5.02:1 vs white */
194
+ --color-success-on-solid: #ffffff;
195
195
 
196
196
  /* Status — Warning */
197
197
  --color-warning-bg: #fffbeb;
198
198
  --color-warning-border: #fde68a;
199
199
  --color-warning-text: #92400e;
200
- --color-warning-solid: #d97706;
200
+ --color-warning-solid: #b45309; /* darkened from #d97706 — AA 5.02:1 vs white */
201
+ --color-warning-on-solid: #ffffff;
201
202
 
202
203
  /* Status — Error */
203
204
  --color-error-bg: #fef2f2;
204
205
  --color-error-border: #fecaca;
205
206
  --color-error-text: #991b1b;
206
207
  --color-error-solid: #dc2626;
208
+ --color-error-on-solid: #ffffff;
207
209
 
208
210
  /* Status — Info */
209
211
  --color-info-bg: #eff6ff;
@@ -268,6 +270,8 @@
268
270
  --color-accent: #0a84ff;
269
271
  --color-accent-hover: #0071e3;
270
272
  --color-accent-pressed: #005bb5;
273
+ --color-accent-solid: #0071e3; /* solid button bg — darkened from #0a84ff — AA 4.70:1 vs white */
274
+ --color-accent-text: #60a5fa; /* text/icon on tinted surfaces — AA 5.87:1 on accent-tint bg */
271
275
  --color-accent-tint: rgba(10, 132, 255, 0.12);
272
276
  --color-accent-tint-hover: rgba(10, 132, 255, 0.2);
273
277
 
@@ -289,19 +293,22 @@
289
293
  --color-success-bg: rgba(22, 163, 74, 0.12);
290
294
  --color-success-border: rgba(22, 163, 74, 0.3);
291
295
  --color-success-text: #4ade80;
292
- --color-success-solid: #22c55e;
296
+ --color-success-solid: #22c55e; /* vivid — AA 9.22:1 vs black text */
297
+ --color-success-on-solid: #000000; /* dark text preserves vibrancy */
293
298
 
294
299
  /* Status — Warning */
295
300
  --color-warning-bg: rgba(217, 119, 6, 0.12);
296
301
  --color-warning-border: rgba(217, 119, 6, 0.3);
297
302
  --color-warning-text: #fbbf24;
298
- --color-warning-solid: #f59e0b;
303
+ --color-warning-solid: #f59e0b; /* vivid — AA 9.78:1 vs black text */
304
+ --color-warning-on-solid: #000000; /* dark text preserves vibrancy */
299
305
 
300
306
  /* Status — Error */
301
307
  --color-error-bg: rgba(220, 38, 38, 0.12);
302
308
  --color-error-border: rgba(220, 38, 38, 0.3);
303
309
  --color-error-text: #f87171;
304
- --color-error-solid: #ef4444;
310
+ --color-error-solid: #dc2626; /* darkened from #ef4444 — AA 4.83:1 vs white */
311
+ --color-error-on-solid: #ffffff;
305
312
 
306
313
  /* Status — Info */
307
314
  --color-info-bg: rgba(37, 99, 235, 0.12);
@@ -355,6 +362,8 @@
355
362
  --color-accent: #0a84ff;
356
363
  --color-accent-hover: #0071e3;
357
364
  --color-accent-pressed: #005bb5;
365
+ --color-accent-solid: #0071e3;
366
+ --color-accent-text: #60a5fa;
358
367
  --color-accent-tint: rgba(10, 132, 255, 0.12);
359
368
  --color-accent-tint-hover: rgba(10, 132, 255, 0.2);
360
369
 
@@ -374,16 +383,19 @@
374
383
  --color-success-border: rgba(22, 163, 74, 0.3);
375
384
  --color-success-text: #4ade80;
376
385
  --color-success-solid: #22c55e;
386
+ --color-success-on-solid: #000000;
377
387
 
378
388
  --color-warning-bg: rgba(217, 119, 6, 0.12);
379
389
  --color-warning-border: rgba(217, 119, 6, 0.3);
380
390
  --color-warning-text: #fbbf24;
381
391
  --color-warning-solid: #f59e0b;
392
+ --color-warning-on-solid: #000000;
382
393
 
383
394
  --color-error-bg: rgba(220, 38, 38, 0.12);
384
395
  --color-error-border: rgba(220, 38, 38, 0.3);
385
396
  --color-error-text: #f87171;
386
- --color-error-solid: #ef4444;
397
+ --color-error-solid: #dc2626;
398
+ --color-error-on-solid: #ffffff;
387
399
 
388
400
  --color-info-bg: rgba(37, 99, 235, 0.12);
389
401
  --color-info-border: rgba(37, 99, 235, 0.3);
@@ -6,10 +6,14 @@
6
6
  cursor: pointer;
7
7
  user-select: none;
8
8
  }
9
- .root[data-disabled] {
10
- opacity: 0.44;
9
+ /* data-disabled lives on the inner button, not the label wrapper.
10
+ Use :has() to dim the label text, and target the track directly. */
11
+ .root:has([data-disabled]) {
11
12
  cursor: not-allowed;
12
13
  }
14
+ .root:has([data-disabled]) .label {
15
+ opacity: 0.44;
16
+ }
13
17
  .thumb-track {
14
18
  position: relative;
15
19
  width: 36px;
@@ -24,6 +28,11 @@
24
28
  outline: none;
25
29
  flex-shrink: 0;
26
30
  }
31
+ .thumb-track[data-disabled] {
32
+ opacity: 0.44;
33
+ cursor: not-allowed;
34
+ pointer-events: none;
35
+ }
27
36
  .thumb-track:focus-visible {
28
37
  box-shadow: var(--shadow-focus);
29
38
  }
@@ -4,19 +4,31 @@ import { Switch as BaseSwitch } from "@base-ui/react/switch";
4
4
  import styles from "./switch.module.css";
5
5
 
6
6
  export interface SwitchProps {
7
+ /** Controlled checked state. */
7
8
  checked?: boolean;
9
+ /** Initial checked state (uncontrolled). */
8
10
  defaultChecked?: boolean;
11
+ /** Prevent interaction. */
9
12
  disabled?: boolean;
13
+ /** Mark the field as required for form validation. */
10
14
  required?: boolean;
15
+ /** HTML name attribute for form submission. */
11
16
  name?: string;
17
+ /** HTML value attribute for form submission. */
12
18
  value?: string;
13
19
  /** Called when checked state changes. `eventDetails` is the base-ui event details object. */
14
20
  onCheckedChange?: (checked: boolean, eventDetails: unknown) => void;
21
+ /** Label content rendered next to the switch. */
15
22
  children?: ReactNode;
23
+ /** Override the auto-generated element id. */
16
24
  id?: string;
17
25
  className?: string;
18
26
  }
19
27
 
28
+ /**
29
+ * Toggle for boolean settings. Renders a track with a sliding thumb and
30
+ * an optional label. Wraps `@base-ui/react` Switch.
31
+ */
20
32
  export function Switch({ children, id, className, onCheckedChange, ...props }: SwitchProps) {
21
33
  // eslint-disable-next-line react-hooks/rules-of-hooks
22
34
  const switchId = id ?? useId();
package/src/tabs/tabs.tsx CHANGED
@@ -11,14 +11,21 @@ export interface TabItem {
11
11
  }
12
12
 
13
13
  export interface TabsProps {
14
+ /** Array of tab definitions to render. */
14
15
  items: TabItem[];
16
+ /** Currently active tab value (controlled). */
15
17
  value?: string;
18
+ /** Initially active tab value (uncontrolled). Defaults to the first item. */
16
19
  defaultValue?: string;
17
- /** `eventDetails` is the base-ui event details object. */
20
+ /** Called when the active tab changes. `eventDetails` is the base-ui event details object. */
18
21
  onValueChange?: (value: unknown, eventDetails: unknown) => void;
19
22
  className?: string;
20
23
  }
21
24
 
25
+ /**
26
+ * Tab navigation for switching between content panels. Each tab can have
27
+ * an optional icon and can be individually disabled.
28
+ */
22
29
  export function Tabs({ items, className, ...props }: TabsProps) {
23
30
  const defaultVal = props.defaultValue ?? items[0]?.value;
24
31
 
@@ -77,8 +77,11 @@ const DEFAULT_LIMIT: Record<NonNullable<ToastViewportProps["variant"]>, number>
77
77
 
78
78
  export interface ToastProviderProps {
79
79
  children: ReactNode;
80
+ /** Layout style for the viewport. @default "list" */
80
81
  variant?: ToastViewportProps["variant"];
82
+ /** Maximum number of toasts shown at once. */
81
83
  limit?: number;
84
+ /** Auto-dismiss delay in milliseconds. */
82
85
  timeout?: number;
83
86
  }
84
87
 
@@ -91,6 +94,10 @@ const MAX_LIMIT: Record<NonNullable<ToastViewportProps["variant"]>, number> = {
91
94
  list: 5,
92
95
  };
93
96
 
97
+ /**
98
+ * Drop-in provider that wires limit defaults to the chosen viewport variant.
99
+ * Use this instead of base-ui's `Toast.Provider` when using `ToastViewport`.
100
+ */
94
101
  export function ToastProvider({ children, variant = "list", limit, timeout }: ToastProviderProps) {
95
102
  const resolvedLimit = Math.min(limit ?? DEFAULT_LIMIT[variant], MAX_LIMIT[variant]);
96
103
  return (
@@ -100,6 +107,10 @@ export function ToastProvider({ children, variant = "list", limit, timeout }: To
100
107
  );
101
108
  }
102
109
 
110
+ /**
111
+ * Renders and manages toast notifications. Supports `"stacked"` (Sonner-style
112
+ * fanned cards) and `"list"` (vertically stacked, each fully visible) layouts.
113
+ */
103
114
  export function ToastViewport({ variant = "list", limit: _limit }: ToastViewportProps) {
104
115
  const manager = useToastManager();
105
116
  const isStacked = variant === "stacked";