@expressivecss/expressive 0.4.0 → 0.5.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 (68) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/README.md +4 -9
  3. package/dist/css/expressive.css +2309 -948
  4. package/dist/css/expressive.css.map +1 -1
  5. package/dist/css/expressive.min.css +1 -1
  6. package/dist/css/expressive.min.css.map +1 -1
  7. package/dist/js/expressive.cjs +643 -804
  8. package/dist/js/expressive.cjs.map +4 -4
  9. package/dist/js/expressive.js +643 -804
  10. package/dist/js/expressive.js.map +4 -4
  11. package/dist/js/expressive.min.js +11 -11
  12. package/dist/js/expressive.min.js.map +4 -4
  13. package/dist/js/expressive.mjs +643 -804
  14. package/dist/js/expressive.mjs.map +4 -4
  15. package/dist/types/behaviors/bottomSheets.d.ts +18 -0
  16. package/dist/types/behaviors/sideSheets.d.ts +18 -0
  17. package/dist/types/components/autocomplete.d.ts +11 -11
  18. package/dist/types/components/index.d.ts +3 -5
  19. package/dist/types/components/{dropdown.d.ts → menu.d.ts} +38 -37
  20. package/dist/types/components/navigationRail.d.ts +53 -0
  21. package/dist/types/components/range.d.ts +3 -0
  22. package/dist/types/components/registry.d.ts +7 -15
  23. package/dist/types/components/select.d.ts +12 -12
  24. package/dist/types/components/{toasts.d.ts → snackbar.d.ts} +28 -25
  25. package/dist/types/index.d.ts +3 -1
  26. package/package.json +3 -2
  27. package/src/sass/README.md +13 -11
  28. package/src/sass/abstracts/_breakpoints.scss +14 -3
  29. package/src/sass/abstracts/_mixins.scss +3 -3
  30. package/src/sass/abstracts/_variables.scss +27 -2
  31. package/src/sass/base/_global.scss +1 -1
  32. package/src/sass/base/_grid.scss +66 -116
  33. package/src/sass/base/_index.scss +1 -1
  34. package/src/sass/components/_badges.scss +76 -61
  35. package/src/sass/components/_bottom-sheet.scss +137 -0
  36. package/src/sass/components/_cards.scss +4 -4
  37. package/src/sass/components/_chips.scss +6 -5
  38. package/src/sass/components/_dialog.scss +268 -0
  39. package/src/sass/components/_index.scss +8 -7
  40. package/src/sass/components/_list.scss +253 -0
  41. package/src/sass/components/_menu.scss +341 -0
  42. package/src/sass/components/_navbar.scss +65 -117
  43. package/src/sass/components/_navigation-bar.scss +165 -0
  44. package/src/sass/components/_navigation-rail.scss +308 -0
  45. package/src/sass/components/_page-footer.scss +7 -3
  46. package/src/sass/components/_side-sheet.scss +193 -0
  47. package/src/sass/components/_sidenav.scss +78 -49
  48. package/src/sass/components/{_toast.scss → _snackbar.scss} +35 -34
  49. package/src/sass/components/_tabs.scss +1 -27
  50. package/src/sass/components/_toolbar.scss +91 -25
  51. package/src/sass/components/_tooltip.scss +1 -1
  52. package/src/sass/components/forms/_fieldset.scss +1 -1
  53. package/src/sass/components/forms/_input-fields.scss +3 -3
  54. package/src/sass/components/forms/_range.scss +423 -98
  55. package/src/sass/components/forms/_select.scss +19 -17
  56. package/src/sass/tokens/_reference.scss +37 -13
  57. package/src/sass/tokens/_theme.scss +5 -13
  58. package/src/sass/utilities/_colors.scss +16 -74
  59. package/src/sass/utilities/_visibility.scss +8 -0
  60. package/dist/types/components/collapsible.d.ts +0 -79
  61. package/dist/types/components/pushpin.d.ts +0 -51
  62. package/dist/types/components/tapTarget.d.ts +0 -57
  63. package/src/sass/components/_collapsible.scss +0 -179
  64. package/src/sass/components/_collection.scss +0 -180
  65. package/src/sass/components/_dropdown.scss +0 -225
  66. package/src/sass/components/_modal.scss +0 -190
  67. package/src/sass/components/_pushpin.scss +0 -11
  68. package/src/sass/components/_tapTarget.scss +0 -187
