@brijbyte/agentic-ui 0.0.1 → 0.0.2

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 (135) hide show
  1. package/README.md +183 -111
  2. package/dist/accordion/accordion.css +6 -10
  3. package/dist/accordion/accordion.module.js.map +1 -1
  4. package/dist/alert-dialog/alert-dialog.css +84 -0
  5. package/dist/alert-dialog/alert-dialog.d.ts +44 -0
  6. package/dist/alert-dialog/alert-dialog.d.ts.map +1 -0
  7. package/dist/alert-dialog/alert-dialog.js +46 -0
  8. package/dist/alert-dialog/alert-dialog.js.map +1 -0
  9. package/dist/alert-dialog/alert-dialog.module.css.d.ts +2 -0
  10. package/dist/alert-dialog/alert-dialog.module.js +14 -0
  11. package/dist/alert-dialog/alert-dialog.module.js.map +1 -0
  12. package/dist/alert-dialog/index.d.ts +3 -0
  13. package/dist/alert-dialog/index.js +4 -0
  14. package/dist/alert-dialog/parts.d.ts +28 -0
  15. package/dist/alert-dialog/parts.d.ts.map +1 -0
  16. package/dist/alert-dialog/parts.js +62 -0
  17. package/dist/alert-dialog/parts.js.map +1 -0
  18. package/dist/badge/badge.css +3 -7
  19. package/dist/badge/badge.module.js.map +1 -1
  20. package/dist/button/button.css +14 -14
  21. package/dist/button/button.module.js.map +1 -1
  22. package/dist/card/card.css +5 -9
  23. package/dist/card/card.module.js.map +1 -1
  24. package/dist/checkbox/checkbox.css +3 -7
  25. package/dist/checkbox/checkbox.module.js.map +1 -1
  26. package/dist/collapsible/collapsible.css +7 -11
  27. package/dist/collapsible/collapsible.module.js.map +1 -1
  28. package/dist/context-menu/context-menu.css +151 -0
  29. package/dist/context-menu/context-menu.d.ts +36 -0
  30. package/dist/context-menu/context-menu.d.ts.map +1 -0
  31. package/dist/context-menu/context-menu.js +54 -0
  32. package/dist/context-menu/context-menu.js.map +1 -0
  33. package/dist/context-menu/context-menu.module.css.d.ts +2 -0
  34. package/dist/context-menu/context-menu.module.js +18 -0
  35. package/dist/context-menu/context-menu.module.js.map +1 -0
  36. package/dist/context-menu/index.d.ts +3 -0
  37. package/dist/context-menu/index.js +4 -0
  38. package/dist/context-menu/parts.d.ts +38 -0
  39. package/dist/context-menu/parts.d.ts.map +1 -0
  40. package/dist/context-menu/parts.js +91 -0
  41. package/dist/context-menu/parts.js.map +1 -0
  42. package/dist/dialog/dialog.css +27 -22
  43. package/dist/dialog/dialog.d.ts +8 -0
  44. package/dist/dialog/dialog.d.ts.map +1 -1
  45. package/dist/dialog/dialog.js +7 -4
  46. package/dist/dialog/dialog.js.map +1 -1
  47. package/dist/dialog/dialog.module.js +2 -0
  48. package/dist/dialog/dialog.module.js.map +1 -1
  49. package/dist/drawer/drawer.css +9 -13
  50. package/dist/drawer/drawer.module.js.map +1 -1
  51. package/dist/index.css +1630 -1353
  52. package/dist/index.d.ts +24 -18
  53. package/dist/index.js +10 -1
  54. package/dist/input/input.css +5 -9
  55. package/dist/input/input.module.js.map +1 -1
  56. package/dist/layer-order.css +22 -0
  57. package/dist/menu/menu.css +13 -17
  58. package/dist/menu/menu.module.js.map +1 -1
  59. package/dist/number-field/number-field.css +12 -16
  60. package/dist/number-field/number-field.module.js.map +1 -1
  61. package/dist/progress/progress.css +1 -5
  62. package/dist/progress/progress.module.js.map +1 -1
  63. package/dist/reset.css +6 -6
  64. package/dist/select/select.css +14 -16
  65. package/dist/select/select.d.ts +5 -2
  66. package/dist/select/select.d.ts.map +1 -1
  67. package/dist/select/select.js +11 -2
  68. package/dist/select/select.js.map +1 -1
  69. package/dist/select/select.module.js.map +1 -1
  70. package/dist/separator/separator.css +1 -5
  71. package/dist/separator/separator.module.js.map +1 -1
  72. package/dist/slider/index.d.ts +3 -0
  73. package/dist/slider/index.js +4 -0
  74. package/dist/slider/parts.d.ts +38 -0
  75. package/dist/slider/parts.d.ts.map +1 -0
  76. package/dist/slider/parts.js +69 -0
  77. package/dist/slider/parts.js.map +1 -0
  78. package/dist/slider/slider.css +97 -0
  79. package/dist/slider/slider.d.ts +38 -0
  80. package/dist/slider/slider.d.ts.map +1 -0
  81. package/dist/slider/slider.js +41 -0
  82. package/dist/slider/slider.js.map +1 -0
  83. package/dist/slider/slider.module.css.d.ts +2 -0
  84. package/dist/slider/slider.module.js +15 -0
  85. package/dist/slider/slider.module.js.map +1 -0
  86. package/dist/styles/reset.css +6 -6
  87. package/dist/styles/tokens.css +73 -71
  88. package/dist/switch/switch.css +2 -6
  89. package/dist/switch/switch.module.js.map +1 -1
  90. package/dist/tabs/tabs.css +5 -9
  91. package/dist/tabs/tabs.module.js.map +1 -1
  92. package/dist/tailwind-theme.css +23 -23
  93. package/dist/toast/toast.css +11 -15
  94. package/dist/toast/toast.module.js.map +1 -1
  95. package/dist/tokens.css +79 -75
  96. package/dist/tooltip/tooltip.css +7 -11
  97. package/dist/tooltip/tooltip.module.js.map +1 -1
  98. package/package.json +17 -1
  99. package/src/accordion/accordion.module.css +6 -20
  100. package/src/alert-dialog/alert-dialog.module.css +91 -0
  101. package/src/alert-dialog/alert-dialog.tsx +69 -0
  102. package/src/alert-dialog/index.ts +7 -0
  103. package/src/alert-dialog/parts.tsx +73 -0
  104. package/src/badge/badge.module.css +3 -13
  105. package/src/button/button.module.css +15 -51
  106. package/src/card/card.module.css +5 -16
  107. package/src/checkbox/checkbox.module.css +3 -14
  108. package/src/collapsible/collapsible.module.css +7 -20
  109. package/src/context-menu/context-menu.module.css +168 -0
  110. package/src/context-menu/context-menu.tsx +75 -0
  111. package/src/context-menu/index.ts +21 -0
  112. package/src/context-menu/parts.tsx +99 -0
  113. package/src/dialog/dialog.module.css +26 -33
  114. package/src/dialog/dialog.tsx +14 -1
  115. package/src/drawer/drawer.module.css +9 -58
  116. package/src/index.ts +48 -0
  117. package/src/input/input.module.css +5 -21
  118. package/src/menu/menu.module.css +13 -43
  119. package/src/number-field/number-field.module.css +12 -28
  120. package/src/progress/progress.module.css +1 -10
  121. package/src/select/select.module.css +14 -35
  122. package/src/select/select.tsx +14 -5
  123. package/src/separator/separator.module.css +1 -5
  124. package/src/slider/index.ts +14 -0
  125. package/src/slider/parts.tsx +90 -0
  126. package/src/slider/slider.module.css +110 -0
  127. package/src/slider/slider.tsx +68 -0
  128. package/src/styles/layer-order.css +22 -0
  129. package/src/styles/reset.css +6 -6
  130. package/src/styles/tailwind-theme.css +23 -23
  131. package/src/styles/tokens.css +79 -75
  132. package/src/switch/switch.module.css +2 -12
  133. package/src/tabs/tabs.module.css +5 -18
  134. package/src/toast/toast.module.css +11 -51
  135. package/src/tooltip/tooltip.module.css +7 -18
