@ctrl-ui/react 0.8.0 → 0.8.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 (62) hide show
  1. package/README.md +1 -0
  2. package/dist/blocks/coding-agent.js +3 -1
  3. package/dist/blocks/file-explorer.js +3 -1
  4. package/dist/blocks/settings.js +3 -1
  5. package/dist/control-props.d.ts +2 -1
  6. package/dist/email/email.d.ts +28 -0
  7. package/dist/email/email.js +79 -0
  8. package/dist/email/length.d.ts +4 -0
  9. package/dist/email/length.js +34 -0
  10. package/dist/email/templates.d.ts +51 -0
  11. package/dist/email/templates.js +228 -0
  12. package/dist/email/theme.d.ts +35 -0
  13. package/dist/email/theme.js +113 -0
  14. package/dist/extensions/create-track-highlight.d.ts +0 -2
  15. package/dist/extensions/create-track-highlight.js +49 -8
  16. package/dist/extensions/supports-anchor-transitions.d.ts +4 -0
  17. package/dist/extensions/supports-anchor-transitions.js +31 -0
  18. package/dist/extensions/track-highlight.d.ts +0 -6
  19. package/dist/extensions/track-highlight.js +25 -9
  20. package/dist/knob-contracts/button-knobs.d.ts +1 -1
  21. package/dist/knob-contracts/button-knobs.js +4 -1
  22. package/dist/knob-contracts/progressive-blur-knobs.d.ts +5 -0
  23. package/dist/knob-contracts/progressive-blur-knobs.js +9 -0
  24. package/dist/scripts/contrast-eval.d.ts +46 -0
  25. package/dist/scripts/contrast-eval.js +327 -0
  26. package/dist/skin.config.js +9 -1
  27. package/dist/skin.d.ts +11 -9
  28. package/dist/skin.js +0 -1
  29. package/dist/styles/index.css +3 -1
  30. package/dist/styles/recipes/button-group.css +46 -0
  31. package/dist/styles/recipes/button.css +53 -5
  32. package/dist/styles/recipes/progressive-blur.css +145 -0
  33. package/dist/styles/recipes/scroll-area.css +140 -5
  34. package/dist/styles/recipes/sidebar-menu.css +156 -0
  35. package/dist/styles/recipes/sidebar.css +25 -69
  36. package/dist/styles/recipes/table-of-contents.css +1 -1
  37. package/dist/styles/recipes/track-highlight.css +126 -2
  38. package/dist/styles/recipes/tree.css +4 -0
  39. package/dist/ui/button-group.d.ts +3 -1
  40. package/dist/ui/button-group.js +21 -18
  41. package/dist/ui/button.d.ts +3 -1
  42. package/dist/ui/button.js +13 -4
  43. package/dist/ui/checkbox-group.d.ts +5 -3
  44. package/dist/ui/checkbox-group.js +23 -6
  45. package/dist/ui/combobox.d.ts +2 -0
  46. package/dist/ui/dropdown-menu.d.ts +28 -21
  47. package/dist/ui/dropdown-menu.js +112 -13
  48. package/dist/ui/progressive-blur.d.ts +12 -0
  49. package/dist/ui/progressive-blur.js +47 -0
  50. package/dist/ui/scroll-area.d.ts +4 -1
  51. package/dist/ui/scroll-area.js +27 -4
  52. package/dist/ui/sidebar-menu.d.ts +49 -0
  53. package/dist/ui/sidebar-menu.js +160 -0
  54. package/dist/ui/sidebar-provider.d.ts +28 -0
  55. package/dist/ui/sidebar-provider.js +80 -0
  56. package/dist/ui/sidebar.d.ts +4 -61
  57. package/dist/ui/sidebar.js +23 -202
  58. package/dist/ui/table-of-contents.js +3 -11
  59. package/dist/ui/toggle.d.ts +3 -2
  60. package/dist/ui/toggle.js +22 -10
  61. package/dist/ui/tree.js +13 -19
  62. package/package.json +5 -1