@@ -1,39 +1,61 @@
1
1
  @use "../../abstracts" as *;
2
2
 
3
- // Sliders (range).
3
+ // Sliders.
4
4
  //
5
5
  // Anatomy is the HTML. An <input type="range"> is the control. A wrapping
6
- // <label> (or .range / .range-field) is the host for the value label.
7
- // .slider is the carousel — do not put it on a range.
8
- // Helpers: .vertical, .small, .large. There is no required <span>.
6
+ // .range (or label / .range-field) is the host. .slider is the carousel —
7
+ // do not put it on a range. The IIFE plugin is Expressive.Range because
8
+ // Slider is the carousel.
9
9
  //
10
- // Tokens follow https://m3.material.io/components/sliders/specs (Expressive)
11
- // track 16dp, fully rounded
10
+ // Variants (M3 Expressive):
11
+ // standard default. Active from the start to the handle.
12
+ // .centered active grows from the midpoint to the handle.
13
+ // dual two inputs in one .range; active is between the handles.
14
+ //
15
+ // Orientation: horizontal (default) or .vertical.
16
+ // Size on the host: default XS, .s / .small, .m / .medium, .l / .large, .xl.
17
+ // Optional: a leading <i> (inset icon), .stops (discrete ticks), value
18
+ // label (JS .thumb).
19
+ //
20
+ // Tokens follow https://m3.material.io/components/sliders/specs
21
+ // XS track 16 / handle 44 / stop inset 8
22
+ // S track 24 / handle 44 / stop inset 8
23
+ // M track 40 / handle 52 / stop inset 12 / icon 24
24
+ // L track 56 / handle 68 / stop inset 16 / icon 24
25
+ // XL track 96 / handle 108 / stop inset 28 / icon 32
12
26
  // gap 6dp between handle and each track
27
+ // handle 4×H, fully rounded, primary; 2dp when pressed
13
28
  // active primary
14
29
  // inactive secondary-container
15
- // handle 4×44dp, fully rounded, primary; 2dp when pressed
16
- // state 40dp, 8% hover / 10% focus
17
- // label inverse-surface / inverse-on-surface, body-small
18
- // disabled 38%
19
- //
20
- // Range.Init() (import-time) sets --md-comp-slider-active-fraction so
21
- // the active track can follow the value. Not in AutoInit(). Dual-handle
22
- // range and discrete ticks are not implemented.
30
+ // stop 4dp primary dot
31
+ // label 40dp, inverse-surface / inverse-on-surface, body-small
23
32
 