@@ -1,5 +1,3 @@
1
- @layer theme, base, components, utilities;
2
-
3
1
  @layer components {
4
2
  .root {
5
3
  display: flex;
@@ -7,30 +5,25 @@
7
5
  align-items: flex-start;
8
6
  gap: var(--space-1);
9
7
  }
10
-
11
8
  .scrub-area {
12
9
  cursor: ew-resize;
13
10
  user-select: none;
14
11
  }
15
-
16
12
  .scrub-area-cursor {
17
13
  filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.5));
18
14
  }
19
-
20
15
  .label {
21
16
  cursor: ew-resize;
22
17
  font-family: var(--font-mono);
23
18
  font-size: var(--font-size-xs);
24
19
  font-weight: var(--font-weight-medium);
25
- color: var(--color-text-secondary);
20
+ color: var(--color-secondary);
26
21
  letter-spacing: var(--letter-spacing-wide);
27
22
  text-transform: uppercase;
28
23
  }
29
-
30
24
  .group {
31
25
  display: flex;
32
26
  }
33
-
34
27
  .input {
35
28
  box-sizing: border-box;
36
29
  margin: 0;
@@ -42,27 +35,24 @@
42
35
  font-weight: normal;
43
36
  text-align: center;
44
37
  font-variant-numeric: tabular-nums;
45
- color: var(--color-text-primary);
46
- background-color: var(--color-bg-elevated);
47
- border-top: var(--border-width-base) solid var(--color-border-base);
48
- border-bottom: var(--border-width-base) solid var(--color-border-base);
38
+ color: var(--color-primary);
39
+ background-color: var(--color-elevated);
40
+ border-top: var(--border-width-base) solid var(--color-line);
41
+ border-bottom: var(--border-width-base) solid var(--color-line);
49
42
  border-left: none;
50
43
  border-right: none;
51
44
  border-radius: 0;
52
45
  outline: none;
53
46
  transition: box-shadow var(--duration-fast) var(--easing-standard);
54
47
  }