@@ -0,0 +1,156 @@
1
+ @layer components {
2
+ :where([data-control-family="sidebar"][data-slot="group-label"]) {
3
+ gap: calc(var(--spacing) * 2);
4
+ padding-inline: calc(var(--spacing) * 2);
5
+ border-radius: var(--radius-md);
6
+ color: var(--cui-sidebar-group-label-foreground);
7
+ font-size: var(--text-caption);
8
+ font-weight: var(--font-weight-medium);
9
+ outline: none;
10
+ transition-property: margin, opacity;
11
+ transition-duration: var(--duration-base);
12
+ transition-timing-function: var(--ease-standard);
13
+ }
14
+
15
+ :where([data-control-family="sidebar"][data-slot="menu-button"]),
16
+ :where([data-control-family="sidebar"][data-slot="menu-action"]) {
17
+ gap: calc(var(--spacing) * 1.5);
18
+ block-size: var(--control-h-md);
19
+ border-radius: var(--cui-sidebar-menu-button-radius);
20
+ padding-inline: var(--padding-x);
21
+ font-size: var(--text-body);
22
+ background-color: transparent;
23
+ color: var(--cui-sidebar-menu-button-foreground);
24
+ outline: none;
25
+ transition-property: width, height, padding, color, background-color, box-shadow;
26
+ transition-duration: var(--duration-base);
27
+ transition-timing-function: var(--ease-standard);
28
+ }
29
+
30
+ :where([data-control-family="sidebar"][data-slot="menu-button"][data-size="sm"]) {
31
+ block-size: var(--control-h-sm);
32
+ padding-inline: calc(var(--padding-x) * 0.75);
33
+ font-size: var(--text-label);
34
+ }
35
+
36
+ :where([data-control-family="sidebar"][data-slot="menu-button"][data-size="lg"]) {
37
+ gap: calc(var(--spacing) * 2);
38
+ block-size: var(--control-h-lg);
39
+ padding-inline: calc(var(--padding-x) * 1.25);
40
+ }
41
+
42
+ @media (hover: hover) {
43
+ :where([data-control-family="sidebar"][data-slot="menu-button"]:hover),
44
+ :where([data-control-family="sidebar"][data-slot="menu-action"]:hover) {
45
+ background-color: var(--cui-sidebar-menu-button-hover-background);
46
+ color: var(--cui-sidebar-menu-button-hover-foreground);
47
+ box-shadow: var(--cui-sidebar-menu-button-hover-shadow);
48
+ }
49
+
50
+ :where([data-control-family="sidebar"][data-slot="menu-button"][data-state="open"]:hover) {
51
+ background-color: var(--cui-sidebar-menu-button-active-background);
52
+ color: var(--cui-sidebar-menu-button-active-foreground);
53
+ }
54
+ }
55
+
56
+ :where([data-control-family="sidebar"][data-slot="menu-action"]:is(:active, [data-popup-open])),
57
+ :where([data-control-family="sidebar"][data-slot="menu-button"]:active),
58
+ :where([data-control-family="sidebar"][data-slot="menu-button"][data-active]) {
59
+ background-color: var(--cui-sidebar-menu-button-active-background);
60
+ color: var(--cui-sidebar-menu-button-active-foreground);
61
+ box-shadow: var(--cui-sidebar-menu-button-active-shadow);
62
+ }
63
+
64
+ :where([data-control-family="sidebar"][data-slot="menu-button"][data-active]) {
65
+ font-weight: var(--font-weight-medium);
66
+ }
67
+
68
+ :where([data-control-family="sidebar"][data-slot="menu-button"][data-variant="outline"]) {
69
+ background-color: var(--background);
70
+ box-shadow: 0 0 0 1px var(--sidebar-border);
71
+ }
72
+
73
+ @media (hover: hover) {
74
+ :where([data-control-family="sidebar"][data-slot="menu-button"][data-variant="outline"]:hover) {
75
+ box-shadow: 0 0 0 1px oklch(from var(--foreground) l c h / 0.08);
76
+ }
77
+ }
78
+
79
+ :where(
80
+ [data-control-family="sidebar"][data-indicator="slide"]
81
+ [data-control-family="sidebar"][data-slot="menu-button"]:is(:hover, :active, [data-active], [data-popup-open])
82
+ ) {
83
+ background-color: transparent;
84
+ }
85
+
86
+ @media (hover: hover) {
87
+ :where([data-control-family="sidebar"][data-indicator="hover"] [data-slot="menu-button"]:hover:not([data-active])) {
88
+ background-color: transparent;
89
+ box-shadow: none;
90
+ }
91
+ }
92
+
93
+ :where([data-control-family="sidebar"][data-slot="menu-action"]:is(:disabled, [aria-disabled="true"])),
94
+ :where([data-control-family="sidebar"][data-slot="menu-button"]:disabled),
95
+ :where([data-control-family="sidebar"][data-slot="menu-button"][aria-disabled="true"]) {
96
+ opacity: 0.5;
97
+ }
98
+
99
+ :where([data-control-family="sidebar"][data-slot="group-content"]) {
100
+ font-size: var(--text-body);
101
+ }
102
+
103
+ :where([data-control-family="sidebar"][data-slot="menu-action"]) {
104
+ inset-block-start: calc(var(--control-h-md) / 2);
105
+ padding: 0;
106
+ }
107
+
108
+ :where([data-control-family="sidebar"][data-slot="menu-button"][data-size="sm"] ~ [data-slot="menu-action"]) {
109
+ inset-block-start: calc(var(--control-h-sm) / 2);
110
+ }
111
+
112
+ :where([data-control-family="sidebar"][data-slot="menu-button"][data-size="lg"] ~ [data-slot="menu-action"]) {
113
+ inset-block-start: calc(var(--control-h-lg) / 2);
114
+ }
115
+
116
+ :where([data-control-family="sidebar"][data-slot="menu-button"]:has(~ [data-slot="menu-action"])) {
117
+ padding-inline-end: calc(var(--spacing) * 8);
118
+ }
119
+
120
+ :where([data-control-family="sidebar"][data-slot="menu-action"]:focus-visible) {
121
+ outline: 2px solid var(--sidebar-ring);
122
+ outline-offset: 2px;
123
+ }
124
+
125
+ @media (hover: hover) and (pointer: fine) {
126
+ :where([data-control-family="sidebar"][data-slot="menu-action"][data-show-on-hover]) {
127
+ opacity: 0;
128
+ }
129
+
130
+ :where([data-control-family="sidebar"][data-slot="menu-item"]:is(:hover, :focus-within) [data-slot="menu-action"]),
131
+ :where([data-control-family="sidebar"][data-slot="menu-action"][data-popup-open]) {
132
+ opacity: 1;
133
+ }
134
+ }
135
+
136
+ @media (pointer: coarse) {
137
+ :where([data-control-family="sidebar"][data-slot="menu-action"]) {
138
+ min-inline-size: 44px;
139
+ min-block-size: 44px;
140
+ inset-inline-end: 0;
141
+ inset-block-start: max(22px, calc(var(--control-h-md) / 2));
142
+ }
143
+
144
+ :where([data-control-family="sidebar"][data-slot="menu-button"][data-size="sm"] ~ [data-slot="menu-action"]) {
145
+ inset-block-start: max(22px, calc(var(--control-h-sm) / 2));
146
+ }
147
+
148
+ :where([data-control-family="sidebar"][data-slot="menu-button"][data-size="lg"] ~ [data-slot="menu-action"]) {
149
+ inset-block-start: max(22px, calc(var(--control-h-lg) / 2));
150
+ }
151
+
152
+ :where([data-control-family="sidebar"][data-slot="menu-button"]:has(~ [data-slot="menu-action"])) {
153
+ padding-inline-end: calc(var(--spacing) * 12);
154
+ }
155
+ }
156
+ }
@@ -134,90 +134,46 @@
134
134
  gap: calc(var(--spacing) * 1);