24
- .range,
25
- .range-field,
26
- label:has(> [type="range"]) {
33
+ $_host: ".range, .range-field, label:has(> [type='range'])";
34
+
35
+ :is(#{$_host}),
36
+ input[type="range"] {
27
37
  --md-comp-slider-track-height: 16px;
28
38
  --md-comp-slider-handle-width: 4px;
29
39
  --md-comp-slider-handle-width-pressed: 2px;
30
40
  --md-comp-slider-handle-height: 44px;
31
41
  --md-comp-slider-gap: 6px;
42
+ --md-comp-slider-stop-size: 2px;
43
+ --md-comp-slider-stop-inset: 8px;
44
+ --md-comp-slider-icon-size: 24px;
32
45
  --md-comp-slider-active-track-color: var(--md-sys-color-primary);
33
46
  --md-comp-slider-inactive-track-color: var(--md-sys-color-secondary-container);
34
47
  --md-comp-slider-handle-color: var(--md-sys-color-primary);
48
+ --md-comp-slider-stop-color: var(--md-sys-color-primary);
35
49
  --md-comp-slider-gradient: to right;
50
+ --md-comp-slider-stop-at-start: var(--md-comp-slider-stop-inset) 50%;
51
+ --md-comp-slider-stop-at-end: calc(100% - var(--md-comp-slider-stop-inset)) 50%;
52
+ --md-comp-slider-active-fraction: 0%;
53
+ --md-comp-slider-start-fraction: 0%;
54
+ --md-comp-slider-end-fraction: 100%;
55
+ --md-comp-slider-stop-count: 6;
56
+ }
36
57
 
58
+ :is(#{$_host}) {
37
59
  position: relative;
38
60
  display: block;
39
61
  overflow: visible;
@@ -42,54 +64,83 @@ label:has(> [type="range"]) {
42
64
  font-family: $font-stack;
43
65
  }
44
66
 
45
- .range.small,
46
- .range-field.small,
47
- label.small:has(> [type="range"]),
48
- .range.small [type="range"],
49
- .range-field.small [type="range"],
50
- label.small > [type="range"] {
51
- --md-comp-slider-track-height: 8px;
52
- --md-comp-slider-handle-height: 32px;
67
+ // Sizes. Default is XS. .small / .large keep the old names, now S / L.
68
+ :is(#{$_host}).s,
69
+ :is(#{$_host}).small,
70
+ :is(#{$_host}).s [type="range"],
71
+ :is(#{$_host}).small [type="range"] {
72
+ --md-comp-slider-track-height: 24px;
73
+ --md-comp-slider-handle-height: 44px;
74
+ --md-comp-slider-stop-inset: 8px;
53
75
  }
54
76
 
55
- .range.large,
56
- .range-field.large,
57
- label.large:has(> [type="range"]),
58
- .range.large [type="range"],
59
- .range-field.large [type="range"],
60
- label.large > [type="range"] {
61
- --md-comp-slider-track-height: 24px;
77
+ :is(#{$_host}).m,
78
+ :is(#{$_host}).medium,
79
+ :is(#{$_host}).m [type="range"],
80
+ :is(#{$_host}).medium [type="range"] {
81
+ --md-comp-slider-track-height: 40px;
62
82
  --md-comp-slider-handle-height: 52px;
83
+ --md-comp-slider-stop-inset: 12px;
84
+ --md-comp-slider-icon-size: 24px;
63
85
  }
64
86
 
65
- .range.vertical,
66
- .range-field.vertical,
67
- label.vertical:has(> [type="range"]) {
87
+ :is(#{$_host}).l,
88
+ :is(#{$_host}).large,
89
+ :is(#{$_host}).l [type="range"],
90
+ :is(#{$_host}).large [type="range"] {
91
+ --md-comp-slider-track-height: 56px;
92
+ --md-comp-slider-handle-height: 68px;
93
+ --md-comp-slider-stop-inset: 16px;
94
+ --md-comp-slider-icon-size: 24px;
95
+ }
96
+
97
+ :is(#{$_host}).xl,
98
+ :is(#{$_host}).xl [type="range"] {
99
+ --md-comp-slider-track-height: 96px;
100
+ --md-comp-slider-handle-height: 108px;
101
+ --md-comp-slider-stop-inset: 28px;
102
+ --md-comp-slider-icon-size: 32px;
103
+ }
104
+
105
+ :is(#{$_host}).vertical {
68
106
  --md-comp-slider-gradient: to top;
107
+ --md-comp-slider-stop-at-start: 50% var(--md-comp-slider-stop-inset);
108
+ --md-comp-slider-stop-at-end: 50% calc(100% - var(--md-comp-slider-stop-inset));
69
109
  display: inline-flex;
70
110
  flex-direction: column;
71
111
  align-items: center;
72
112
  width: auto;
73
113
  }
74
114
 
75
- .range.vertical [type="range"],
76
- .range-field.vertical [type="range"],
77
- label.vertical > [type="range"] {
115
+ :is(#{$_host}).vertical [type="range"] {
78
116
  --md-comp-slider-gradient: to top;
117
+ --md-comp-slider-stop-at-start: 50% var(--md-comp-slider-stop-inset);
118
+ --md-comp-slider-stop-at-end: 50% calc(100% - var(--md-comp-slider-stop-inset));
79
119
  }
80
120
 
81
- input[type="range"] {
82
- --md-comp-slider-track-height: 16px;
83
- --md-comp-slider-handle-width: 4px;
84
- --md-comp-slider-handle-width-pressed: 2px;
85
- --md-comp-slider-handle-height: 44px;
86
- --md-comp-slider-gap: 6px;
87
- --md-comp-slider-active-track-color: var(--md-sys-color-primary);
88
- --md-comp-slider-inactive-track-color: var(--md-sys-color-secondary-container);
89
- --md-comp-slider-handle-color: var(--md-sys-color-primary);
90
- --md-comp-slider-gradient: to right;
91
- --md-comp-slider-active-fraction: 0%;
121
+ // Inset icon. Sits in the active track; M / L / XL are the intended sizes.
122
+ :is(#{$_host}) > i:first-child {
123
+ position: absolute;
124
+ z-index: 1;
125
+ left: var(--md-comp-slider-stop-inset);
126
+ top: 50%;
127
+ translate: 0 -50%;
128
+ font-size: var(--md-comp-slider-icon-size);
129
+ width: var(--md-comp-slider-icon-size);
130
+ height: var(--md-comp-slider-icon-size);
131
+ line-height: 1;
132
+ color: var(--md-sys-color-on-primary);
133
+ pointer-events: none;
134
+ }
92
135
 
136
+ :is(#{$_host}).vertical > i:first-child {
137
+ left: 50%;
138
+ top: auto;
139
+ bottom: var(--md-comp-slider-stop-inset);
140
+ translate: -50% 0;
141
+ }
142
+
143
+ input[type="range"] {
93
144
  appearance: none;
94
145
  box-sizing: border-box;
95
146
  display: block;
@@ -113,9 +164,7 @@ input[type="range"] {
113
164
  }
114
165
  }
115
166
 
116
- .range.vertical > [type="range"],
117
- .range-field.vertical > [type="range"],
118
- label.vertical > [type="range"] {
167
+ :is(#{$_host}).vertical > [type="range"] {
119
168
  writing-mode: vertical-lr;
120
169
  direction: rtl;
121
170
  width: var(--md-comp-slider-handle-height);
@@ -123,19 +172,211 @@ label.vertical > [type="range"] {
123
172
  margin: 0 8px;
124
173
  }
125
174
 
126
- // Gap around the handle so the tracks do not run under it (M3 stop gap).
175
+ // Gap around the handle so the tracks do not run under it.
127
176
  @mixin range-track-fill {
128
- background-image: linear-gradient(
129
- var(--md-comp-slider-gradient),
130
- var(--md-comp-slider-active-track-color)
131
- max(0%, calc(var(--md-comp-slider-active-fraction) - var(--md-comp-slider-gap) - var(--md-comp-slider-handle-width) / 2)),
132
- transparent
133
- max(0%, calc(var(--md-comp-slider-active-fraction) - var(--md-comp-slider-gap) - var(--md-comp-slider-handle-width) / 2)),
134
- transparent
135
- min(100%, calc(var(--md-comp-slider-active-fraction) + var(--md-comp-slider-gap) + var(--md-comp-slider-handle-width) / 2)),
136
- var(--md-comp-slider-inactive-track-color)
137
- min(100%, calc(var(--md-comp-slider-active-fraction) + var(--md-comp-slider-gap) + var(--md-comp-slider-handle-width) / 2))
138
- );
177
+ background-image:
178
+ radial-gradient(
179
+ circle var(--md-comp-slider-stop-size) at var(--md-comp-slider-stop-at-end),
180
+ var(--md-comp-slider-stop-color) 99%,
181
+ transparent
182
+ ),
183
+ linear-gradient(
184
+ var(--md-comp-slider-gradient),
185
+ var(--md-comp-slider-active-track-color)
186
+ max(
187
+ 0%,
188
+ calc(
189
+ var(--md-comp-slider-active-fraction) - var(--md-comp-slider-gap) -
190
+ var(--md-comp-slider-handle-width) / 2
191
+ )
192
+ ),
193
+ transparent
194
+ max(
195
+ 0%,
196
+ calc(
197
+ var(--md-comp-slider-active-fraction) - var(--md-comp-slider-gap) -
198
+ var(--md-comp-slider-handle-width) / 2
199
+ )
200
+ ),
201
+ transparent
202
+ min(
203
+ 100%,
204
+ calc(
205
+ var(--md-comp-slider-active-fraction) + var(--md-comp-slider-gap) +
206
+ var(--md-comp-slider-handle-width) / 2
207
+ )
208
+ ),
209
+ var(--md-comp-slider-inactive-track-color)
210
+ min(
211
+ 100%,
212
+ calc(
213
+ var(--md-comp-slider-active-fraction) + var(--md-comp-slider-gap) +
214
+ var(--md-comp-slider-handle-width) / 2
215
+ )
216
+ )
217
+ );
218
+ }
219
+
220
+ @mixin range-track-fill-centered {
221
+ background-image:
222
+ radial-gradient(
223
+ circle var(--md-comp-slider-stop-size) at var(--md-comp-slider-stop-at-start),
224
+ var(--md-comp-slider-stop-color) 99%,
225
+ transparent
226
+ ),
227
+ radial-gradient(
228
+ circle var(--md-comp-slider-stop-size) at var(--md-comp-slider-stop-at-end),
229
+ var(--md-comp-slider-stop-color) 99%,
230
+ transparent
231
+ ),
232
+ linear-gradient(
233
+ var(--md-comp-slider-gradient),
234
+ var(--md-comp-slider-inactive-track-color)
235
+ max(
236
+ 0%,
237
+ calc(
238
+ min(50%, var(--md-comp-slider-active-fraction)) - var(--md-comp-slider-gap) -
239
+ var(--md-comp-slider-handle-width) / 2
240
+ )
241
+ ),
242
+ transparent
243
+ max(
244
+ 0%,
245
+ calc(
246
+ min(50%, var(--md-comp-slider-active-fraction)) - var(--md-comp-slider-gap) -
247
+ var(--md-comp-slider-handle-width) / 2
248
+ )
249
+ ),
250
+ transparent
251
+ min(
252
+ 100%,
253
+ calc(
254
+ min(50%, var(--md-comp-slider-active-fraction)) + var(--md-comp-slider-gap) +
255
+ var(--md-comp-slider-handle-width) / 2
256
+ )
257
+ ),
258
+ var(--md-comp-slider-active-track-color)
259
+ min(
260
+ 100%,
261
+ calc(
262
+ min(50%, var(--md-comp-slider-active-fraction)) + var(--md-comp-slider-gap) +
263
+ var(--md-comp-slider-handle-width) / 2
264
+ )
265
+ ),
266
+ var(--md-comp-slider-active-track-color)
267
+ max(
268
+ 0%,
269
+ calc(
270
+ max(50%, var(--md-comp-slider-active-fraction)) - var(--md-comp-slider-gap) -
271
+ var(--md-comp-slider-handle-width) / 2
272
+ )
273
+ ),
274
+ transparent
275
+ max(
276
+ 0%,
277
+ calc(
278
+ max(50%, var(--md-comp-slider-active-fraction)) - var(--md-comp-slider-gap) -
279
+ var(--md-comp-slider-handle-width) / 2
280
+ )
281
+ ),
282
+ transparent
283
+ min(
284
+ 100%,
285
+ calc(
286
+ max(50%, var(--md-comp-slider-active-fraction)) + var(--md-comp-slider-gap) +
287
+ var(--md-comp-slider-handle-width) / 2
288
+ )
289
+ ),
290
+ var(--md-comp-slider-inactive-track-color)
291
+ min(
292
+ 100%,
293
+ calc(
294
+ max(50%, var(--md-comp-slider-active-fraction)) + var(--md-comp-slider-gap) +
295
+ var(--md-comp-slider-handle-width) / 2
296
+ )
297
+ )
298
+ );
299
+ }
300
+
301
+ @mixin range-track-fill-dual {
302
+ background-image:
303
+ radial-gradient(
304
+ circle var(--md-comp-slider-stop-size) at var(--md-comp-slider-stop-at-start),
305
+ var(--md-comp-slider-stop-color) 99%,
306
+ transparent
307
+ ),
308
+ radial-gradient(
309
+ circle var(--md-comp-slider-stop-size) at var(--md-comp-slider-stop-at-end),
310
+ var(--md-comp-slider-stop-color) 99%,
311
+ transparent
312
+ ),
313
+ linear-gradient(
314
+ var(--md-comp-slider-gradient),
315
+ var(--md-comp-slider-inactive-track-color)
316
+ max(
317
+ 0%,
318
+ calc(
319
+ var(--md-comp-slider-start-fraction) - var(--md-comp-slider-gap) -
320
+ var(--md-comp-slider-handle-width) / 2
321
+ )
322
+ ),
323
+ transparent
324
+ max(
325
+ 0%,
326
+ calc(
327
+ var(--md-comp-slider-start-fraction) - var(--md-comp-slider-gap) -
328
+ var(--md-comp-slider-handle-width) / 2
329
+ )
330
+ ),
331
+ transparent
332
+ min(
333
+ 100%,
334
+ calc(
335
+ var(--md-comp-slider-start-fraction) + var(--md-comp-slider-gap) +
336
+ var(--md-comp-slider-handle-width) / 2
337
+ )
338
+ ),
339
+ var(--md-comp-slider-active-track-color)
340
+ min(
341
+ 100%,
342
+ calc(
343
+ var(--md-comp-slider-start-fraction) + var(--md-comp-slider-gap) +
344
+ var(--md-comp-slider-handle-width) / 2
345
+ )
346
+ ),
347
+ var(--md-comp-slider-active-track-color)
348
+ max(
349
+ 0%,
350
+ calc(
351
+ var(--md-comp-slider-end-fraction) - var(--md-comp-slider-gap) -
352
+ var(--md-comp-slider-handle-width) / 2
353
+ )
354
+ ),
355
+ transparent
356
+ max(
357
+ 0%,
358
+ calc(
359
+ var(--md-comp-slider-end-fraction) - var(--md-comp-slider-gap) -
360
+ var(--md-comp-slider-handle-width) / 2
361
+ )
362
+ ),
363
+ transparent
364
+ min(
365
+ 100%,
366
+ calc(
367
+ var(--md-comp-slider-end-fraction) + var(--md-comp-slider-gap) +
368
+ var(--md-comp-slider-handle-width) / 2
369
+ )
370
+ ),
371
+ var(--md-comp-slider-inactive-track-color)
372
+ min(
373
+ 100%,
374
+ calc(
375
+ var(--md-comp-slider-end-fraction) + var(--md-comp-slider-gap) +
376
+ var(--md-comp-slider-handle-width) / 2
377
+ )
378
+ )
379
+ );
139
380
  }
140
381
 
141
382
  @mixin range-track {
@@ -163,7 +404,7 @@ label.vertical > [type="range"] {
163
404
  background: var(--md-comp-slider-handle-color);
164
405
  box-shadow: none;
165
406
  cursor: grab;
166
- transition: box-shadow .2s ease, width .15s ease, height .15s ease;
407
+ transition: box-shadow 0.2s ease, width 0.15s ease, height 0.15s ease;
167
408
  }
168
409
 
169
410
  @mixin range-thumb-vertical {
@@ -195,26 +436,22 @@ input[type="range"]:active:not(:disabled)::-webkit-slider-thumb {
195
436
  width: var(--md-comp-slider-handle-width-pressed);
196
437
  }
197
438
 
198
- .range.vertical > [type="range"]::-webkit-slider-runnable-track,
199
- .range-field.vertical > [type="range"]::-webkit-slider-runnable-track,
200
- label.vertical > [type="range"]::-webkit-slider-runnable-track {
439
+ :is(#{$_host}).centered [type="range"]::-webkit-slider-runnable-track {
440
+ @include range-track-fill-centered;
441
+ }
442
+
443
+ :is(#{$_host}).vertical > [type="range"]::-webkit-slider-runnable-track {
201
444
  @include range-track-vertical;
202
- // writing-mode does not swap physical width/height on these pseudos.
203
- // Center the 16dp track inside the 44dp-wide input.
204
445
  margin-left: calc((var(--md-comp-slider-handle-height) - var(--md-comp-slider-track-height)) / 2);
205
446
  }
206
447
 
207
- .range.vertical > [type="range"]::-webkit-slider-thumb,
208
- .range-field.vertical > [type="range"]::-webkit-slider-thumb,
209
- label.vertical > [type="range"]::-webkit-slider-thumb {
448
+ :is(#{$_host}).vertical > [type="range"]::-webkit-slider-thumb {
210
449
  @include range-thumb-vertical;
211
450
  margin-top: 0;
212
451
  margin-left: calc((var(--md-comp-slider-track-height) - var(--md-comp-slider-handle-height)) / 2);
213
452
  }
214
453
 
215
- .range.vertical > [type="range"]:active:not(:disabled)::-webkit-slider-thumb,
216
- .range-field.vertical > [type="range"]:active:not(:disabled)::-webkit-slider-thumb,
217
- label.vertical > [type="range"]:active:not(:disabled)::-webkit-slider-thumb {
454
+ :is(#{$_host}).vertical > [type="range"]:active:not(:disabled)::-webkit-slider-thumb {
218
455
  width: var(--md-comp-slider-handle-height);
219
456
  height: var(--md-comp-slider-handle-width-pressed);
220
457
  }
@@ -241,28 +478,118 @@ input[type="range"]:active:not(:disabled)::-moz-range-thumb {
241
478
  width: var(--md-comp-slider-handle-width-pressed);
242
479
  }
243
480
 
244
- .range.vertical > [type="range"]::-moz-range-track,
245
- .range-field.vertical > [type="range"]::-moz-range-track,
246
- label.vertical > [type="range"]::-moz-range-track {
481
+ :is(#{$_host}).centered [type="range"]::-moz-range-track {
482
+ @include range-track-fill-centered;
483
+ }
484
+
485
+ :is(#{$_host}).vertical > [type="range"]::-moz-range-track {
247
486
  @include range-track-vertical;
248
487
  margin-left: calc((var(--md-comp-slider-handle-height) - var(--md-comp-slider-track-height)) / 2);
249
488
  }
250
489
 
251
- .range.vertical > [type="range"]::-moz-range-thumb,
252
- .range-field.vertical > [type="range"]::-moz-range-thumb,
253
- label.vertical > [type="range"]::-moz-range-thumb {
490
+ :is(#{$_host}).vertical > [type="range"]::-moz-range-thumb {
254
491
  @include range-thumb-vertical;
255
492
  margin-left: calc((var(--md-comp-slider-track-height) - var(--md-comp-slider-handle-height)) / 2);
256
493
  }
257
494
 
258
- .range.vertical > [type="range"]:active:not(:disabled)::-moz-range-thumb,
259
- .range-field.vertical > [type="range"]:active:not(:disabled)::-moz-range-thumb,
260
- label.vertical > [type="range"]:active:not(:disabled)::-moz-range-thumb {
495
+ :is(#{$_host}).vertical > [type="range"]:active:not(:disabled)::-moz-range-thumb {
261
496
  width: var(--md-comp-slider-handle-height);
262
497
  height: var(--md-comp-slider-handle-width-pressed);
263
498
  }
264
499
 
265
- // Value label — M3 labeled slider. The JS plugin inserts this after the input.
500
+ // Dual-handle range. Two inputs share one host; the host paints the track.
501
+ :is(#{$_host}):has(> [type="range"] ~ [type="range"]) {
502
+ display: grid;
503
+ align-items: center;
504
+ min-height: var(--md-comp-slider-handle-height);
505
+
506
+ &::before {
507
+ content: "";
508
+ grid-area: 1 / 1;
509
+ height: var(--md-comp-slider-track-height);
510
+ border-radius: calc(var(--md-comp-slider-track-height) / 2);
511
+ pointer-events: none;
512
+ @include range-track-fill-dual;
513
+ }
514
+
515
+ > [type="range"] {
516
+ grid-area: 1 / 1;
517
+ width: 100%;
518
+ margin: 0;
519
+ background: none;
520
+ pointer-events: none;
521
+ }
522
+
523
+ > [type="range"]::-webkit-slider-runnable-track {
524
+ background-image: none;
525
+ background-color: transparent;
526
+ }
527
+
528
+ > [type="range"]::-webkit-slider-thumb {
529
+ pointer-events: auto;
530
+ position: relative;
531
+ z-index: 2;
532
+ }
533
+
534
+ > [type="range"]::-moz-range-track {
535
+ background-image: none;
536
+ background-color: transparent;
537
+ }
538
+
539
+ > [type="range"]::-moz-range-thumb {
540
+ pointer-events: auto;
541
+ }
542
+
543
+ > .thumb {
544
+ grid-area: 1 / 1;
545
+ }
546
+ }
547
+
548
+ :is(#{$_host}).vertical:has(> [type="range"] ~ [type="range"])::before {
549
+ width: var(--md-comp-slider-track-height);
550
+ height: 200px;
551
+ justify-self: center;
552
+ }
553
+
554
+ // Discrete stops. Dots along the active track; JS sets --stop-count.
555
+ :is(#{$_host}).stops::after {
556
+ content: "";
557
+ position: absolute;
558
+ z-index: 1;
559
+ left: 0;
560
+ top: 50%;
561
+ width: max(
562
+ 0px,
563
+ calc(
564
+ var(--md-comp-slider-active-fraction) - var(--md-comp-slider-gap) -
565
+ var(--md-comp-slider-handle-width) / 2
566
+ )
567
+ );
568
+ height: var(--md-comp-slider-track-height);
569
+ translate: 0 -50%;
570
+ pointer-events: none;
571
+ background-image: radial-gradient(
572
+ circle var(--md-comp-slider-stop-size),
573
+ var(--md-sys-color-on-primary) 99%,
574
+ transparent
575
+ );
576
+ background-size: calc(100% / max(1, var(--md-comp-slider-stop-count) - 1)) 100%;
577
+ background-repeat: repeat-x;
578
+ background-position: center;
579
+ }
580
+
581
+ :is(#{$_host}).stops.centered::after {
582
+ left: min(50%, var(--md-comp-slider-active-fraction));
583
+ width: max(
584
+ 0%,
585
+ calc(
586
+ abs(50% - var(--md-comp-slider-active-fraction)) - var(--md-comp-slider-gap) -
587
+ var(--md-comp-slider-handle-width) / 2
588
+ )
589
+ );
590
+ }
591
+
592
+ // Value label — 40dp, 4dp above the handle. JS inserts this after the input.
266
593
  input[type="range"] + .thumb {
267
594
  position: absolute;
268
595
  left: 0;
@@ -272,18 +599,18 @@ input[type="range"] + .thumb {
272
599
  display: flex;
273
600
  align-items: center;
274
601
  justify-content: center;
275
- min-width: 28px;
276
- min-height: 28px;
277
- padding: 4px 8px;
602
+ min-width: 40px;
603
+ height: 40px;
604
+ padding: 0 12px;
278
605
  border: none;
279
- border-radius: 14px;
606
+ border-radius: 20px;
280
607
  background-color: var(--md-sys-color-inverse-surface);
281
608
  color: var(--md-sys-color-inverse-on-surface);
282
609
  pointer-events: none;
283
610
  opacity: 0;
284
- transform: translate(-50%, calc(-100% - 12px)) scale(0.9);
611
+ transform: translate(-50%, calc(-100% - 4px)) scale(0.9);
285
612
  transform-origin: center bottom;
286
- transition: opacity .15s ease, transform .15s ease;
613
+ transition: opacity 0.15s ease, transform 0.15s ease;
287
614
  @include typescale("body-small");
288
615
 
289
616
  .value {
@@ -294,17 +621,15 @@ input[type="range"] + .thumb {
294
621
 
295
622
  &.active {
296
623
  opacity: 1;
297
- transform: translate(-50%, calc(-100% - 12px)) scale(1);
624
+ transform: translate(-50%, calc(-100% - 4px)) scale(1);
298
625
  }
299
626
  }
300
627
 
301
- .range.vertical > [type="range"] + .thumb,
302
- .range-field.vertical > [type="range"] + .thumb,
303
- label.vertical > [type="range"] + .thumb {
304
- transform: translate(calc(100% + 12px), -50%) scale(0.9);
628
+ :is(#{$_host}).vertical > [type="range"] + .thumb {
629
+ transform: translate(calc(100% + 4px), -50%) scale(0.9);
305
630
  transform-origin: left center;
306
631
 
307
632
  &.active {
308
- transform: translate(calc(100% + 12px), -50%) scale(1);
633
+ transform: translate(calc(100% + 4px), -50%) scale(1);
309
634
  }
310
635
  }