@streamscloud/kit 0.86.0 → 0.87.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/dist/styles/_mixins.scss +71 -17
  2. package/dist/styles/reset.css +19 -12
  3. package/dist/ui/ai-panel/cmp.ai-panel.svelte +27 -0
  4. package/dist/ui/date-picker/cmp.date-time-picker.svelte +67 -8
  5. package/dist/ui/date-picker/cmp.date-time-picker.svelte.d.ts +8 -5
  6. package/dist/ui/date-picker/cmp.time-picker.svelte +3 -2
  7. package/dist/ui/date-picker/cmp.time-picker.svelte.d.ts +2 -0
  8. package/dist/ui/dialog/cmp.dialog-container.svelte +19 -12
  9. package/dist/ui/dialog/cmp.dialog.svelte +19 -12
  10. package/dist/ui/dialog/layout/cmp.stepper-dialog.svelte +54 -0
  11. package/dist/ui/emoji-picker/cmp.emoji-panel.svelte +19 -12
  12. package/dist/ui/file-uploader/cmp.upload-progress-toaster.svelte +20 -11
  13. package/dist/ui/grouped-sidebar/cmp.grouped-sidebar.svelte +20 -11
  14. package/dist/ui/navigation/cmp.nav-menu.svelte +20 -11
  15. package/dist/ui/page-layout/cmp.page-layout.svelte +38 -24
  16. package/dist/ui/page-layout/cmp.page-panel-sidebars.svelte +40 -22
  17. package/dist/ui/page-layout/cmp.page-panel.svelte +20 -11
  18. package/dist/ui/popover/cmp.popover.svelte +19 -12
  19. package/dist/ui/select/cmp.input-suggest.svelte +1 -0
  20. package/dist/ui/select/cmp.singleselect-async.svelte +1 -0
  21. package/dist/ui/select/multiselect-base.svelte +1 -0
  22. package/dist/ui/select/select-core.svelte.js +41 -11
  23. package/dist/ui/select/select-listbox.svelte +42 -19
  24. package/dist/ui/select/select-listbox.svelte.d.ts +5 -3
  25. package/dist/ui/table/table-core.svelte +19 -12
  26. package/dist/ui/textarea/cmp.textarea.svelte +20 -11
  27. package/package.json +1 -1
@@ -82,14 +82,24 @@
82
82
  max-height: 100%;
83
83
  }
84
84
 