135
135
  }
136
136
 
137
- :where([data-control-family="sidebar"][data-slot="group-label"]) {
138
- gap: calc(var(--spacing) * 2);
139
- padding-inline: calc(var(--spacing) * 2);
140
- border-radius: var(--radius-md);
141
- color: var(--cui-sidebar-group-label-foreground);
142
- font-size: var(--text-caption);
143
- font-weight: var(--font-weight-medium);
144
- outline: none;
145
- transition-property: margin, opacity;
146
- transition-duration: var(--duration-base);
147
- transition-timing-function: var(--ease-standard);
137
+ :where([data-control-family="sidebar"][data-slot="menu"][data-submenu]) {
138
+ margin-block: calc(var(--spacing) * 1);
139
+ margin-inline-start: calc(var(--spacing) * 3);
140
+ padding-inline-start: calc(var(--spacing) * 2);
141
+ border-inline-start: 1px solid var(--sidebar-border);
142
+ width: auto;
148
143
  }
149
144
 
150
- :where([data-control-family="sidebar"][data-slot="menu-button"]) {
151
- gap: calc(var(--spacing) * 1.5);
152
- block-size: var(--control-h-md);
153
- border-radius: var(--cui-sidebar-menu-button-radius);
154
- padding-inline: var(--padding-x);
155
- font-size: var(--text-body);
156
- background-color: transparent;
157
- color: var(--cui-sidebar-menu-button-foreground);
158
- outline: none;
159
- transition-property: width, height, padding, color, background-color, box-shadow;
160
- transition-duration: var(--duration-base);
161
- transition-timing-function: var(--ease-standard);
145
+ :where([data-control-family="sidebar"]:is([data-slot="group-label"], [data-slot="menu-button"])[aria-expanded] > [data-slot="chevron"]) {
146
+ margin-inline-start: auto;
147
+ transition: rotate var(--duration-base) var(--ease-standard);
162
148
  }
163
149
 
164
- :where([data-control-family="sidebar"][data-slot="menu-button"][data-size="sm"]) {
165
- block-size: var(--control-h-sm);
166
- padding-inline: calc(var(--padding-x) * 0.75);
167
- font-size: var(--text-label);
150
+ :where([data-control-family="sidebar"][aria-expanded="true"] > [data-slot="chevron"]) {
151
+ rotate: 90deg;
168
152
  }
169
153
 
170
- :where([data-control-family="sidebar"][data-slot="menu-button"][data-size="lg"]) {
171
- gap: calc(var(--spacing) * 2);
172
- block-size: var(--control-h-lg);
173
- padding-inline: calc(var(--padding-x) * 1.25);
154
+ :where([data-control-family="sidebar"][data-slot="group-label"][aria-expanded]) {
155
+ cursor: pointer;
156
+ width: 100%;
157
+ text-align: start;
174
158
  }
175
159
 
176
- @media (hover: hover) {
177
- :where([data-control-family="sidebar"][data-slot="menu-button"]:hover) {
178
- background-color: var(--cui-sidebar-menu-button-hover-background);
179
- color: var(--cui-sidebar-menu-button-hover-foreground);
180
- box-shadow: var(--cui-sidebar-menu-button-hover-shadow);
181
- }
182
-
183
- :where([data-control-family="sidebar"][data-slot="menu-button"][data-state="open"]:hover) {
184
- background-color: var(--cui-sidebar-menu-button-active-background);
185
- color: var(--cui-sidebar-menu-button-active-foreground);
160
+ @media (pointer: coarse) {
161
+ :where([data-control-family="sidebar"]:is([data-slot="menu-button"], [data-slot="group-label"][aria-expanded])) {
162
+ min-block-size: 44px;
186
163
  }
187
- }
188
-
189
- :where([data-control-family="sidebar"][data-slot="menu-button"]:active),
190
- :where([data-control-family="sidebar"][data-slot="menu-button"][data-active]) {
191
- background-color: var(--cui-sidebar-menu-button-active-background);
192
- color: var(--cui-sidebar-menu-button-active-foreground);
193
- box-shadow: var(--cui-sidebar-menu-button-active-shadow);
194
- }
195
-
196
- :where([data-control-family="sidebar"][data-slot="menu-button"][data-active]) {
197
- font-weight: var(--font-weight-medium);
198
- }
199
-
200
- :where([data-control-family="sidebar"][data-slot="menu-button"][data-variant="outline"]) {
201
- background-color: var(--background);
202
- box-shadow: 0 0 0 1px var(--sidebar-border);
203
- }
204
164
 
205
- @media (hover: hover) {
206
- :where([data-control-family="sidebar"][data-slot="menu-button"][data-variant="outline"]:hover) {
207
- box-shadow: 0 0 0 1px oklch(from var(--foreground) l c h / 0.08);
165
+ :where([data-control-family="button"][data-sidebar-trigger]) {
166
+ min-inline-size: 44px;
167
+ min-block-size: 44px;
208
168
  }
209
169
  }
210
170
 
211
- :where(
212
- [data-control-family="sidebar"][data-indicator="slide"]
213
- [data-control-family="sidebar"][data-slot="menu-button"]:is(:hover, :active, [data-active], [data-popup-open])
214
- ) {
215
- background-color: transparent;
171
+ :where([data-control-family="sidebar"][data-slot="menu"]:is([data-indicator="hover"], [data-indicator="slide"])) {
172
+ gap: 0;
216
173
  }
217
174
 
218
- :where([data-control-family="sidebar"][data-slot="menu-button"]:disabled),
219
- :where([data-control-family="sidebar"][data-slot="menu-button"][aria-disabled="true"]) {
220
- opacity: 0.5;
175
+ :where([data-control-family="sidebar"][data-slot="menu-track"] > [data-control-family="track-highlight"]) {
176
+ --cui-track-highlight-radius: var(--cui-sidebar-menu-button-radius);
221
177
  }
222
178
 
223
179
  :where([data-control-family="sidebar"][data-slot="root"][data-side="right"] [data-control-family="sidebar"][data-slot="gap"]) {
@@ -1,6 +1,5 @@
1
1
  @layer components {
2
2
  :where([data-control-family="table-of-contents"][data-slot="root"]) {
3
- --cui-track-highlight-transition-duration: var(--duration-base);
4
3
  --cui-table-of-contents-rail-background: oklch(from var(--border) l c h / 0.6);
5
4
  --cui-table-of-contents-highlight-radius: var(--radius-md);
6
5
  --cui-table-of-contents-highlight-background: oklch(from var(--foreground) l c h / 0.04);
@@ -71,6 +70,7 @@
71
70
  }
72
71
 
73
72
  :where([data-control-family="table-of-contents"][data-slot="root"] [data-control-family="track-highlight"][data-slot="root"]) {
73
+ --cui-track-highlight-transition-duration: var(--duration-base);
74
74
  --cui-track-highlight-radius: var(--cui-table-of-contents-highlight-radius);
75
75
  --cui-track-highlight-background: var(--cui-table-of-contents-highlight-background);
76
76
  --cui-track-highlight-ring-color: transparent;
@@ -4,7 +4,7 @@
4
4
  --_track-highlight-opacity: 0;
5
5
  border-radius: var(--cui-track-highlight-radius);
6
6
  opacity: var(--_track-highlight-opacity);
7
- transition-property: top, left, right, bottom, opacity, background-color, box-shadow;
7
+ transition-property: top, left, right, bottom, width, height, opacity, background-color, box-shadow;
8
8
  transition-duration: var(--cui-track-highlight-transition-duration);
9
9
  transition-timing-function: var(--ease-emphasized);
10
10
  }
@@ -22,7 +22,8 @@
22
22
  var(--cui-track-highlight-shadow);
23
23
  }
24
24
 
25
- :where([data-control-family="track-highlight"][data-slot="root"][data-hover]) {
25
+ :where([data-control-family="track-highlight"][data-slot="root"][data-hover]),
26
+ :where([data-track="hover"] > [data-control-family="track-highlight"][data-slot="root"]) {
26
27
  --cui-track-highlight-background: var(--cui-track-highlight-hover-background);
27
28
  --cui-track-highlight-ring-color: transparent;
28
29
  --cui-track-highlight-shadow: none;
@@ -36,6 +37,129 @@
36
37
  :where([data-control-family="track-highlight"][data-slot="hover"][data-visible]) {
37
38
  --_track-highlight-opacity: 1;
38
39
  }
40
+
41
+ :where(
42
+ [data-track="hover"]:is([data-control-family="button-group"], [data-control-family="button"][data-slot="group"])
43
+ > [data-control-family="track-highlight"]
44
+ ) {
45
+ --cui-track-highlight-hover-background: oklch(from var(--foreground) l c h / 0.06);
46
+ }
47
+
48
+ :where([data-track]:has(> [data-control-family="button"][data-track-item]) > [data-control-family="track-highlight"]),
49
+ :where(
50
+ [data-track]:is(
51
+ [data-control-family="button-group"],
52
+ [data-control-family="button"][data-slot="group"],
53
+ [data-control-family="checkbox-group"]
54
+ )
55
+ > [data-control-family="track-highlight"]
56
+ ) {
57
+ --cui-track-highlight-radius: var(--radius-control);
58
+ }
59
+
60
+ @supports ((anchor-name: --cui-track) and (anchor-scope: --cui-track)) {
61
+ :where([data-track]:has(> [data-control-family="track-highlight"])) {
62
+ anchor-scope: --cui-track-active, --cui-track-hover, --cui-track-focus;
63
+ --_track-anchor: --cui-track-active;
64
+ --_track-display: none;
65
+ }
66
+
67
+ :where(
68
+ [data-track="slide"]:has(> [data-control-family="track-highlight"]):has(
69
+ [data-track-item][data-active="true"]:not(:disabled, [aria-disabled="true"], [data-disabled])
70
+ )
71
+ ) {
72
+ --_track-display: block;
73
+ }
74
+
75
+ :where([data-track="hover"]:has(> [data-control-family="track-highlight"])) {
76
+ --_track-anchor: --cui-track-idle;
77
+ }
78
+
79
+ :where(
80
+ [data-track]:has(> [data-control-family="track-highlight"])
81
+ [data-track-item][data-active="true"]:not(:disabled, [aria-disabled="true"], [data-disabled])
82
+ ) {
83
+ anchor-name: --cui-track-active;
84
+ }
85
+
86
+ :where([data-track] > [data-control-family="track-highlight"][data-positioning="anchor"]:not([data-measured])) {
87
+ display: var(--_track-display);
88
+ position-anchor: var(--_track-anchor);
89
+ top: anchor(top, 0px);
90
+ width: anchor-size(width, 0px);
91
+ height: anchor-size(height, 0px);
92
+ left: anchor(left, 0px);
93
+ --_track-highlight-opacity: 1;
94
+
95
+ @starting-style {
96
+ opacity: 0;
97
+ }
98
+ }
99
+
100
+ @media (hover: hover) {
101
+ :where(
102
+ [data-track]:has(> [data-control-family="track-highlight"])
103
+ [data-track-item]:hover:not(:disabled, [aria-disabled="true"], [data-disabled], :has([role="checkbox"][data-disabled]))
104
+ ) {
105
+ anchor-name: --cui-track-hover;
106
+ }
107
+
108
+ :where(
109
+ [data-track]:has(> [data-control-family="track-highlight"]):has(
110
+ [data-track-item]:hover:not(:disabled, [aria-disabled="true"], [data-disabled])
111
+ ):not(:has([data-track-item]:hover [role="checkbox"][data-disabled])):not(:has([data-track] [data-track-item]:hover))
112
+ ) {
113
+ --_track-anchor: --cui-track-hover;
114
+ --_track-display: block;
115
+ }
116
+
117
+ :where(
118
+ [data-track="slide"]:has(
119
+ [data-track-item]:hover:not([data-active="true"], :disabled, [aria-disabled="true"], [data-disabled])
120
+ ):not(:has(:focus-visible, [data-track] [data-track-item]:hover))
121
+ > [data-control-family="track-highlight"][data-positioning="anchor"]:not([data-measured])
122
+ ) {
123
+ --cui-track-highlight-background: var(--cui-track-highlight-hover-background);
124
+ --cui-track-highlight-ring-color: transparent;
125
+ --cui-track-highlight-shadow: none;
126
+ }
127
+ }
128
+
129
+ :where(
130
+ [data-track]:has(> [data-control-family="track-highlight"])
131
+ [data-track-item]:is(:focus-visible, :has(:focus-visible)):not(
132
+ :disabled,
133
+ [aria-disabled="true"],
134
+ [data-disabled],
135
+ :has([role="checkbox"][data-disabled])
136
+ )
137
+ ) {
138
+ anchor-name: --cui-track-focus;
139
+ }
140
+
141
+ :where(
142
+ [data-track]:has(> [data-control-family="track-highlight"]):has(
143
+ [data-track-item]:focus-visible,
144
+ [data-track-item] :focus-visible
145
+ ):not(:has([data-track] :focus-visible))
146
+ ) {
147
+ --_track-anchor: --cui-track-focus;
148
+ --_track-display: block;
149
+ }
150
+
151
+ :where(
152
+ [data-track="slide"]:has(
153
+ [data-track-item]:not([data-active="true"]):focus-visible,
154
+ [data-track-item]:not([data-active="true"]) :focus-visible
155
+ ):not(:has([data-track] :focus-visible))
156
+ > [data-control-family="track-highlight"][data-positioning="anchor"]:not([data-measured])
157
+ ) {
158
+ --cui-track-highlight-background: var(--cui-track-highlight-hover-background);
159
+ --cui-track-highlight-ring-color: transparent;
160
+ --cui-track-highlight-shadow: none;
161
+ }
162
+ }
39
163
  }
40
164
 
41
165
  @property --cui-track-highlight-radius {
@@ -15,6 +15,10 @@
15
15
  outline: none;
16
16
  }
17
17
 
18
+ :where([data-control-family="tree"][data-slot="root"] > [data-control-family="track-highlight"]) {
19
+ --cui-track-highlight-radius: var(--cui-tree-item-trigger-radius);
20
+ }
21
+
18
22
  :where([data-control-family="tree"][data-slot="item-trigger"]) {
19
23
  gap: calc(var(--spacing) * 1.5);
20
24
  padding-block: calc(var(--spacing) * 1);
@@ -1,3 +1,4 @@
1
+ import { HoverIndicator } from "../control-props.js";
1
2
  import { ControlSize } from "../control-variants.js";
2
3
  import { ButtonGroupKnobStyle } from "../knob-contracts/button-group-knobs.js";
3
4
  import { CSSProperties, ComponentProps } from "react";
@@ -9,6 +10,7 @@ type ButtonGroupTextProps = Omit<ComponentProps<"div"> & {
9
10
  };
10
11
  type ButtonGroupProps = ComponentProps<"div"> & {
11
12
  orientation?: "horizontal" | "vertical";
13
+ indicator?: HoverIndicator;
12
14
  } & {
13
15
  style?: CSSProperties & ButtonGroupKnobStyle;
14
16
  };
@@ -17,7 +19,7 @@ type ButtonGroupSeparatorProps = Omit<ComponentProps<"div"> & {
17
19
  }, "style"> & {
18
20
  style?: CSSProperties & ButtonGroupKnobStyle;
19
21
  };
20
- declare function ButtonGroup({ orientation, className, ...props }: ButtonGroupProps): import("react").JSX.Element;
22
+ declare function ButtonGroup({ orientation, indicator, className, children, ...props }: ButtonGroupProps): import("react").JSX.Element;
21
23
  declare function ButtonGroupText({ size, className, ...props }: ButtonGroupTextProps): import("react").JSX.Element;
22
24
  declare function ButtonGroupSeparator({ orientation, className, ...props }: ButtonGroupSeparatorProps): import("react").JSX.Element;
23
25
  //#endregion
@@ -1,23 +1,26 @@
1
+ "use client";
1
2
  import { cn } from "../lib/cn.js";
2
- import { jsx } from "react/jsx-runtime";
3
- import { cva } from "class-variance-authority";
3
+ import { skinIndicator } from "../skin.js";
4
+ import { ButtonTrackContext, buttonTrackStructureClasses } from "./button.js";
5
+ import { TrackHighlight } from "../extensions/track-highlight.js";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
4
7
  //#region src/ui/button-group.tsx
5
- const buttonGroupVariant = cva("inline-flex w-fit items-stretch [&>*]:relative [&>*:focus-within:not([data-active=true])]:z-[1] [&>*[data-active=true]]:z-[2] [&>*[data-active=true]:focus-within]:z-[3]", {
6
- variants: { orientation: {
7
- horizontal: "flex-row [&>*:not(:first-child)]:-ml-px [&>*:not(:first-child)]:rounded-l-none [&>*:not(:last-child)]:rounded-r-none",
8
- vertical: "flex-col [&>*:not(:first-child)]:-mt-px [&>*:not(:first-child)]:rounded-t-none [&>*:not(:last-child)]:rounded-b-none"
9
- } },
10
- defaultVariants: { orientation: "horizontal" }
11
- });
12
- function ButtonGroup({ orientation = "horizontal", className, ...props }) {
13
- return /* @__PURE__ */ jsx("div", {
14
- role: "group",
15
- "data-control-ui": "button-group",
16
- "data-control-family": "button-group",
17
- "data-slot": "root",
18
- "data-orientation": orientation,
19
- className: cn(buttonGroupVariant({ orientation }), className),
20
- ...props
8
+ function ButtonGroup({ orientation = "horizontal", indicator, className, children, ...props }) {
9
+ const resolvedIndicator = indicator ?? skinIndicator("button-group") ?? "none";
10
+ const tracksHover = resolvedIndicator === "hover";
11
+ return /* @__PURE__ */ jsx(ButtonTrackContext, {
12
+ value: tracksHover,
13
+ children: /* @__PURE__ */ jsxs("div", {
14
+ role: "group",
15
+ "data-control-ui": "button-group",
16
+ "data-control-family": "button-group",
17
+ "data-slot": "root",
18
+ "data-orientation": orientation,
19
+ "data-track": resolvedIndicator,
20
+ className: cn(buttonTrackStructureClasses, "inline-flex w-fit items-stretch data-[orientation=vertical]:flex-col", className),
21
+ ...props,
22
+ children: [children, tracksHover ? /* @__PURE__ */ jsx(TrackHighlight, { className: "z-0" }) : null]
23
+ })
21
24
  });
22
25
  }
23
26
  function ButtonGroupText({ size = "sm", className, ...props }) {
@@ -6,6 +6,7 @@ import { CSSProperties, ComponentProps } from "react";
6
6
  type ButtonVariant = ControlVariant;
7
7
  type ButtonSize = ControlSize;
8
8
  type ButtonTone = ControlTone;
9
+ declare const ButtonTrackContext: import("react").Context<boolean>;
9
10
  declare const buttonShapes: readonly ["default", "circle"];
10
11
  type ButtonShape = (typeof buttonShapes)[number];
11
12
  type ButtonAppearanceProps = {
@@ -28,9 +29,10 @@ type ButtonLinkProps = ComponentProps<"a"> & ButtonAppearanceProps & {
28
29
  };
29
30
  type ButtonLabelProps = ComponentProps<"label"> & ButtonAppearanceProps;
30
31
  declare const buttonStructureClasses = "relative isolate inline-flex shrink-0 items-center justify-center overflow-visible whitespace-nowrap";
32
+ declare const buttonTrackStructureClasses = "data-[track=hover]:relative data-[track=hover]:isolate [&_[data-track-item]]:[isolation:auto]";
31
33
  declare const buttonContentClasses = "relative z-[1] inline-flex min-w-0 items-center justify-center gap-[inherit]";
32
34
  declare function Button({ variant, size, tone, active, iconOnly, shape, type, disabled, render, nativeButton, className, children, ...props }: ButtonProps): import("react").JSX.Element;
33
35
  declare function ButtonLink({ variant, size, tone, active, iconOnly, shape, render, className, children, ...props }: ButtonLinkProps): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
34
36
  declare function ButtonLabel({ variant, size, tone, active, iconOnly, shape, className, children, ...props }: ButtonLabelProps): import("react").JSX.Element;
35
37
  //#endregion
36
- export { Button, ButtonAppearanceProps, ButtonLabel, ButtonLabelProps, ButtonLink, ButtonLinkProps, ButtonProps, ButtonShape, ButtonSize, ButtonTone, ButtonVariant, buttonContentClasses, buttonShapes, buttonStructureClasses };
38
+ export { Button, ButtonAppearanceProps, ButtonLabel, ButtonLabelProps, ButtonLink, ButtonLinkProps, ButtonProps, ButtonShape, ButtonSize, ButtonTone, ButtonTrackContext, ButtonVariant, buttonContentClasses, buttonShapes, buttonStructureClasses, buttonTrackStructureClasses };
package/dist/ui/button.js CHANGED
@@ -1,12 +1,15 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/cn.js";
3
3
  import { skinAdornment } from "../skin.js";
4
+ import { createContext, useContext } from "react";
4
5
  import { Button as Button$1 } from "@base-ui/react/button";
5
6
  import { useRender } from "@base-ui/react/use-render";
6
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
7
8
  //#region src/ui/button.tsx
9
+ const ButtonTrackContext = createContext(false);
8
10
  const buttonShapes = ["default", "circle"];
9
11
  const buttonStructureClasses = "relative isolate inline-flex shrink-0 items-center justify-center overflow-visible whitespace-nowrap";
12
+ const buttonTrackStructureClasses = "data-[track=hover]:relative data-[track=hover]:isolate [&_[data-track-item]]:[isolation:auto]";
10
13
  const buttonContentClasses = "relative z-[1] inline-flex min-w-0 items-center justify-center gap-[inherit]";
11
14
  function ButtonContent({ children }) {
12
15
  return /* @__PURE__ */ jsx("span", {
@@ -17,15 +20,16 @@ function ButtonContent({ children }) {
17
20
  children
18
21
  });
19
22
  }
20
- /** The content wrapper only earns its node when a skin layer sits behind it and needs stacking above it. */
21
23
  function ButtonBody({ layer, wrap = true, children }) {
22
- return /* @__PURE__ */ jsxs(Fragment, { children: [layer, wrap && layer ? /* @__PURE__ */ jsx(ButtonContent, { children }) : children] });
24
+ const wrapsContent = useContext(ButtonTrackContext) || wrap && Boolean(layer);
25
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [layer, wrapsContent ? /* @__PURE__ */ jsx(ButtonContent, { children }) : children] });
23
26
  }
24
27
  function Button({ variant = "quiet", size = "sm", tone = "neutral", active = false, iconOnly = false, shape = "default", type = "button", disabled, render, nativeButton, className, children, ...props }) {
25
28
  const layer = skinAdornment("button", "layer", {
26
29
  variant,
27
30
  tone
28
31
  });
32
+ const tracksHover = useContext(ButtonTrackContext);
29
33
  return /* @__PURE__ */ jsx(Button$1, {
30
34
  type: nativeButton === false ? void 0 : type,
31
35
  disabled,
@@ -33,6 +37,7 @@ function Button({ variant = "quiet", size = "sm", tone = "neutral", active = fal
33
37
  "data-control-family": "button",
34
38
  "data-slot": "root",
35
39
  "data-control": "true",
40
+ "data-track-item": tracksHover ? "" : void 0,
36
41
  "data-active": active ? "true" : void 0,
37
42
  "data-icon-only": iconOnly ? "true" : void 0,
38
43
  "data-shape": shape,
@@ -51,10 +56,12 @@ function Button({ variant = "quiet", size = "sm", tone = "neutral", active = fal
51
56
  });
52
57
  }
53
58
  function ButtonLink({ variant = "quiet", size = "sm", tone = "neutral", active = false, iconOnly = false, shape = "default", render, className, children, ...props }) {
59
+ const tracksHover = useContext(ButtonTrackContext);
54
60
  return useRender({
55
61
  defaultTagName: "a",
56
62
  render,
57
63
  props: {
64
+ "data-track-item": tracksHover ? "" : void 0,
58
65
  ...props,
59
66
  "data-control-ui": "button",
60
67
  "data-control-family": "button",
@@ -78,7 +85,9 @@ function ButtonLink({ variant = "quiet", size = "sm", tone = "neutral", active =
78
85
  });
79
86
  }
80
87
  function ButtonLabel({ variant = "quiet", size = "sm", tone = "neutral", active = false, iconOnly = false, shape = "default", className, children, ...props }) {
88
+ const tracksHover = useContext(ButtonTrackContext);
81
89
  return /* @__PURE__ */ jsx("label", {
90
+ "data-track-item": tracksHover ? "" : void 0,
82
91
  ...props,
83
92
  "data-control-ui": "button",
84
93
  "data-control-family": "button",
@@ -101,4 +110,4 @@ function ButtonLabel({ variant = "quiet", size = "sm", tone = "neutral", active
101
110
  });
102
111
  }
103
112
  //#endregion
104
- export { Button, ButtonLabel, ButtonLink, buttonContentClasses, buttonShapes, buttonStructureClasses };
113
+ export { Button, ButtonLabel, ButtonLink, ButtonTrackContext, buttonContentClasses, buttonShapes, buttonStructureClasses, buttonTrackStructureClasses };
@@ -1,11 +1,13 @@
1
- import { ControlledMultiChoice } from "../control-props.js";
1
+ import { ControlledMultiChoice, HoverIndicator } from "../control-props.js";
2
2
  import { ComponentProps } from "react";
3
3
  //#region src/ui/checkbox-group.d.ts
4
4
  type CheckboxGroupProps = Omit<ComponentProps<"div">, "defaultValue" | "onChange"> & ControlledMultiChoice & {
5
5
  allValues?: string[];
6
6
  disabled?: boolean;
7
7
  orientation?: "horizontal" | "vertical";
8
+ indicator?: HoverIndicator;
8
9
  };
9
- declare function CheckboxGroup({ className, orientation, ...props }: CheckboxGroupProps): import("react").JSX.Element;
10
+ declare function CheckboxGroup({ className, orientation, indicator, children, ...props }: CheckboxGroupProps): import("react").JSX.Element;
11
+ declare function CheckboxGroupItem({ className, htmlFor, children, ...props }: ComponentProps<"label">): import("react").JSX.Element;
10
12
  //#endregion
11
- export { CheckboxGroup, CheckboxGroupProps };
13
+ export { CheckboxGroup, CheckboxGroupItem, CheckboxGroupProps };