55
-
56
48
  .input:focus {
57
49
  z-index: 1;
58
- box-shadow: inset var(--shadow-focus);
50
+ box-shadow: inset 0 0 0 1.5px var(--color-accent);
59
51
  }
60
-
61
52
  .input[data-disabled] {
62
53
  opacity: 0.44;
63
54
  cursor: not-allowed;
64
55
  }
65
-
66
56
  .step-button {
67
57
  box-sizing: border-box;
68
58
  display: flex;
@@ -72,9 +62,9 @@
72
62
  height: 2rem;
73
63
  margin: 0;
74
64
  padding: 0;
75
- border: var(--border-width-base) solid var(--color-border-base);
65
+ border: var(--border-width-base) solid var(--color-line);
76
66
  background-color: var(--color-surface-1);
77
- color: var(--color-text-secondary);
67
+ color: var(--color-secondary);
78
68
  cursor: pointer;
79
69
  outline: none;
80
70
  user-select: none;
@@ -83,31 +73,25 @@
83
73
  color var(--duration-fast) var(--easing-standard),
84
74
  transform 100ms var(--easing-ease-out);
85
75
  }
86
-
87
76
  .step-button:hover {
88
- background-color: var(--color-surface-hover);
89
- color: var(--color-text-primary);
77
+ background-color: var(--color-hover);
78
+ color: var(--color-primary);
90
79
  }
91
-
92
80
  .step-button:active:not([data-disabled]) {
93
- background-color: var(--color-surface-active);
81
+ background-color: var(--color-active);
94
82
  transform: scale(0.93);
95
83
  }
96
-
97
84
  .step-button:focus-visible {
98
85
  z-index: 1;
99
- box-shadow: inset var(--shadow-focus);
86
+ box-shadow: inset 0 0 0 1.5px var(--color-accent);
100
87
  }
101
-
102
88
  .step-button[data-disabled] {
103
89
  opacity: 0.44;
104
90
  cursor: not-allowed;
105
91
  }
106
-
107
92
  .decrement {
108
93
  border-radius: var(--radius-md) 0 0 var(--radius-md);
109
94
  }
110
-
111
95
  .increment {
112
96
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
113
97
  }