85
- @mixin scrollbar($thumb-color: var(--scrollbar--thumb-color, light-dark(#d1d5db, #4b5563)), $track-color: var(--scrollbar--track-color, transparent)) {
85
+ @mixin scrollbar(
86
+ $thumb-color: var(--scrollbar--thumb-color, light-dark(#d1d5db, #4b5563)),
87
+ $track-color: var(--scrollbar--track-color, transparent),
88
+ $size: 6px,
89
+ $symmetric: null
90
+ ) {
86
91
  --_cross-browser-scrollbar--thumb-color: #{$thumb-color};
87
92
  --_cross-browser-scrollbar--track-color: #{$track-color};
88
93
 
89
- @include _cross-browser-scrollbar;
94
+ @include _cross-browser-scrollbar($size, $symmetric);
90
95
  }
91
96
 
92
- @mixin scrollbar-on-hover($thumb-color: var(--scrollbar--thumb-color, light-dark(#d1d5db, #4b5563)), $track-color: var(--scrollbar--track-color, transparent)) {
97
+ @mixin scrollbar-on-hover(
98
+ $thumb-color: var(--scrollbar--thumb-color, light-dark(#d1d5db, #4b5563)),
99
+ $track-color: var(--scrollbar--track-color, transparent),
100
+ $size: 6px,
101
+ $symmetric: null
102
+ ) {
93
103
  --_cross-browser-scrollbar--thumb-color: transparent;
94
104
  --_cross-browser-scrollbar--track-color: transparent;
95
105
 
@@ -98,29 +108,73 @@
98
108
  --_cross-browser-scrollbar--track-color: #{$track-color};
99
109
  }
100
110
 
101
- @include _cross-browser-scrollbar;
111
+ @include _cross-browser-scrollbar($size, $symmetric);
102
112
  }
103
113
 
104
- @mixin _cross-browser-scrollbar {
105
- &::-webkit-scrollbar {
106
- width: 6px;
107
- height: 6px;
108
- }
114
+ @mixin _cross-browser-scrollbar($size, $symmetric) {
115
+ --_cross-browser-scrollbar--gutter: 0px;
109
116
 
110
- &::-webkit-scrollbar-track {
111
- background: var(--_cross-browser-scrollbar--track-color);
112
- border-radius: 100vw;
113
- }
117
+ // a non-auto scrollbar-color / scrollbar-width (own or inherited) makes WebKit / Blink drop the 6px pseudo-element bar for the wider platform one
118
+ @include when-scrollbar-sizable {
119
+ scrollbar-color: auto;
120
+ scrollbar-width: auto;
121
+
122
+ &::-webkit-scrollbar {
123
+ width: $size;
124
+ height: $size;
125
+ }
126
+
127
+ &::-webkit-scrollbar-track {
128
+ background: var(--_cross-browser-scrollbar--track-color);
129
+ border-radius: 100vw;
130
+ }
114
131
 
115
- &::-webkit-scrollbar-thumb {
116
- background: var(--_cross-browser-scrollbar--thumb-color);
117
- border-radius: 100vw;
132
+ &::-webkit-scrollbar-thumb {
133
+ background: var(--_cross-browser-scrollbar--thumb-color);
134
+ border-radius: 100vw;
135
+ }
118
136
  }
119
137
 
120
- @supports (scrollbar-color: transparent transparent) {
138
+ @include when-scrollbar-unsizable {
121
139
  scrollbar-color: var(--_cross-browser-scrollbar--thumb-color) var(--_cross-browser-scrollbar--track-color);
122
140
  scrollbar-width: thin;
123
141
  }
142
+
143
+ // not scrollbar-gutter: both-edges here — Safari reserves both gutters on the end side
144
+ @if $symmetric {
145
+ @include when-scrollbar-sizable {
146
+ --_cross-browser-scrollbar--gutter: #{$size};
147
+
148
+ overflow-y: scroll;
149
+ }
150
+ }
151
+
152
+ @if $symmetric == all {
153
+ @include when-scrollbar-unsizable {
154
+ scrollbar-gutter: stable both-edges;
155
+ }
156
+ }
157
+ }
158
+
159
+ @mixin when-scrollbar-sizable {
160
+ @supports selector(::-webkit-scrollbar) {
161
+ @content;
162
+ }
163
+ }
164
+
165
+ // room to reserve for the platform scrollbar where its width can't be set (~11px thin bar + 1px); never subtract it — overlay scrollbars take 0
166
+ $unsizable-scrollbar-allowance: 12px;
167
+
168
+ // no ::-webkit-scrollbar (Firefox): the platform bar can't shrink into a panel padding, so a box sized for the 4–6px bar clips its content unless it adds the allowance here
169
+ @mixin when-scrollbar-unsizable {
170
+ @supports not selector(::-webkit-scrollbar) {
171
+ @content;
172
+ }
173
+ }
174
+
175
+ @mixin scrollbar-padding-inline($padding) {
176
+ padding-inline-start: max(#{$padding}, var(--_cross-browser-scrollbar--gutter, 0px));
177
+ padding-inline-end: max(0px, calc(#{$padding} - var(--_cross-browser-scrollbar--gutter, 0px)));
124
178
  }
125
179
 
126
180
  @mixin fade-in-animation($duration: 5s) {
@@ -123,20 +123,27 @@ textarea {
123
123
  resize: vertical;
124
124
  --_cross-browser-scrollbar--thumb-color: var(--scrollbar--thumb-color, light-dark(#d1d5db, #4b5563));
125
125
  --_cross-browser-scrollbar--track-color: var(--scrollbar--track-color, transparent);
126
+ --_cross-browser-scrollbar--gutter: 0px;
126
127
  }
127
- textarea::-webkit-scrollbar {
128
- width: 6px;
129
- height: 6px;
130
- }
131
- textarea::-webkit-scrollbar-track {
132
- background: var(--_cross-browser-scrollbar--track-color);
133
- border-radius: 100vw;
134
- }
135
- textarea::-webkit-scrollbar-thumb {
136
- background: var(--_cross-browser-scrollbar--thumb-color);
137
- border-radius: 100vw;
128
+ @supports selector(::-webkit-scrollbar) {
129
+ textarea {
130
+ scrollbar-color: auto;
131
+ scrollbar-width: auto;
132
+ }
133
+ textarea::-webkit-scrollbar {
134
+ width: 6px;
135
+ height: 6px;
136
+ }
137
+ textarea::-webkit-scrollbar-track {
138
+ background: var(--_cross-browser-scrollbar--track-color);
139
+ border-radius: 100vw;
140
+ }
141
+ textarea::-webkit-scrollbar-thumb {
142
+ background: var(--_cross-browser-scrollbar--thumb-color);
143
+ border-radius: 100vw;
144
+ }
138
145
  }
139
- @supports (scrollbar-color: transparent transparent) {
146
+ @supports not selector(::-webkit-scrollbar) {
140
147
  textarea {
141
148
  scrollbar-color: var(--_cross-browser-scrollbar--thumb-color) var(--_cross-browser-scrollbar--track-color);
142
149
  scrollbar-width: thin;
@@ -69,6 +69,33 @@ layout's concern. User-facing copy (placeholder, Generate label) is consumer-sup
69
69
  background: var(--_ai-panel--surface);
70
70
  border-radius: var(--_ai-panel--surface-radius);
71
71
  overflow-y: auto;
72
+ --_cross-browser-scrollbar--thumb-color: var(--scrollbar--thumb-color, light-dark(#d1d5db, #4b5563));
73
+ --_cross-browser-scrollbar--track-color: var(--scrollbar--track-color, transparent);
74
+ --_cross-browser-scrollbar--gutter: 0px;
75
+ }
76
+ @supports selector(::-webkit-scrollbar) {
77
+ .ai-panel__body {
78
+ scrollbar-color: auto;
79
+ scrollbar-width: auto;
80
+ }
81
+ .ai-panel__body::-webkit-scrollbar {
82
+ width: 6px;
83
+ height: 6px;
84
+ }
85
+ .ai-panel__body::-webkit-scrollbar-track {
86
+ background: var(--_cross-browser-scrollbar--track-color);
87
+ border-radius: 100vw;
88
+ }
89
+ .ai-panel__body::-webkit-scrollbar-thumb {
90
+ background: var(--_cross-browser-scrollbar--thumb-color);
91
+ border-radius: 100vw;
92
+ }
93
+ }
94
+ @supports not selector(::-webkit-scrollbar) {
95
+ .ai-panel__body {
96
+ scrollbar-color: var(--_cross-browser-scrollbar--thumb-color) var(--_cross-browser-scrollbar--track-color);
97
+ scrollbar-width: thin;
98
+ }
72
99
  }
73
100
  .ai-panel__composer {
74
101
  position: relative;
@@ -2,10 +2,47 @@
2
2
  import TimePicker from './cmp.time-picker.svelte';
3
3
  const { value, size = 'md', datePlaceholder = '', min, step = 30, on } = $props();
4
4
  const DEFAULT_TIME = '12:00';
5
+ const MINUTES_PER_DAY = 24 * 60;
6
+ const AFTER_LAST_SLOT = '24:00';
5
7
  const pad = (n) => n.toString().padStart(2, '0');
6
8
  const toTimeValue = (date) => `${pad(date.getHours())}:${pad(date.getMinutes())}`;
9
+ const minutesToTimeValue = (minutes) => `${pad(Math.floor(minutes / 60))}:${pad(minutes % 60)}`;
10
+ const isSameDay = (a, b) => a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
7
11
  const currentDate = $derived(value ? new Date(value) : null);
8
12
  const timeValue = $derived(currentDate ? toTimeValue(currentDate) : DEFAULT_TIME);
13
+ const minDate = $derived.by(() => {
14
+ if (min === undefined) {
15
+ return null;
16
+ }
17
+ const date = new Date(min);
18
+ return Number.isNaN(date.getTime()) ? null : date;
19
+ });
20
+ const minSlotMinutes = $derived.by(() => {
21
+ if (!minDate) {
22
+ return null;
23
+ }
24
+ const elapsed = minDate.getHours() * 60 + minDate.getMinutes() + minDate.getSeconds() / 60 + minDate.getMilliseconds() / 60_000;
25
+ const slot = Math.ceil(elapsed / step) * step;
26
+ return slot < MINUTES_PER_DAY ? slot : null;
27
+ });
28
+ const firstAllowedDate = $derived.by(() => {
29
+ if (!minDate) {
30
+ return null;
31
+ }
32
+ if (minSlotMinutes === null) {
33
+ return new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate() + 1);
34
+ }
35
+ return new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate(), Math.floor(minSlotMinutes / 60), minSlotMinutes % 60);
36
+ });
37
+ const timeMin = $derived.by(() => {
38
+ if (!currentDate || !firstAllowedDate) {
39
+ return undefined;
40
+ }
41
+ if (isSameDay(currentDate, firstAllowedDate)) {
42
+ return minutesToTimeValue(firstAllowedDate.getHours() * 60 + firstAllowedDate.getMinutes());
43
+ }
44
+ return currentDate < firstAllowedDate ? AFTER_LAST_SLOT : undefined;
45
+ });
9
46
  const onDateChange = (next) => {
10
47
  if (!next) {
11
48
  on?.change?.(null);
@@ -18,6 +55,9 @@ const onDateChange = (next) => {
18
55
  combined.setHours(hours, minutes, 0, 0);
19
56
  }
20
57
  combined.setFullYear(picked.getFullYear(), picked.getMonth(), picked.getDate());
58
+ if (minDate && minSlotMinutes !== null && combined < minDate) {
59
+ combined.setHours(Math.floor(minSlotMinutes / 60), minSlotMinutes % 60, 0, 0);
60
+ }
21
61
  on?.change?.(combined.toISOString());
22
62
  };
23
63
  const onTimeChange = (next) => {
@@ -31,35 +71,54 @@ const onTimeChange = (next) => {
31
71
  };
32
72
  </script>
33
73
 
34
- <div class="date-time" class:date-time--date-only={!currentDate}>
35
- <DatePicker clearable size={size} value={value} placeholder={datePlaceholder} min={min} on={{ change: onDateChange }} />
74
+ <div class="date-time date-time--{size}" class:date-time--date-only={!currentDate}>
75
+ <DatePicker clearable size={size} value={value} placeholder={datePlaceholder} min={firstAllowedDate ?? undefined} on={{ change: onDateChange }} />
36
76
  {#if currentDate}
37
- <TimePicker clearable={false} size={size} step={step} value={timeValue} on={{ change: onTimeChange }} />
77
+ <TimePicker clearable={false} size={size} step={step} min={timeMin} value={timeValue} on={{ change: onTimeChange }} />
38
78
  {/if}
39
79
  </div>
40
80
 
41
81
  <!--
42
82
  @component
43
- DateTimePicker — composes `DatePicker` + `TimePicker` into one ISO date-time field. The time row appears
44
- only once a date is picked (defaulting to `12:00`); `size` and `step` thread down to both controls.
45
- Emits an ISO 8601 string — or `null` when the date is cleared — via `on.change`.
83
+ DateTimePicker — composes `DatePicker` + `TimePicker` into one ISO 8601 date-time field whose time row appears
84
+ once a date is picked (defaulting to `12:00`). Emits the ISO string — or `null` when the date is cleared — via `on.change`.
46
85
 
47
86
  ### CSS Custom Properties
48
87
  | Property | Description | Default |
49
88
  |---|---|---|
50
- | `--sc-kit--date-time-picker--time-width` | Width of the time column | `6rem` |
89
+ | `--sc-kit--date-time-picker--time-width` | Width of the time column | per size — `sm` 5.75rem, `md` 6.25rem, `lg` 6.75rem (+0.75rem without `::-webkit-scrollbar`, e.g. Firefox) |
51
90
  | `--sc-kit--date-time-picker--gap` | Gap between the date and time fields | `var(--sc-kit--space--2)` |
52
91
  -->
53
92
 
54
93
  <style>.date-time {
55
- --_dtp--time-width: var(--sc-kit--date-time-picker--time-width, 6rem);
94
+ --_dtp--scrollbar-allowance: 0px;
95
+ --_dtp--time-width: var(
96
+ --sc-kit--date-time-picker--time-width,
97
+ calc(var(--_dtp--size-time-width, 6.25rem) + var(--_dtp--scrollbar-allowance))
98
+ );
56
99
  --_dtp--gap: var(--sc-kit--date-time-picker--gap, var(--sc-kit--space--2));
100
+ }
101
+ @supports not selector(::-webkit-scrollbar) {
102
+ .date-time {
103
+ --_dtp--scrollbar-allowance: 12px;
104
+ }
105
+ }
106
+ .date-time {
57
107
  display: grid;
58
108
  grid-template-columns: minmax(0, 1fr) var(--_dtp--time-width);
59
109
  gap: var(--_dtp--gap);
60
110
  width: 100%;
61
111
  max-width: 100%;
62
112
  }
113
+ .date-time--sm {
114
+ --_dtp--size-time-width: 5.75rem;
115
+ }
116
+ .date-time--md {
117
+ --_dtp--size-time-width: 6.25rem;
118
+ }
119
+ .date-time--lg {
120
+ --_dtp--size-time-width: 6.75rem;
121
+ }
63
122
  .date-time--date-only {
64
123
  grid-template-columns: minmax(0, 1fr);
65
124
  }</style>
@@ -4,7 +4,11 @@ type Props = {
4
4
  /** @default 'md' */
5
5
  size?: 'sm' | 'md' | 'lg';
6
6
  datePlaceholder?: string;
7
- /** Earliest selectable date (inclusive). */
7
+ /**
8
+ * Earliest selectable date-time (inclusive). On its day only the `step` slots at or after it are offered and an
9
+ * earlier time is raised to the first of them; a day with no slot left is not selectable. A `value` before the first
10
+ * allowed day still renders, but its time list is empty — only picking an allowed date changes it.
11
+ */
8
12
  min?: LikeDate;
9
13
  /** Minute interval for the time list. @default 30 */
10
14
  step?: number;
@@ -13,14 +17,13 @@ type Props = {
13
17
  };
14
18
  };
15
19
  /**
16
- * DateTimePicker — composes `DatePicker` + `TimePicker` into one ISO date-time field. The time row appears
17
- * only once a date is picked (defaulting to `12:00`); `size` and `step` thread down to both controls.
18
- * Emits an ISO 8601 string — or `null` when the date is cleared — via `on.change`.
20
+ * DateTimePicker — composes `DatePicker` + `TimePicker` into one ISO 8601 date-time field whose time row appears
21
+ * once a date is picked (defaulting to `12:00`). Emits the ISO string — or `null` when the date is cleared — via `on.change`.
19
22
  *
20
23
  * ### CSS Custom Properties
21
24
  * | Property | Description | Default |
22
25
  * |---|---|---|
23
- * | `--sc-kit--date-time-picker--time-width` | Width of the time column | `6rem` |
26
+ * | `--sc-kit--date-time-picker--time-width` | Width of the time column | per size — `sm` 5.75rem, `md` 6.25rem, `lg` 6.75rem (+0.75rem without `::-webkit-scrollbar`, e.g. Firefox) |
24
27
  * | `--sc-kit--date-time-picker--gap` | Gap between the date and time fields | `var(--sc-kit--space--2)` |
25
28
  */
26
29
  declare const Cmp: import("svelte").Component<Props, {}, "">;
@@ -1,14 +1,15 @@
1
1
  <script lang="ts">import { Singleselect } from '../select';
2
- const { size = 'md', step = 30, placeholder = '', disabled = false, readonly = false, inert = false, error = false, borderless = false, id, name, 'aria-label': ariaLabel, 'aria-describedby': ariaDescribedby, 'aria-required': ariaRequired, ...mode } = $props();
2
+ const { size = 'md', step = 30, min, placeholder = '', disabled = false, readonly = false, inert = false, error = false, borderless = false, id, name, 'aria-label': ariaLabel, 'aria-describedby': ariaDescribedby, 'aria-required': ariaRequired, ...mode } = $props();
3
3
  const MINUTES_PER_DAY = 24 * 60;
4
4
  const pad = (n) => n.toString().padStart(2, '0');
5
5
  const stepOptions = $derived.by(() => {
6
6
  const count = Math.max(1, Math.floor(MINUTES_PER_DAY / step));
7
- return Array.from({ length: count }, (_, i) => {
7
+ const grid = Array.from({ length: count }, (_, i) => {
8
8
  const total = i * step;
9
9
  const label = `${pad(Math.floor(total / 60))}:${pad(total % 60)}`;
10
10
  return { label, value: label };
11
11
  });
12
+ return min ? grid.filter((option) => option.value >= min) : grid;
12
13
  });
13
14
  const options = $derived.by(() => {
14
15
  const current = mode.value;
@@ -3,6 +3,8 @@ type BaseProps = {
3
3
  size?: 'sm' | 'md' | 'lg';
4
4
  /** Minute interval between generated options. @default 30 */
5
5
  step?: number;
6
+ /** Earliest selectable time (`'HH:mm'`, inclusive) — earlier grid slots are hidden; an earlier `value` still renders. */
7
+ min?: string;
6
8
  placeholder?: string;
7
9
  disabled?: boolean;
8
10
  /** Focusable, value visible, but selection cannot change. */
@@ -140,20 +140,27 @@ DialogContainer — wraps the native `<dialog>` element with sizing, positioning
140
140
  padding: var(--_dialog-container--padding);
141
141
  --_cross-browser-scrollbar--thumb-color: var(--sc-kit--color--border--strong);
142
142
  --_cross-browser-scrollbar--track-color: var(--scrollbar--track-color, transparent);
143
+ --_cross-browser-scrollbar--gutter: 0px;
143
144
  }
144
- .dialog-container::-webkit-scrollbar {
145
- width: 6px;
146
- height: 6px;
147
- }
148
- .dialog-container::-webkit-scrollbar-track {
149
- background: var(--_cross-browser-scrollbar--track-color);
150
- border-radius: 100vw;
151
- }
152
- .dialog-container::-webkit-scrollbar-thumb {
153
- background: var(--_cross-browser-scrollbar--thumb-color);
154
- border-radius: 100vw;
145
+ @supports selector(::-webkit-scrollbar) {
146
+ .dialog-container {
147
+ scrollbar-color: auto;
148
+ scrollbar-width: auto;
149
+ }
150
+ .dialog-container::-webkit-scrollbar {
151
+ width: 6px;
152
+ height: 6px;
153
+ }
154
+ .dialog-container::-webkit-scrollbar-track {
155
+ background: var(--_cross-browser-scrollbar--track-color);
156
+ border-radius: 100vw;
157
+ }
158
+ .dialog-container::-webkit-scrollbar-thumb {
159
+ background: var(--_cross-browser-scrollbar--thumb-color);
160
+ border-radius: 100vw;
161
+ }
155
162
  }
156
- @supports (scrollbar-color: transparent transparent) {
163
+ @supports not selector(::-webkit-scrollbar) {
157
164
  .dialog-container {
158
165
  scrollbar-color: var(--_cross-browser-scrollbar--thumb-color) var(--_cross-browser-scrollbar--track-color);
159
166
  scrollbar-width: thin;
@@ -136,20 +136,27 @@ A structured dialog panel with header (title + close button), scrollable body, a
136
136
  padding: var(--_--dialog--body--padding-block) var(--_--dialog--body--padding-inline);
137
137
  --_cross-browser-scrollbar--thumb-color: var(--scrollbar--thumb-color, light-dark(#d1d5db, #4b5563));
138
138
  --_cross-browser-scrollbar--track-color: var(--scrollbar--track-color, transparent);
139
+ --_cross-browser-scrollbar--gutter: 0px;
139
140
  }
140
- .dialog__body::-webkit-scrollbar {
141
- width: 6px;
142
- height: 6px;
143
- }
144
- .dialog__body::-webkit-scrollbar-track {
145
- background: var(--_cross-browser-scrollbar--track-color);
146
- border-radius: 100vw;
147
- }
148
- .dialog__body::-webkit-scrollbar-thumb {
149
- background: var(--_cross-browser-scrollbar--thumb-color);
150
- border-radius: 100vw;
141
+ @supports selector(::-webkit-scrollbar) {
142
+ .dialog__body {
143
+ scrollbar-color: auto;
144
+ scrollbar-width: auto;
145
+ }
146
+ .dialog__body::-webkit-scrollbar {
147
+ width: 6px;
148
+ height: 6px;
149
+ }
150
+ .dialog__body::-webkit-scrollbar-track {
151
+ background: var(--_cross-browser-scrollbar--track-color);
152
+ border-radius: 100vw;
153
+ }
154
+ .dialog__body::-webkit-scrollbar-thumb {
155
+ background: var(--_cross-browser-scrollbar--thumb-color);
156
+ border-radius: 100vw;
157
+ }
151
158
  }
152
- @supports (scrollbar-color: transparent transparent) {
159
+ @supports not selector(::-webkit-scrollbar) {
153
160
  .dialog__body {
154
161
  scrollbar-color: var(--_cross-browser-scrollbar--thumb-color) var(--_cross-browser-scrollbar--track-color);
155
162
  scrollbar-width: thin;
@@ -174,6 +174,33 @@ Header, body and sidebar block padding follow `Dialog`'s header / body padding v
174
174
  border-inline-end: 1px solid var(--sc-kit--color--border);
175
175
  overflow-y: auto;
176
176
  min-block-size: 0;
177
+ --_cross-browser-scrollbar--thumb-color: var(--scrollbar--thumb-color, light-dark(#d1d5db, #4b5563));
178
+ --_cross-browser-scrollbar--track-color: var(--scrollbar--track-color, transparent);
179
+ --_cross-browser-scrollbar--gutter: 0px;
180
+ }
181
+ @supports selector(::-webkit-scrollbar) {
182
+ .stepper-dialog__sidebar {
183
+ scrollbar-color: auto;
184
+ scrollbar-width: auto;
185
+ }
186
+ .stepper-dialog__sidebar::-webkit-scrollbar {
187
+ width: 6px;
188
+ height: 6px;
189
+ }
190
+ .stepper-dialog__sidebar::-webkit-scrollbar-track {
191
+ background: var(--_cross-browser-scrollbar--track-color);
192
+ border-radius: 100vw;
193
+ }
194
+ .stepper-dialog__sidebar::-webkit-scrollbar-thumb {
195
+ background: var(--_cross-browser-scrollbar--thumb-color);
196
+ border-radius: 100vw;
197
+ }
198
+ }
199
+ @supports not selector(::-webkit-scrollbar) {
200
+ .stepper-dialog__sidebar {
201
+ scrollbar-color: var(--_cross-browser-scrollbar--thumb-color) var(--_cross-browser-scrollbar--track-color);
202
+ scrollbar-width: thin;
203
+ }
177
204
  }
178
205
  @media screen and (max-width: 768px) {
179
206
  .stepper-dialog__sidebar {
@@ -194,4 +221,31 @@ Header, body and sidebar block padding follow `Dialog`'s header / body padding v
194
221
  min-block-size: 0;
195
222
  overflow-y: auto;
196
223
  padding: var(--_--dialog--body--padding-block) var(--_--dialog--body--padding-inline);
224
+ --_cross-browser-scrollbar--thumb-color: var(--scrollbar--thumb-color, light-dark(#d1d5db, #4b5563));
225
+ --_cross-browser-scrollbar--track-color: var(--scrollbar--track-color, transparent);
226
+ --_cross-browser-scrollbar--gutter: 0px;
227
+ }
228
+ @supports selector(::-webkit-scrollbar) {
229
+ .stepper-dialog__body {
230
+ scrollbar-color: auto;
231
+ scrollbar-width: auto;
232
+ }
233
+ .stepper-dialog__body::-webkit-scrollbar {
234
+ width: 6px;
235
+ height: 6px;
236
+ }
237
+ .stepper-dialog__body::-webkit-scrollbar-track {
238
+ background: var(--_cross-browser-scrollbar--track-color);
239
+ border-radius: 100vw;
240
+ }
241
+ .stepper-dialog__body::-webkit-scrollbar-thumb {
242
+ background: var(--_cross-browser-scrollbar--thumb-color);
243
+ border-radius: 100vw;
244
+ }
245
+ }
246
+ @supports not selector(::-webkit-scrollbar) {
247
+ .stepper-dialog__body {
248
+ scrollbar-color: var(--_cross-browser-scrollbar--thumb-color) var(--_cross-browser-scrollbar--track-color);
249
+ scrollbar-width: thin;
250
+ }
197
251
  }</style>
@@ -148,20 +148,27 @@ can be embedded directly by input components.
148
148
  overflow-y: auto;
149
149
  --_cross-browser-scrollbar--thumb-color: var(--scrollbar--thumb-color, light-dark(#d1d5db, #4b5563));
150
150
  --_cross-browser-scrollbar--track-color: var(--scrollbar--track-color, transparent);
151
+ --_cross-browser-scrollbar--gutter: 0px;
151
152
  }
152
- .emoji-panel__categories::-webkit-scrollbar {
153
- width: 6px;
154
- height: 6px;
155
- }
156
- .emoji-panel__categories::-webkit-scrollbar-track {
157
- background: var(--_cross-browser-scrollbar--track-color);
158
- border-radius: 100vw;
159
- }
160
- .emoji-panel__categories::-webkit-scrollbar-thumb {
161
- background: var(--_cross-browser-scrollbar--thumb-color);
162
- border-radius: 100vw;
153
+ @supports selector(::-webkit-scrollbar) {
154
+ .emoji-panel__categories {
155
+ scrollbar-color: auto;
156
+ scrollbar-width: auto;
157
+ }
158
+ .emoji-panel__categories::-webkit-scrollbar {
159
+ width: 6px;
160
+ height: 6px;
161
+ }
162
+ .emoji-panel__categories::-webkit-scrollbar-track {
163
+ background: var(--_cross-browser-scrollbar--track-color);
164
+ border-radius: 100vw;
165
+ }
166
+ .emoji-panel__categories::-webkit-scrollbar-thumb {
167
+ background: var(--_cross-browser-scrollbar--thumb-color);
168
+ border-radius: 100vw;
169
+ }
163
170
  }
164
- @supports (scrollbar-color: transparent transparent) {
171
+ @supports not selector(::-webkit-scrollbar) {
165
172
  .emoji-panel__categories {
166
173
  scrollbar-color: var(--_cross-browser-scrollbar--thumb-color) var(--_cross-browser-scrollbar--track-color);
167
174
  scrollbar-width: thin;
@@ -112,19 +112,28 @@ at the store's default concurrency of 20 can never push the toast off screen.
112
112
  --_cross-browser-scrollbar--thumb-color: var(--scrollbar--thumb-color, light-dark(#d1d5db, #4b5563));
113
113
  --_cross-browser-scrollbar--track-color: var(--scrollbar--track-color, transparent);
114
114
  }
115
- .upload-progress-toaster__files::-webkit-scrollbar {
116
- width: 6px;
117
- height: 6px;
118
- }
119
- .upload-progress-toaster__files::-webkit-scrollbar-track {
120
- background: var(--_cross-browser-scrollbar--track-color);
121
- border-radius: 100vw;
115
+ .upload-progress-toaster__files {
116
+ --_cross-browser-scrollbar--gutter: 0px;
122
117
  }
123
- .upload-progress-toaster__files::-webkit-scrollbar-thumb {
124
- background: var(--_cross-browser-scrollbar--thumb-color);
125
- border-radius: 100vw;
118
+ @supports selector(::-webkit-scrollbar) {
119
+ .upload-progress-toaster__files {
120
+ scrollbar-color: auto;
121
+ scrollbar-width: auto;
122
+ }
123
+ .upload-progress-toaster__files::-webkit-scrollbar {
124
+ width: 6px;
125
+ height: 6px;
126
+ }
127
+ .upload-progress-toaster__files::-webkit-scrollbar-track {
128
+ background: var(--_cross-browser-scrollbar--track-color);
129
+ border-radius: 100vw;
130
+ }
131
+ .upload-progress-toaster__files::-webkit-scrollbar-thumb {
132
+ background: var(--_cross-browser-scrollbar--thumb-color);
133
+ border-radius: 100vw;
134
+ }
126
135
  }
127
- @supports (scrollbar-color: transparent transparent) {
136
+ @supports not selector(::-webkit-scrollbar) {
128
137
  .upload-progress-toaster__files {
129
138
  scrollbar-color: var(--_cross-browser-scrollbar--thumb-color) var(--_cross-browser-scrollbar--track-color);
130
139
  scrollbar-width: thin;
@@ -111,19 +111,28 @@ dummy-placeholder + post-init bind needed:
111
111
  --_cross-browser-scrollbar--thumb-color: var(--scrollbar--thumb-color, light-dark(#d1d5db, #4b5563));
112
112
  --_cross-browser-scrollbar--track-color: var(--scrollbar--track-color, transparent);
113
113
  }
114
- .grouped-sidebar__sections::-webkit-scrollbar {
115
- width: 6px;
116
- height: 6px;
117
- }
118
- .grouped-sidebar__sections::-webkit-scrollbar-track {
119
- background: var(--_cross-browser-scrollbar--track-color);
120
- border-radius: 100vw;
114
+ .grouped-sidebar__sections {
115
+ --_cross-browser-scrollbar--gutter: 0px;
121
116
  }
122
- .grouped-sidebar__sections::-webkit-scrollbar-thumb {
123
- background: var(--_cross-browser-scrollbar--thumb-color);
124
- border-radius: 100vw;
117
+ @supports selector(::-webkit-scrollbar) {
118
+ .grouped-sidebar__sections {
119
+ scrollbar-color: auto;
120
+ scrollbar-width: auto;
121
+ }
122
+ .grouped-sidebar__sections::-webkit-scrollbar {
123
+ width: 6px;
124
+ height: 6px;
125
+ }
126
+ .grouped-sidebar__sections::-webkit-scrollbar-track {
127
+ background: var(--_cross-browser-scrollbar--track-color);
128
+ border-radius: 100vw;
129
+ }
130
+ .grouped-sidebar__sections::-webkit-scrollbar-thumb {
131
+ background: var(--_cross-browser-scrollbar--thumb-color);
132
+ border-radius: 100vw;
133
+ }
125
134
  }
126
- @supports (scrollbar-color: transparent transparent) {
135
+ @supports not selector(::-webkit-scrollbar) {
127
136
  .grouped-sidebar__sections {
128
137
  scrollbar-color: var(--_cross-browser-scrollbar--thumb-color) var(--_cross-browser-scrollbar--track-color);
129
138
  scrollbar-width: thin;
@@ -70,19 +70,28 @@ routing, or feature gating. Compose with `NavMenuGroup`, `NavMenuHeader`, `NavMe
70
70
  --_cross-browser-scrollbar--thumb-color: var(--sc-kit--color--border--strong);
71
71
  --_cross-browser-scrollbar--track-color: var(--scrollbar--track-color, transparent);
72
72
  }
73
- .nav-menu__body::-webkit-scrollbar {
74
- width: 6px;
75
- height: 6px;
76
- }
77
- .nav-menu__body::-webkit-scrollbar-track {
78
- background: var(--_cross-browser-scrollbar--track-color);
79
- border-radius: 100vw;
73
+ .nav-menu__body {
74
+ --_cross-browser-scrollbar--gutter: 0px;
80
75
  }
81
- .nav-menu__body::-webkit-scrollbar-thumb {
82
- background: var(--_cross-browser-scrollbar--thumb-color);
83
- border-radius: 100vw;
76
+ @supports selector(::-webkit-scrollbar) {
77
+ .nav-menu__body {
78
+ scrollbar-color: auto;
79
+ scrollbar-width: auto;
80
+ }
81
+ .nav-menu__body::-webkit-scrollbar {
82
+ width: 6px;
83
+ height: 6px;
84
+ }
85
+ .nav-menu__body::-webkit-scrollbar-track {
86
+ background: var(--_cross-browser-scrollbar--track-color);
87
+ border-radius: 100vw;
88
+ }
89
+ .nav-menu__body::-webkit-scrollbar-thumb {
90
+ background: var(--_cross-browser-scrollbar--thumb-color);
91
+ border-radius: 100vw;
92
+ }
84
93
  }
85
- @supports (scrollbar-color: transparent transparent) {
94
+ @supports not selector(::-webkit-scrollbar) {
86
95
  .nav-menu__body {
87
96
  scrollbar-color: var(--_cross-browser-scrollbar--thumb-color) var(--_cross-browser-scrollbar--track-color);
88
97
  scrollbar-width: thin;
@@ -135,20 +135,27 @@ A loading `PagePanel` inside a loading layout draws its own spinner and overlay
135
135
  overflow-y: var(--_page-layout--body-overflow, auto);
136
136
  --_cross-browser-scrollbar--thumb-color: var(--sc-kit--color--border);
137
137
  --_cross-browser-scrollbar--track-color: var(--scrollbar--track-color, transparent);
138
+ --_cross-browser-scrollbar--gutter: 0px;
138
139
  }
139
- .page-layout__body::-webkit-scrollbar {
140
- width: 6px;
141
- height: 6px;
142
- }
143
- .page-layout__body::-webkit-scrollbar-track {
144
- background: var(--_cross-browser-scrollbar--track-color);
145
- border-radius: 100vw;
146
- }
147
- .page-layout__body::-webkit-scrollbar-thumb {
148
- background: var(--_cross-browser-scrollbar--thumb-color);
149
- border-radius: 100vw;
140
+ @supports selector(::-webkit-scrollbar) {
141
+ .page-layout__body {
142
+ scrollbar-color: auto;
143
+ scrollbar-width: auto;
144
+ }
145
+ .page-layout__body::-webkit-scrollbar {
146
+ width: 6px;
147
+ height: 6px;
148
+ }
149
+ .page-layout__body::-webkit-scrollbar-track {
150
+ background: var(--_cross-browser-scrollbar--track-color);
151
+ border-radius: 100vw;
152
+ }
153
+ .page-layout__body::-webkit-scrollbar-thumb {
154
+ background: var(--_cross-browser-scrollbar--thumb-color);
155
+ border-radius: 100vw;
156
+ }
150
157
  }
151
- @supports (scrollbar-color: transparent transparent) {
158
+ @supports not selector(::-webkit-scrollbar) {
152
159
  .page-layout__body {
153
160
  scrollbar-color: var(--_cross-browser-scrollbar--thumb-color) var(--_cross-browser-scrollbar--track-color);
154
161
  scrollbar-width: thin;
@@ -158,20 +165,27 @@ A loading `PagePanel` inside a loading layout draws its own spinner and overlay
158
165
  overflow-y: auto;
159
166
  --_cross-browser-scrollbar--thumb-color: var(--sc-kit--color--border);
160
167
  --_cross-browser-scrollbar--track-color: var(--scrollbar--track-color, transparent);
168
+ --_cross-browser-scrollbar--gutter: 0px;
161
169
  }
162
- .page-layout--scrollable::-webkit-scrollbar {
163
- width: 6px;
164
- height: 6px;
165
- }
166
- .page-layout--scrollable::-webkit-scrollbar-track {
167
- background: var(--_cross-browser-scrollbar--track-color);
168
- border-radius: 100vw;
169
- }
170
- .page-layout--scrollable::-webkit-scrollbar-thumb {
171
- background: var(--_cross-browser-scrollbar--thumb-color);
172
- border-radius: 100vw;
170
+ @supports selector(::-webkit-scrollbar) {
171
+ .page-layout--scrollable {
172
+ scrollbar-color: auto;
173
+ scrollbar-width: auto;
174
+ }
175
+ .page-layout--scrollable::-webkit-scrollbar {
176
+ width: 6px;
177
+ height: 6px;
178
+ }
179
+ .page-layout--scrollable::-webkit-scrollbar-track {
180
+ background: var(--_cross-browser-scrollbar--track-color);
181
+ border-radius: 100vw;
182
+ }
183
+ .page-layout--scrollable::-webkit-scrollbar-thumb {
184
+ background: var(--_cross-browser-scrollbar--thumb-color);
185
+ border-radius: 100vw;
186
+ }
173
187
  }
174
- @supports (scrollbar-color: transparent transparent) {
188
+ @supports not selector(::-webkit-scrollbar) {
175
189
  .page-layout--scrollable {
176
190
  scrollbar-color: var(--_cross-browser-scrollbar--thumb-color) var(--_cross-browser-scrollbar--track-color);
177
191
  scrollbar-width: thin;
@@ -145,19 +145,28 @@ line instead of doubling it. The collapse button stays put outside that track.
145
145
  --_cross-browser-scrollbar--thumb-color: var(--sc-kit--color--border--strong);
146
146
  --_cross-browser-scrollbar--track-color: var(--scrollbar--track-color, transparent);
147
147
  }
148
- .page-panel-sidebars__sidebar::-webkit-scrollbar {
149
- width: 6px;
150
- height: 6px;
151
- }
152
- .page-panel-sidebars__sidebar::-webkit-scrollbar-track {
153
- background: var(--_cross-browser-scrollbar--track-color);
154
- border-radius: 100vw;
148
+ .page-panel-sidebars__sidebar {
149
+ --_cross-browser-scrollbar--gutter: 0px;
155
150
  }
156
- .page-panel-sidebars__sidebar::-webkit-scrollbar-thumb {
157
- background: var(--_cross-browser-scrollbar--thumb-color);
158
- border-radius: 100vw;
151
+ @supports selector(::-webkit-scrollbar) {
152
+ .page-panel-sidebars__sidebar {
153
+ scrollbar-color: auto;
154
+ scrollbar-width: auto;
155
+ }
156
+ .page-panel-sidebars__sidebar::-webkit-scrollbar {
157
+ width: 6px;
158
+ height: 6px;
159
+ }
160
+ .page-panel-sidebars__sidebar::-webkit-scrollbar-track {
161
+ background: var(--_cross-browser-scrollbar--track-color);
162
+ border-radius: 100vw;
163
+ }
164
+ .page-panel-sidebars__sidebar::-webkit-scrollbar-thumb {
165
+ background: var(--_cross-browser-scrollbar--thumb-color);
166
+ border-radius: 100vw;
167
+ }
159
168
  }
160
- @supports (scrollbar-color: transparent transparent) {
169
+ @supports not selector(::-webkit-scrollbar) {
161
170
  .page-panel-sidebars__sidebar {
162
171
  scrollbar-color: var(--_cross-browser-scrollbar--thumb-color) var(--_cross-browser-scrollbar--track-color);
163
172
  scrollbar-width: thin;
@@ -181,19 +190,28 @@ line instead of doubling it. The collapse button stays put outside that track.
181
190
  --_cross-browser-scrollbar--thumb-color: var(--sc-kit--color--border--strong);
182
191
  --_cross-browser-scrollbar--track-color: var(--scrollbar--track-color, transparent);
183
192
  }
184
- .page-panel-sidebars__main::-webkit-scrollbar {
185
- width: 6px;
186
- height: 6px;
187
- }
188
- .page-panel-sidebars__main::-webkit-scrollbar-track {
189
- background: var(--_cross-browser-scrollbar--track-color);
190
- border-radius: 100vw;
193
+ .page-panel-sidebars__main {
194
+ --_cross-browser-scrollbar--gutter: 0px;
191
195
  }
192
- .page-panel-sidebars__main::-webkit-scrollbar-thumb {
193
- background: var(--_cross-browser-scrollbar--thumb-color);
194
- border-radius: 100vw;
196
+ @supports selector(::-webkit-scrollbar) {
197
+ .page-panel-sidebars__main {
198
+ scrollbar-color: auto;
199
+ scrollbar-width: auto;
200
+ }
201
+ .page-panel-sidebars__main::-webkit-scrollbar {
202
+ width: 6px;
203
+ height: 6px;
204
+ }
205
+ .page-panel-sidebars__main::-webkit-scrollbar-track {
206
+ background: var(--_cross-browser-scrollbar--track-color);
207
+ border-radius: 100vw;
208
+ }
209
+ .page-panel-sidebars__main::-webkit-scrollbar-thumb {
210
+ background: var(--_cross-browser-scrollbar--thumb-color);
211
+ border-radius: 100vw;
212
+ }
195
213
  }
196
- @supports (scrollbar-color: transparent transparent) {
214
+ @supports not selector(::-webkit-scrollbar) {
197
215
  .page-panel-sidebars__main {
198
216
  scrollbar-color: var(--_cross-browser-scrollbar--thumb-color) var(--_cross-browser-scrollbar--track-color);
199
217
  scrollbar-width: thin;
@@ -469,19 +469,28 @@ to min / max; Enter / Space toggle collapsed.
469
469
  --_cross-browser-scrollbar--thumb-color: var(--sc-kit--color--border--strong);
470
470
  --_cross-browser-scrollbar--track-color: var(--scrollbar--track-color, transparent);
471
471
  }
472
- .page-panel__content::-webkit-scrollbar {
473
- width: 6px;
474
- height: 6px;
475
- }
476
- .page-panel__content::-webkit-scrollbar-track {
477
- background: var(--_cross-browser-scrollbar--track-color);
478
- border-radius: 100vw;
472
+ .page-panel__content {
473
+ --_cross-browser-scrollbar--gutter: 0px;
479
474
  }
480
- .page-panel__content::-webkit-scrollbar-thumb {
481
- background: var(--_cross-browser-scrollbar--thumb-color);
482
- border-radius: 100vw;
475
+ @supports selector(::-webkit-scrollbar) {
476
+ .page-panel__content {
477
+ scrollbar-color: auto;
478
+ scrollbar-width: auto;
479
+ }
480
+ .page-panel__content::-webkit-scrollbar {
481
+ width: 6px;
482
+ height: 6px;
483
+ }
484
+ .page-panel__content::-webkit-scrollbar-track {
485
+ background: var(--_cross-browser-scrollbar--track-color);
486
+ border-radius: 100vw;
487
+ }
488
+ .page-panel__content::-webkit-scrollbar-thumb {
489
+ background: var(--_cross-browser-scrollbar--thumb-color);
490
+ border-radius: 100vw;
491
+ }
483
492
  }
484
- @supports (scrollbar-color: transparent transparent) {
493
+ @supports not selector(::-webkit-scrollbar) {
485
494
  .page-panel__content {
486
495
  scrollbar-color: var(--_cross-browser-scrollbar--thumb-color) var(--_cross-browser-scrollbar--track-color);
487
496
  scrollbar-width: thin;
@@ -399,20 +399,27 @@ Props for keeping the popover open during interaction:
399
399
  overflow: var(--_dd--content-overflow);
400
400
  --_cross-browser-scrollbar--thumb-color: var(--scrollbar--thumb-color, light-dark(#d1d5db, #4b5563));
401
401
  --_cross-browser-scrollbar--track-color: var(--scrollbar--track-color, transparent);
402
+ --_cross-browser-scrollbar--gutter: 0px;
402
403
  }
403
- .popover__panel--styled::-webkit-scrollbar {
404
- width: 6px;
405
- height: 6px;
406
- }
407
- .popover__panel--styled::-webkit-scrollbar-track {
408
- background: var(--_cross-browser-scrollbar--track-color);
409
- border-radius: 100vw;
410
- }
411
- .popover__panel--styled::-webkit-scrollbar-thumb {
412
- background: var(--_cross-browser-scrollbar--thumb-color);
413
- border-radius: 100vw;
404
+ @supports selector(::-webkit-scrollbar) {
405
+ .popover__panel--styled {
406
+ scrollbar-color: auto;
407
+ scrollbar-width: auto;
408
+ }
409
+ .popover__panel--styled::-webkit-scrollbar {
410
+ width: 6px;
411
+ height: 6px;
412
+ }
413
+ .popover__panel--styled::-webkit-scrollbar-track {
414
+ background: var(--_cross-browser-scrollbar--track-color);
415
+ border-radius: 100vw;
416
+ }
417
+ .popover__panel--styled::-webkit-scrollbar-thumb {
418
+ background: var(--_cross-browser-scrollbar--thumb-color);
419
+ border-radius: 100vw;
420
+ }
414
421
  }
415
- @supports (scrollbar-color: transparent transparent) {
422
+ @supports not selector(::-webkit-scrollbar) {
416
423
  .popover__panel--styled {
417
424
  scrollbar-color: var(--_cross-browser-scrollbar--thumb-color) var(--_cross-browser-scrollbar--track-color);
418
425
  scrollbar-width: thin;
@@ -261,6 +261,7 @@ const handlePickRow = (row) => {
261
261
  <SelectListbox
262
262
  triggerEl={rootEl}
263
263
  isOpen={isPanelOpen}
264
+ size={size}
264
265
  rows={rows}
265
266
  highlight={activeIndex}
266
267
  listboxId={listboxId}
@@ -136,6 +136,7 @@ const showClear = $derived(mode.clearable && hasValue && isInteractive && !core.
136
136
  <SelectListbox
137
137
  triggerEl={rootEl}
138
138
  isOpen={core.isOpen}
139
+ size={size}
139
140
  rows={core.rows}
140
141
  highlight={core.highlight}
141
142
  loading={core.loading}
@@ -257,6 +257,7 @@ const handleDndFinalize = (e) => {
257
257
  <SelectListbox
258
258
  triggerEl={rootEl}
259
259
  isOpen={core.isOpen}
260
+ size={size}
260
261
  rows={core.rows}
261
262
  highlight={core.highlight}
262
263
  loading={core.loading}
@@ -11,6 +11,7 @@ export function createSelectCore(config) {
11
11
  let isOpen = $state(false);
12
12
  let query = $state('');
13
13
  let highlight = $state(0);
14
+ let highlightMovedByUser = false;
14
15
  let isFiltering = $state(false);
15
16
  let items = $state.raw([]);
16
17
  let loadToken = 0;
@@ -152,13 +153,28 @@ export function createSelectCore(config) {
152
153
  return out;
153
154
  });
154
155
  const navigableIndices = $derived(getNavigableIndices(rows));
155
- const openPopover = () => {
156
- if (!config.getInteractive() || isOpen) {
157
- return;
156
+ const findSelectedRowIndex = () => {
157
+ const selected = config.getSelectedOptions()[0];
158
+ if (!selected) {
159
+ return navigableIndices[0] ?? 0;
158
160
  }
159
- isOpen = true;
160
- query = '';
161
- isFiltering = false;
161
+ const compare = config.getCompare();
162
+ const idx = rows.findIndex((r) => r.kind === 'option' && compare(r.option.value, selected.value));
163
+ return idx >= 0 ? idx : (navigableIndices[0] ?? 0);
164
+ };
165
+ const moveHighlightByUser = (index) => {
166
+ highlight = index;
167
+ highlightMovedByUser = true;
168
+ };
169
+ const loadAndHighlightSelected = async () => {
170
+ highlight = findSelectedRowIndex();
171
+ highlightMovedByUser = false;
172
+ await runLoad('');
173
+ if (isOpen && !query && !highlightMovedByUser) {
174
+ highlight = findSelectedRowIndex();
175
+ }
176
+ };
177
+ const highlightFirstSelectedAndLoad = () => {
162
178
  const selected = config.getSelectedOptions()[0];
163
179
  if (selected) {
164
180
  const idx = rows.findIndex((r) => r.kind === 'option' && r.option === selected);
@@ -168,6 +184,20 @@ export function createSelectCore(config) {
168
184
  highlight = navigableIndices[0] ?? 0;
169
185
  }
170
186
  void runLoad('');
187
+ };
188
+ const openPopover = () => {
189
+ if (!config.getInteractive() || isOpen) {
190
+ return;
191
+ }
192
+ isOpen = true;
193
+ query = '';
194
+ isFiltering = false;
195
+ if (config.picker === 'single') {
196
+ void loadAndHighlightSelected();
197
+ }
198
+ else {
199
+ highlightFirstSelectedAndLoad();
200
+ }
171
201
  // Clicks land on the trigger root (div role=presentation) — focus the inner input so the
172
202
  // user can start typing immediately without a second click.
173
203
  config.getInputEl()?.focus();
@@ -292,7 +322,7 @@ export function createSelectCore(config) {
292
322
  if (!isOpen) {
293
323
  isOpen = true;
294
324
  }
295
- highlight = navigableIndices[0] ?? 0;
325
+ moveHighlightByUser(navigableIndices[0] ?? 0);
296
326
  debouncedLoad(query);
297
327
  };
298
328
  const handleKeydown = (e) => {
@@ -308,11 +338,11 @@ export function createSelectCore(config) {
308
338
  }
309
339
  if (e.key === 'ArrowDown') {
310
340
  e.preventDefault();
311
- highlight = moveHighlightIndex(navigableIndices, highlight, 1);
341
+ moveHighlightByUser(moveHighlightIndex(navigableIndices, highlight, 1));
312
342
  }
313
343
  else if (e.key === 'ArrowUp') {
314
344
  e.preventDefault();
315
- highlight = moveHighlightIndex(navigableIndices, highlight, -1);
345
+ moveHighlightByUser(moveHighlightIndex(navigableIndices, highlight, -1));
316
346
  }
317
347
  else if (e.key === 'Enter') {
318
348
  e.preventDefault();
@@ -337,7 +367,7 @@ export function createSelectCore(config) {
337
367
  e.preventDefault();
338
368
  isFiltering = true;
339
369
  query = e.key;
340
- highlight = navigableIndices[0] ?? 0;
370
+ moveHighlightByUser(navigableIndices[0] ?? 0);
341
371
  debouncedLoad(query);
342
372
  }
343
373
  };
@@ -381,7 +411,7 @@ export function createSelectCore(config) {
381
411
  closePopover,
382
412
  pickRow,
383
413
  setHighlight(i) {
384
- highlight = i;
414
+ moveHighlightByUser(i);
385
415
  },
386
416
  refresh() {
387
417
  if (isOpen) {
@@ -6,7 +6,7 @@ import IconCheckboxChecked from '@fluentui/svg-icons/icons/checkbox_checked_16_f
6
6
  import IconCheckboxIndeterminate from '@fluentui/svg-icons/icons/checkbox_indeterminate_16_filled.svg?raw';
7
7
  import IconCheckboxUnchecked from '@fluentui/svg-icons/icons/checkbox_unchecked_16_regular.svg?raw';
8
8
  import IconCheckmark from '@fluentui/svg-icons/icons/checkmark_16_regular.svg?raw';
9
- let { triggerEl, isOpen, rows, highlight, loading = false, listboxId, matchTriggerWidth = true, selectedDisplay = 'checkmark', optionSnippet, headerSnippet, bodySnippet, hideCreateRows = false, createPrefix, suppressEmpty = false, on } = $props();
9
+ let { triggerEl, isOpen, rows, highlight, loading = false, size = 'md', listboxId, matchTriggerWidth = true, selectedDisplay = 'checkmark', optionSnippet, headerSnippet, bodySnippet, hideCreateRows = false, createPrefix, suppressEmpty = false, on } = $props();
10
10
  const localization = new SelectListboxLocalization();
11
11
  // rows keep their index into the full `rows` array — `highlight` and row ids are core-side indices,
12
12
  // so filtering without preserving them shifts every row by one and Enter acts on the wrong row
@@ -115,7 +115,7 @@ $effect(() => {
115
115
  {#if isOpen && (hasRows || showEmpty)}
116
116
  <div
117
117
  bind:this={panelEl}
118
- class="select-listbox select-listbox--display-{selectedDisplay}"
118
+ class="select-listbox select-listbox--{size} select-listbox--display-{selectedDisplay}"
119
119
  role="listbox"
120
120
  id={listboxId}
121
121
  tabindex="-1"
@@ -237,10 +237,10 @@ dismiss events upward. Floating UI handles placement with autoUpdate on scroll/r
237
237
  | `--sc-kit--select--panel--border-color` | Panel border | `var(--sc-kit--color--border)` |
238
238
  | `--sc-kit--select--panel--border-radius` | Panel radius | `var(--sc-kit--radius--md)` |
239
239
  | `--sc-kit--select--panel--box-shadow` | Panel shadow | `var(--sc-kit--shadow--lg)` |
240
- | `--sc-kit--select--panel--font-size` | Option / group / empty-state text size | `var(--sc-kit--font-size--md)` |
240
+ | `--sc-kit--select--panel--font-size` | Option / group / empty-state text size | per size — `var(--sc-kit--font-size--{sm,md,lg})` |
241
241
  | `--sc-kit--select--panel--max-height` | Panel max height | `18rem` |
242
- | `--sc-kit--select--panel--padding` | Panel inner padding | `var(--sc-kit--space--1)` |
243
- | `--sc-kit--select--option--height` | Option row height | `var(--sc-kit--field--height--md)` (32px) |
242
+ | `--sc-kit--select--panel--padding` | Panel inner padding; in Chrome / Safari the 4px scrollbar takes its share of the end side | `var(--sc-kit--space--1)` |
243
+ | `--sc-kit--select--option--height` | Option row height | per size — `var(--sc-kit--field--height--{sm,md,lg})` |
244
244
  | `--sc-kit--select--option--padding-inline` | Option horizontal padding | `var(--sc-kit--space--3)` |
245
245
  | `--sc-kit--select--option--background--active` | Active / focused row background | `var(--sc-kit--color--accent--softer)` |
246
246
  | `--sc-kit--select--option--color` | Row text color | `var(--sc-kit--color--text--primary)` |
@@ -257,8 +257,8 @@ dismiss events upward. Floating UI handles placement with autoUpdate on scroll/r
257
257
  --_lb--box-shadow: var(--sc-kit--select--panel--box-shadow, var(--sc-kit--shadow--lg));
258
258
  --_lb--max-height: var(--sc-kit--select--panel--max-height, 18rem);
259
259
  --_lb--padding: var(--sc-kit--select--panel--padding, var(--sc-kit--space--1));
260
- --_lb--font-size: var(--sc-kit--select--panel--font-size, var(--sc-kit--font-size--md));
261
- --_lb--row-height: var(--sc-kit--select--option--height, var(--sc-kit--field--height--md));
260
+ --_lb--font-size: var(--sc-kit--select--panel--font-size, var(--_lb--size-font-size, var(--sc-kit--font-size--md)));
261
+ --_lb--row-height: var(--sc-kit--select--option--height, var(--_lb--size-row-height, var(--sc-kit--field--height--md)));
262
262
  --_lb--row-padding-inline: var(--sc-kit--select--option--padding-inline, var(--sc-kit--space--3));
263
263
  --_lb--row-background-active: var(--sc-kit--select--option--background--active, var(--sc-kit--color--accent--softer));
264
264
  --_lb--row-color: var(--sc-kit--select--option--color, var(--sc-kit--color--text--primary));
@@ -278,28 +278,51 @@ dismiss events upward. Floating UI handles placement with autoUpdate on scroll/r
278
278
  box-shadow: var(--_lb--box-shadow);
279
279
  max-height: var(--_lb--max-height);
280
280
  padding: var(--_lb--padding);
281
+ padding-inline-start: max(var(--_lb--padding), var(--_cross-browser-scrollbar--gutter, 0px));
282
+ padding-inline-end: max(0px, var(--_lb--padding) - var(--_cross-browser-scrollbar--gutter, 0px));
281
283
  overflow-y: auto;
282
284
  --_cross-browser-scrollbar--thumb-color: var(--scrollbar--thumb-color, light-dark(#d1d5db, #4b5563));
283
285
  --_cross-browser-scrollbar--track-color: var(--scrollbar--track-color, transparent);
286
+ --_cross-browser-scrollbar--gutter: 0px;
284
287
  }
285
- .select-listbox::-webkit-scrollbar {
286
- width: 6px;
287
- height: 6px;
288
- }
289
- .select-listbox::-webkit-scrollbar-track {
290
- background: var(--_cross-browser-scrollbar--track-color);
291
- border-radius: 100vw;
292
- }
293
- .select-listbox::-webkit-scrollbar-thumb {
294
- background: var(--_cross-browser-scrollbar--thumb-color);
295
- border-radius: 100vw;
288
+ @supports selector(::-webkit-scrollbar) {
289
+ .select-listbox {
290
+ scrollbar-color: auto;
291
+ scrollbar-width: auto;
292
+ }
293
+ .select-listbox::-webkit-scrollbar {
294
+ width: 4px;
295
+ height: 4px;
296
+ }
297
+ .select-listbox::-webkit-scrollbar-track {
298
+ background: var(--_cross-browser-scrollbar--track-color);
299
+ border-radius: 100vw;
300
+ }
301
+ .select-listbox::-webkit-scrollbar-thumb {
302
+ background: var(--_cross-browser-scrollbar--thumb-color);
303
+ border-radius: 100vw;
304
+ }
296
305
  }
297
- @supports (scrollbar-color: transparent transparent) {
306
+ @supports not selector(::-webkit-scrollbar) {
298
307
  .select-listbox {
299
308
  scrollbar-color: var(--_cross-browser-scrollbar--thumb-color) var(--_cross-browser-scrollbar--track-color);
300
309
  scrollbar-width: thin;
301
310
  }
302
311
  }
312
+ @supports selector(::-webkit-scrollbar) {
313
+ .select-listbox {
314
+ --_cross-browser-scrollbar--gutter: 4px;
315
+ overflow-y: scroll;
316
+ }
317
+ }
318
+ .select-listbox--sm {
319
+ --_lb--size-font-size: var(--sc-kit--font-size--sm);
320
+ --_lb--size-row-height: var(--sc-kit--field--height--sm);
321
+ }
322
+ .select-listbox--lg {
323
+ --_lb--size-font-size: var(--sc-kit--font-size--lg);
324
+ --_lb--size-row-height: var(--sc-kit--field--height--lg);
325
+ }
303
326
  .select-listbox__row {
304
327
  display: flex;
305
328
  align-items: center;
@@ -9,6 +9,8 @@ declare function $$render<T>(): {
9
9
  /** Index into `rows` — highlighted row gets the active background and scrolls into view. */
10
10
  highlight: number;
11
11
  loading?: boolean;
12
+ /** Field size the rows follow (font size + row height). @default 'md' */
13
+ size?: "sm" | "md" | "lg";
12
14
  /** Stable id for aria-controls + aria-activedescendant. */
13
15
  listboxId: string;
14
16
  /** When true, panel width equals trigger width. @default true */
@@ -79,10 +81,10 @@ interface $$IsomorphicComponent {
79
81
  * | `--sc-kit--select--panel--border-color` | Panel border | `var(--sc-kit--color--border)` |
80
82
  * | `--sc-kit--select--panel--border-radius` | Panel radius | `var(--sc-kit--radius--md)` |
81
83
  * | `--sc-kit--select--panel--box-shadow` | Panel shadow | `var(--sc-kit--shadow--lg)` |
82
- * | `--sc-kit--select--panel--font-size` | Option / group / empty-state text size | `var(--sc-kit--font-size--md)` |
84
+ * | `--sc-kit--select--panel--font-size` | Option / group / empty-state text size | per size — `var(--sc-kit--font-size--{sm,md,lg})` |
83
85
  * | `--sc-kit--select--panel--max-height` | Panel max height | `18rem` |
84
- * | `--sc-kit--select--panel--padding` | Panel inner padding | `var(--sc-kit--space--1)` |
85
- * | `--sc-kit--select--option--height` | Option row height | `var(--sc-kit--field--height--md)` (32px) |
86
+ * | `--sc-kit--select--panel--padding` | Panel inner padding; in Chrome / Safari the 4px scrollbar takes its share of the end side | `var(--sc-kit--space--1)` |
87
+ * | `--sc-kit--select--option--height` | Option row height | per size — `var(--sc-kit--field--height--{sm,md,lg})` |
86
88
  * | `--sc-kit--select--option--padding-inline` | Option horizontal padding | `var(--sc-kit--space--3)` |
87
89
  * | `--sc-kit--select--option--background--active` | Active / focused row background | `var(--sc-kit--color--accent--softer)` |
88
90
  * | `--sc-kit--select--option--color` | Row text color | `var(--sc-kit--color--text--primary)` |
@@ -290,20 +290,27 @@ Not exported from `index.ts` — for kit-internal use only.
290
290
  overflow: auto;
291
291
  --_cross-browser-scrollbar--thumb-color: var(--scrollbar--thumb-color, light-dark(#d1d5db, #4b5563));
292
292
  --_cross-browser-scrollbar--track-color: var(--scrollbar--track-color, transparent);
293
+ --_cross-browser-scrollbar--gutter: 0px;
293
294
  }
294
- .table-container__scroll::-webkit-scrollbar {
295
- width: 6px;
296
- height: 6px;
297
- }
298
- .table-container__scroll::-webkit-scrollbar-track {
299
- background: var(--_cross-browser-scrollbar--track-color);
300
- border-radius: 100vw;
301
- }
302
- .table-container__scroll::-webkit-scrollbar-thumb {
303
- background: var(--_cross-browser-scrollbar--thumb-color);
304
- border-radius: 100vw;
295
+ @supports selector(::-webkit-scrollbar) {
296
+ .table-container__scroll {
297
+ scrollbar-color: auto;
298
+ scrollbar-width: auto;
299
+ }
300
+ .table-container__scroll::-webkit-scrollbar {
301
+ width: 6px;
302
+ height: 6px;
303
+ }
304
+ .table-container__scroll::-webkit-scrollbar-track {
305
+ background: var(--_cross-browser-scrollbar--track-color);
306
+ border-radius: 100vw;
307
+ }
308
+ .table-container__scroll::-webkit-scrollbar-thumb {
309
+ background: var(--_cross-browser-scrollbar--thumb-color);
310
+ border-radius: 100vw;
311
+ }
305
312
  }
306
- @supports (scrollbar-color: transparent transparent) {
313
+ @supports not selector(::-webkit-scrollbar) {
307
314
  .table-container__scroll {
308
315
  scrollbar-color: var(--_cross-browser-scrollbar--thumb-color) var(--_cross-browser-scrollbar--track-color);
309
316
  scrollbar-width: thin;
@@ -285,19 +285,28 @@ what the field shows. Blank lines inside the text are left alone. Typing is unto
285
285
  --_cross-browser-scrollbar--thumb-color: var(--sc-kit--color--border--strong);
286
286
  --_cross-browser-scrollbar--track-color: var(--scrollbar--track-color, transparent);
287
287
  }
288
- .textarea__el::-webkit-scrollbar {
289
- width: 6px;
290
- height: 6px;
291
- }
292
- .textarea__el::-webkit-scrollbar-track {
293
- background: var(--_cross-browser-scrollbar--track-color);
294
- border-radius: 100vw;
288
+ .textarea__el {
289
+ --_cross-browser-scrollbar--gutter: 0px;
295
290
  }
296
- .textarea__el::-webkit-scrollbar-thumb {
297
- background: var(--_cross-browser-scrollbar--thumb-color);
298
- border-radius: 100vw;
291
+ @supports selector(::-webkit-scrollbar) {
292
+ .textarea__el {
293
+ scrollbar-color: auto;
294
+ scrollbar-width: auto;
295
+ }
296
+ .textarea__el::-webkit-scrollbar {
297
+ width: 6px;
298
+ height: 6px;
299
+ }
300
+ .textarea__el::-webkit-scrollbar-track {
301
+ background: var(--_cross-browser-scrollbar--track-color);
302
+ border-radius: 100vw;
303
+ }
304
+ .textarea__el::-webkit-scrollbar-thumb {
305
+ background: var(--_cross-browser-scrollbar--thumb-color);
306
+ border-radius: 100vw;
307
+ }
299
308
  }
300
- @supports (scrollbar-color: transparent transparent) {
309
+ @supports not selector(::-webkit-scrollbar) {
301
310
  .textarea__el {
302
311
  scrollbar-color: var(--_cross-browser-scrollbar--thumb-color) var(--_cross-browser-scrollbar--track-color);
303
312
  scrollbar-width: thin;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@streamscloud/kit",
3
- "version": "0.86.0",
3
+ "version": "0.87.0",
4
4
  "author": "StreamsCloud",
5
5
  "repository": {
6
6
  "type": "git",