@@ -1,5 +1,3 @@
1
- @layer theme, base, components, utilities;
2
-
3
1
  @layer components {
4
2
  .root {
5
3
  display: flex;
@@ -7,16 +5,14 @@
7
5
  gap: var(--space-1-5);
8
6
  width: 100%;
9
7
  }
10
-
11
8
  .label-row {
12
9
  display: flex;
13
10
  align-items: center;
14
11
  justify-content: space-between;
15
12
  font-family: var(--font-mono);
16
13
  font-size: var(--font-size-xs);
17
- color: var(--color-text-secondary);
14
+ color: var(--color-secondary);
18
15
  }
19
-
20
16
  .track {
21
17
  width: 100%;
22
18
  height: 6px;
@@ -24,7 +20,6 @@
24
20
  border-radius: var(--radius-full);
25
21
  overflow: hidden;
26
22
  }
27
-
28
23
  .track-sm {
29
24
  height: 4px;
30
25
  }
@@ -34,14 +29,12 @@
34
29
  .track-lg {
35
30
  height: 8px;
36
31
  }
37
-
38
32
  .indicator {
39
33
  height: 100%;
40
34
  border-radius: var(--radius-full);
41
35
  background-color: var(--color-accent);
42
36
  transition: width var(--duration-slower) var(--easing-standard);
43
37
  }
44
-
45
38
  .indicator-success {
46
39
  background-color: var(--color-success-solid);
47
40
  }
@@ -51,7 +44,6 @@
51
44
  .indicator-error {
52
45
  background-color: var(--color-error-solid);
53
46
  }
54
-
55
47
  /* Indeterminate */
56
48
  @keyframes progress-indeterminate {
57
49
  0% {
@@ -61,7 +53,6 @@
61
53
  transform: translateX(400%);
62
54
  }
63
55
  }
64
-
65
56
  .indicator[data-value="null"],
66
57
  .indicator-indeterminate {
67
58
  width: 30% !important;
@@ -1,5 +1,3 @@
1
- @layer theme, base, components, utilities;
2
-
3
1
  @layer components {
4
2
  .trigger {
5
3
  display: inline-flex;
@@ -9,9 +7,9 @@
9
7
  width: 100%;
10
8
  font-family: var(--font-mono);
11
9
  font-size: var(--font-size-sm);
12
- color: var(--color-text-primary);
10
+ color: var(--color-primary);
13
11
  background-color: var(--color-surface-1);
14
- border: var(--border-width-base) solid var(--color-border-base);
12
+ border: var(--border-width-base) solid var(--color-line);
15
13
  border-radius: var(--radius-md);
16
14
  cursor: pointer;
17
15
  outline: none;
@@ -27,64 +25,55 @@
27
25
  overflow: hidden;
28
26
  text-overflow: ellipsis;
29
27
  }
30
-
31
28
  .trigger:hover:not([data-disabled]) {
32
- border-color: var(--color-border-strong);
29
+ border-color: var(--color-line-strong);
33
30
  }
34
-
35
31
  .trigger:focus-visible {
36
32
  border-color: var(--color-accent);
37
33
  box-shadow: var(--shadow-focus);
38
34
  }
39
-
40
35
  .trigger[data-disabled] {
41
36
  opacity: 0.44;
42
37
  cursor: not-allowed;
43
38
  }
44
-
45
39
  .trigger-value {
46
40
  flex: 1;
47
41
  overflow: hidden;
48
42
  text-overflow: ellipsis;
49
43
  }
50
-
51
44
  .trigger-icon {
52
45
  flex-shrink: 0;
53
- color: var(--color-text-tertiary);
46
+ color: var(--color-tertiary);
54
47
  transition: transform var(--duration-normal) var(--easing-standard);
55
48
  }
56
-
57
49
  .trigger[data-popup-open] .trigger-icon {
58
50
  transform: rotate(180deg);
59
51
  }
60
-
61
52
  /* ─── Popup / Listbox ───────────────────────────────── */
62
53
  .positioner {
63
54
  z-index: var(--z-dropdown);
64
55
  }
65
-
66
56
  .popup {
67
- background-color: var(--color-bg-overlay);
68
- border: var(--border-width-base) solid var(--color-border-base);
57
+ background-color: var(--color-overlay);
58
+ border: var(--border-width-base) solid var(--color-line);
69
59
  border-radius: var(--radius-lg);
70
60
  box-shadow: var(--shadow-popover);
71
61
  padding: var(--space-1);
72
- min-width: var(--available-width);
62
+ min-width: var(--anchor-width);
63
+ max-width: var(--available-width);
64
+ width: fit-content;
73
65
  max-height: 280px;
74
66
  overflow-y: auto;
75
67
  outline: none;
76
-
77
68
  transform-origin: var(--transform-origin);
78
69
  transition:
79
70
  opacity 150ms var(--easing-ease-out),
80
71
  transform 150ms var(--easing-ease-out);
81
72
  }
82
-
83
73
  .popup[data-starting-style] {
84
74
  opacity: 0;
85
75
  transform: scale(0.95);
86
76
  }
87
-
88
77
  .popup[data-ending-style] {
89
78
  opacity: 0;
90
79
  transform: scale(0.98);
@@ -92,7 +81,6 @@
92
81
  opacity 75ms var(--easing-ease-in),
93
82
  transform 75ms var(--easing-ease-in);
94
83
  }
95
-
96
84
  /* Backdrop blur for macOS feel */
97
85
  @supports (backdrop-filter: blur(12px)) {
98
86
  .popup {
@@ -100,7 +88,6 @@
100
88
  -webkit-backdrop-filter: blur(12px) saturate(1.5);
101
89
  }
102
90
  }
103
-
104
91
  .item {
105
92
  display: flex;
106
93
  align-items: center;
@@ -108,56 +95,48 @@
108
95
  padding: var(--space-1-5) var(--space-2-5);
109
96
  font-family: var(--font-mono);
110
97
  font-size: var(--font-size-sm);
111
- color: var(--color-text-primary);
98
+ color: var(--color-primary);
112
99
  border-radius: var(--radius-sm);
113
100
  cursor: default;
114
101
  outline: none;
115
102
  transition: background-color var(--duration-fast) var(--easing-standard);
116
103
  user-select: none;
117
104
  }
118
-
119
105
  .item:hover,
120
106
  .item[data-highlighted] {
121
107
  background-color: var(--color-accent);
122
- color: var(--color-text-on-accent);
108
+ color: var(--color-on-accent);
123
109
  }
124
-
125
110
  .item[data-selected] {
126
111
  font-weight: var(--font-weight-medium);
127
112
  }
128
-
129
113
  .item[data-disabled] {
130
114
  opacity: 0.44;
131
115
  cursor: not-allowed;
132
116
  }
133
-
134
117
  .item-indicator {
135
118
  margin-left: auto;
136
119
  color: var(--color-accent);
137
120
  display: flex;
138
121
  align-items: center;
139
122
  }
140
-
141
123
  .item:hover .item-indicator,
142
124
  .item[data-highlighted] .item-indicator {
143
- color: var(--color-text-on-accent);
125
+ color: var(--color-on-accent);
144
126
  }
145
-
146
127
  .group-label {
147
128
  padding: var(--space-1) var(--space-2-5);
148
129
  font-size: var(--font-size-xs);
149
130
  font-weight: var(--font-weight-semibold);
150
- color: var(--color-text-tertiary);
131
+ color: var(--color-tertiary);
151
132
  letter-spacing: var(--letter-spacing-wider);
152
133
  text-transform: uppercase;
153
134
  }
154
-
155
135
  .separator {
156
136
  height: var(--border-width-base);
157
- background-color: var(--color-border-subtle);
137
+ background-color: var(--color-line-subtle);
158
138
  margin: var(--space-1) 0;
159
139
  }
160
-
161
140
  /* List container — wraps items inside the popup */
162
141
  .list {
163
142
  display: flex;
@@ -1,14 +1,15 @@
1
+ import { useMemo, type ReactNode } from "react";
1
2
  import { Select as BaseSelect } from "@base-ui/react/select";
2
3
  import styles from "./select.module.css";
3
4
 
4
5
  export interface SelectOption {
5
6
  value: string;
6
- label: string;
7
+ label: ReactNode;
7
8
  disabled?: boolean;
8
9
  }
9
10
 
10
11
  export interface SelectGroup {
11
- label?: string;
12
+ label?: ReactNode;
12
13
  options: SelectOption[];
13
14
  }
14
15
 
@@ -54,11 +55,19 @@ function SelectItem({ option }: { option: SelectOption }) {
54
55
  );
55
56
  }
56
57
 
57
- export function Select({ placeholder = "Select…", options, groups, className, onValueChange, ...props }: SelectProps) {
58
+ export function Select({ placeholder = "Select…", options, groups, className, onValueChange, value, defaultValue, ...props }: SelectProps) {
59
+ const allOptions = useMemo(() => [...(options ?? []), ...(groups?.flatMap((g) => g.options) ?? [])], [options, groups]);
60
+
58
61
  return (
59
- <BaseSelect.Root onValueChange={onValueChange as never} {...props}>
62
+ <BaseSelect.Root onValueChange={onValueChange as never} value={value} defaultValue={defaultValue} {...props}>
60
63
  <BaseSelect.Trigger className={`${styles.trigger} ${className ?? ""}`}>
61
- <BaseSelect.Value className={styles["trigger-value"]} placeholder={placeholder} />
64
+ <BaseSelect.Value className={styles["trigger-value"]} placeholder={placeholder}>
65
+ {(currentValue: string | null) => {
66
+ if (!currentValue) return placeholder;
67
+ const match = allOptions.find((o) => o.value === currentValue);
68
+ return match ? match.label : currentValue;
69
+ }}
70
+ </BaseSelect.Value>
62
71
  <span className={styles["trigger-icon"]}>
63
72
  <ChevronIcon />
64
73
  </span>
@@ -1,16 +1,12 @@
1
- @layer theme, base, components, utilities;
2
-
3
1
  @layer components {
4
2
  .separator {
5
- background-color: var(--color-border-base);
3
+ background-color: var(--color-line);
6
4
  flex-shrink: 0;
7
5
  }
8
-
9
6
  .separator[data-orientation="horizontal"] {
10
7
  width: 100%;
11
8
  height: var(--border-width-base, 1px);
12
9
  }
13
-
14
10
  .separator[data-orientation="vertical"] {
15
11
  width: var(--border-width-base, 1px);
16
12
  align-self: stretch;
@@ -0,0 +1,14 @@
1
+ export { Slider } from "./slider";
2
+ export type { SliderProps } from "./slider";
3
+
4
+ export { SliderControl, SliderTrack, SliderIndicator, SliderThumb, SliderLabel, SliderValue } from "./parts";
5
+ export type {
6
+ SliderControlProps,
7
+ SliderTrackProps,
8
+ SliderIndicatorProps,
9
+ SliderThumbProps,
10
+ SliderLabelProps,
11
+ SliderValueProps,
12
+ } from "./parts";
13
+
14
+ export { SliderStyles } from "./slider";
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Styled primitives for Slider.
3
+ *
4
+ * @example
5
+ * ```tsx
6
+ * import { Slider as BaseSlider } from '@base-ui/react/slider';
7
+ * import { SliderControl, SliderTrack, SliderIndicator, SliderThumb } from '@brijbyte/agentic-ui/slider';
8
+ *
9
+ * <BaseSlider.Root defaultValue={50}>
10
+ * <SliderControl>
11
+ * <SliderTrack>
12
+ * <SliderIndicator />
13
+ * <SliderThumb aria-label="Volume" />
14
+ * </SliderTrack>
15
+ * </SliderControl>
16
+ * </BaseSlider.Root>
17
+ * ```
18
+ */
19
+ import { forwardRef } from "react";
20
+ import type { ComponentRef, ComponentPropsWithoutRef } from "react";
21
+ import { Slider as BaseSlider } from "@base-ui/react/slider";
22
+ import styles from "./slider.module.css";
23
+
24
+ type BaseControlProps = ComponentPropsWithoutRef<typeof BaseSlider.Control>;
25
+ type BaseTrackProps = ComponentPropsWithoutRef<typeof BaseSlider.Track>;
26
+ type BaseIndicatorProps = ComponentPropsWithoutRef<typeof BaseSlider.Indicator>;
27
+ type BaseThumbProps = ComponentPropsWithoutRef<typeof BaseSlider.Thumb>;
28
+ type BaseLabelProps = ComponentPropsWithoutRef<typeof BaseSlider.Label>;
29
+ type BaseValueProps = ComponentPropsWithoutRef<typeof BaseSlider.Value>;
30
+
31
+ export interface SliderControlProps extends Omit<BaseControlProps, "className"> {
32
+ className?: string;
33
+ }
34
+ export interface SliderTrackProps extends Omit<BaseTrackProps, "className"> {
35
+ className?: string;
36
+ }
37
+ export interface SliderIndicatorProps extends Omit<BaseIndicatorProps, "className"> {
38
+ className?: string;
39
+ }
40
+ export interface SliderThumbProps extends Omit<BaseThumbProps, "className"> {
41
+ className?: string;
42
+ }
43
+ export interface SliderLabelProps extends Omit<BaseLabelProps, "className"> {
44
+ className?: string;
45
+ }
46
+ export interface SliderValueProps extends Omit<BaseValueProps, "className"> {
47
+ className?: string;
48
+ }
49
+
50
+ export const SliderControl = forwardRef<ComponentRef<typeof BaseSlider.Control>, SliderControlProps>(function SliderControl(
51
+ { className, ...props },
52
+ ref,
53
+ ) {
54
+ return <BaseSlider.Control ref={ref} className={`${styles.control} ${className ?? ""}`} {...props} />;
55
+ });
56
+
57
+ export const SliderTrack = forwardRef<ComponentRef<typeof BaseSlider.Track>, SliderTrackProps>(function SliderTrack(
58
+ { className, ...props },
59
+ ref,
60
+ ) {
61
+ return <BaseSlider.Track ref={ref} className={`${styles.track} ${className ?? ""}`} {...props} />;
62
+ });
63
+
64
+ export const SliderIndicator = forwardRef<ComponentRef<typeof BaseSlider.Indicator>, SliderIndicatorProps>(function SliderIndicator(
65
+ { className, ...props },
66
+ ref,
67
+ ) {
68
+ return <BaseSlider.Indicator ref={ref} className={`${styles.indicator} ${className ?? ""}`} {...props} />;
69
+ });
70
+
71
+ export const SliderThumb = forwardRef<ComponentRef<typeof BaseSlider.Thumb>, SliderThumbProps>(function SliderThumb(
72
+ { className, ...props },
73
+ ref,
74
+ ) {
75
+ return <BaseSlider.Thumb ref={ref} className={`${styles.thumb} ${className ?? ""}`} {...props} />;
76
+ });
77
+
78
+ export const SliderLabel = forwardRef<ComponentRef<typeof BaseSlider.Label>, SliderLabelProps>(function SliderLabel(
79
+ { className, ...props },
80
+ ref,
81
+ ) {
82
+ return <BaseSlider.Label ref={ref} className={`${styles.label} ${className ?? ""}`} {...props} />;
83
+ });
84
+
85
+ export const SliderValue = forwardRef<ComponentRef<typeof BaseSlider.Value>, SliderValueProps>(function SliderValue(
86
+ { className, ...props },
87
+ ref,
88
+ ) {
89
+ return <BaseSlider.Value ref={ref} className={`${styles.value} ${className ?? ""}`} {...props} />;
90
+ });
@@ -0,0 +1,110 @@
1
+ @layer components {
2
+ .root {
3
+ display: flex;
4
+ flex-direction: column;
5
+ gap: var(--space-2);
6
+ width: 100%;
7
+ }
8
+
9
+ /* ─── Label + Value row ──────────────────────────────────────── */
10
+
11
+ .header {
12
+ display: flex;
13
+ align-items: center;
14
+ justify-content: space-between;
15
+ gap: var(--space-2);
16
+ }
17
+
18
+ .label {
19
+ font-family: var(--font-mono);
20
+ font-size: var(--font-size-xs);
21
+ font-weight: var(--font-weight-medium);
22
+ color: var(--color-secondary);
23
+ letter-spacing: var(--letter-spacing-wide);
24
+ text-transform: uppercase;
25
+ user-select: none;
26
+ }
27
+
28
+ .value {
29
+ font-family: var(--font-mono);
30
+ font-size: var(--font-size-xs);
31
+ color: var(--color-tertiary);
32
+ font-variant-numeric: tabular-nums;
33
+ }
34
+
35
+ /* ─── Control (hit area) ─────────────────────────────────────── */
36
+
37
+ .control {
38
+ display: flex;
39
+ align-items: center;
40
+ width: 100%;
41
+ padding-block: var(--space-2);
42
+ touch-action: none;
43
+ user-select: none;
44
+ cursor: pointer;
45
+ }
46
+
47
+ .control[data-disabled] {
48
+ opacity: 0.44;
49
+ cursor: not-allowed;
50
+ }
51
+
52
+ /* ─── Track ──────────────────────────────────────────────────── */
53
+
54
+ .track {
55
+ position: relative;
56
+ width: 100%;
57
+ height: 4px;
58
+ border-radius: var(--radius-full);
59
+ background-color: var(--color-surface-3);
60
+ border: var(--border-width-base) solid var(--color-line-subtle);
61
+ user-select: none;
62
+ }
63
+
64
+ /* ─── Indicator (filled portion) ────────────────────────────── */
65
+
66
+ .indicator {
67
+ border-radius: var(--radius-full);
68
+ background-color: var(--color-accent);
69
+ user-select: none;
70
+ transition: background-color var(--duration-fast) var(--easing-standard);
71
+ }
72
+
73
+ .root[data-disabled] .indicator {
74
+ background-color: var(--color-tertiary);
75
+ }
76
+
77
+ /* ─── Thumb ──────────────────────────────────────────────────── */
78
+
79
+ .thumb {
80
+ width: 16px;
81
+ height: 16px;
82
+ border-radius: var(--radius-full);
83
+ background-color: var(--color-elevated);
84
+ border: var(--border-width-base) solid var(--color-line);
85
+ box-shadow: var(--shadow-sm);
86
+ user-select: none;
87
+ transition:
88
+ transform var(--duration-fast) var(--easing-spring),
89
+ box-shadow var(--duration-fast) var(--easing-standard),
90
+ border-color var(--duration-fast) var(--easing-standard);
91
+ }
92
+
93
+ /* Grow on drag — gives tactile feedback */
94
+ .thumb[data-dragging] {
95
+ transform: scale(1.2);
96
+ border-color: var(--color-accent);
97
+ box-shadow: var(--shadow-md);
98
+ }
99
+
100
+ /* Focus ring via the nested <input> */
101
+ .thumb:has(:focus-visible) {
102
+ border-color: var(--color-accent);
103
+ box-shadow: var(--shadow-focus);
104
+ }
105
+
106
+ .thumb[data-disabled] {
107
+ cursor: not-allowed;
108
+ background-color: var(--color-surface-3);
109
+ }
110
+ }
@@ -0,0 +1,68 @@
1
+ import type { ReactNode } from "react";
2
+ import { Slider as BaseSlider } from "@base-ui/react/slider";
3
+ import styles from "./slider.module.css";
4
+
5
+ export interface SliderProps {
6
+ value?: number | number[];
7
+ defaultValue?: number | number[];
8
+ onValueChange?: (value: number | number[], eventDetails: unknown) => void;
9
+ onValueCommitted?: (value: number | number[], eventDetails: unknown) => void;
10
+ min?: number;
11
+ max?: number;
12
+ step?: number;
13
+ largeStep?: number;
14
+ disabled?: boolean;
15
+ name?: string;
16
+ orientation?: "horizontal" | "vertical";
17
+ format?: Intl.NumberFormatOptions;
18
+ /** Visible label rendered above the track. */
19
+ label?: ReactNode;
20
+ /** Show the current value next to the label. */
21
+ showValue?: boolean;
22
+ /** Aria-label for single-thumb sliders without a visible label. */
23
+ "aria-label"?: string;
24
+ className?: string;
25
+ }
26
+
27
+ export function Slider({
28
+ label,
29
+ showValue = false,
30
+ className,
31
+ onValueChange,
32
+ onValueCommitted,
33
+ "aria-label": ariaLabel,
34
+ ...props
35
+ }: SliderProps) {
36
+ const isRange = Array.isArray(props.defaultValue ?? props.value);
37
+
38
+ return (
39
+ <BaseSlider.Root
40
+ className={`${styles.root} ${className ?? ""}`}
41
+ onValueChange={onValueChange as never}
42
+ onValueCommitted={onValueCommitted as never}
43
+ {...props}
44
+ >
45
+ {(label || showValue) && (
46
+ <div className={styles.header}>
47
+ {label && <BaseSlider.Label className={styles.label}>{label}</BaseSlider.Label>}
48
+ {showValue && <BaseSlider.Value className={styles.value} />}
49
+ </div>
50
+ )}
51
+ <BaseSlider.Control className={styles.control}>
52
+ <BaseSlider.Track className={styles.track}>
53
+ <BaseSlider.Indicator className={styles.indicator} />
54
+ {isRange ? (
55
+ <>
56
+ <BaseSlider.Thumb index={0} className={styles.thumb} aria-label="Minimum value" />
57
+ <BaseSlider.Thumb index={1} className={styles.thumb} aria-label="Maximum value" />
58
+ </>
59
+ ) : (
60
+ <BaseSlider.Thumb className={styles.thumb} aria-label={ariaLabel} />
61
+ )}
62
+ </BaseSlider.Track>
63
+ </BaseSlider.Control>
64
+ </BaseSlider.Root>
65
+ );
66
+ }
67
+
68
+ export { styles as SliderStyles };