@expressivecss/expressive 0.7.0-rc.0 → 0.8.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 (105) hide show
  1. package/CHANGELOG.md +1311 -1
  2. package/README.md +14 -11
  3. package/dist/css/expressive.css +4215 -1668
  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 +1231 -1253
  8. package/dist/js/expressive.cjs.map +4 -4
  9. package/dist/js/expressive.js +1231 -1253
  10. package/dist/js/expressive.js.map +4 -4
  11. package/dist/js/expressive.min.js +12 -12
  12. package/dist/js/expressive.min.js.map +4 -4
  13. package/dist/js/expressive.mjs +1231 -1253
  14. package/dist/js/expressive.mjs.map +4 -4
  15. package/dist/types/behaviors/bottomSheets.d.ts +19 -11
  16. package/dist/types/behaviors/sheetDrag.d.ts +40 -0
  17. package/dist/types/behaviors/sideSheets.d.ts +2 -9
  18. package/dist/types/components/autocomplete.d.ts +15 -3
  19. package/dist/types/components/buttons.d.ts +5 -1
  20. package/dist/types/components/carousel.d.ts +87 -53
  21. package/dist/types/components/chips.d.ts +21 -1
  22. package/dist/types/components/expandingCard.d.ts +31 -0
  23. package/dist/types/components/index.d.ts +21 -20
  24. package/dist/types/components/lightbox.d.ts +1 -0
  25. package/dist/types/components/menu.d.ts +7 -1
  26. package/dist/types/components/{sidenav.d.ts → navigationDrawer.d.ts} +16 -16
  27. package/dist/types/components/registry.d.ts +8 -8
  28. package/dist/types/components/slider.d.ts +26 -85
  29. package/dist/types/components/snackbar.d.ts +20 -5
  30. package/dist/types/components/tabs.d.ts +12 -0
  31. package/dist/types/components/timepicker.d.ts +12 -9
  32. package/dist/types/core/utils.d.ts +64 -53
  33. package/dist/types/index.d.ts +8 -9
  34. package/package.json +15 -1
  35. package/src/sass/README.md +61 -11
  36. package/src/sass/abstracts/_breakpoints.scss +17 -19
  37. package/src/sass/abstracts/_mixins.scss +103 -21
  38. package/src/sass/abstracts/_variables.scss +50 -5
  39. package/src/sass/base/_global.scss +6 -24
  40. package/src/sass/base/_grid.scss +15 -12
  41. package/src/sass/base/_normalize.scss +34 -38
  42. package/src/sass/base/_typography.scss +4 -18
  43. package/src/sass/components/_badges.scss +2 -3
  44. package/src/sass/components/_banners.scss +298 -0
  45. package/src/sass/components/_bottom-app-bar.scss +180 -0
  46. package/src/sass/components/_bottom-sheet.scss +12 -4
  47. package/src/sass/components/_button-groups.scss +186 -0
  48. package/src/sass/components/_buttons.scss +245 -92
  49. package/src/sass/components/_cards.scss +494 -73
  50. package/src/sass/components/_carousel.scss +562 -68
  51. package/src/sass/components/_chips.scss +64 -5
  52. package/src/sass/components/_datepicker.scss +24 -24
  53. package/src/sass/components/_dialog.scss +17 -11
  54. package/src/sass/components/_drag-handle.scss +130 -0
  55. package/src/sass/components/_expanding-card.scss +260 -0
  56. package/src/sass/components/_fab-menu.scss +232 -0
  57. package/src/sass/components/_floating-sheet.scss +86 -0
  58. package/src/sass/components/_icon-buttons.scss +194 -0
  59. package/src/sass/components/_icons-material-design.scss +16 -4
  60. package/src/sass/components/_index.scss +22 -6
  61. package/src/sass/components/_list.scss +16 -13
  62. package/src/sass/components/_loading-indicator.scss +106 -0
  63. package/src/sass/components/_menu.scss +335 -61
  64. package/src/sass/components/_navbar.scss +71 -24
  65. package/src/sass/components/_navigation-bar.scss +13 -10
  66. package/src/sass/components/{_sidenav.scss → _navigation-drawer.scss} +123 -33
  67. package/src/sass/components/_navigation-rail.scss +22 -22
  68. package/src/sass/components/_page-footer.scss +19 -16
  69. package/src/sass/components/_pagination.scss +24 -6
  70. package/src/sass/components/_panes.scss +52 -11
  71. package/src/sass/components/{_preloader.scss → _progress.scss} +11 -3
  72. package/src/sass/components/_scrim.scss +37 -0
  73. package/src/sass/components/_search.scss +259 -0
  74. package/src/sass/components/_segmented-buttons.scss +188 -0
  75. package/src/sass/components/_side-sheet.scss +2 -2
  76. package/src/sass/components/_snackbar.scss +20 -15
  77. package/src/sass/components/_split-button.scss +196 -0
  78. package/src/sass/components/_tabs.scss +7 -8
  79. package/src/sass/components/_timepicker.scss +67 -58
  80. package/src/sass/components/_toolbar.scss +206 -80
  81. package/src/sass/components/forms/_checkboxes.scss +49 -12
  82. package/src/sass/components/forms/_fieldset.scss +4 -4
  83. package/src/sass/components/forms/_file-input.scss +8 -1
  84. package/src/sass/components/forms/_index.scss +1 -1
  85. package/src/sass/components/forms/_input-fields.scss +50 -44
  86. package/src/sass/components/forms/_radio-buttons.scss +42 -10
  87. package/src/sass/components/forms/_select.scss +0 -1
  88. package/src/sass/components/forms/{_range.scss → _slider.scss} +8 -9
  89. package/src/sass/components/forms/_switches.scss +6 -7
  90. package/src/sass/tokens/_index.scss +4 -1
  91. package/src/sass/tokens/_reference.scss +70 -61
  92. package/src/sass/tokens/_state.scss +24 -0
  93. package/src/sass/tokens/_theme.scss +6 -3
  94. package/src/sass/tokens/_vibrant.scss +76 -0
  95. package/src/sass/utilities/_colors.scss +14 -1
  96. package/src/sass/utilities/_helpers.scss +5 -5
  97. package/src/sass/utilities/_visibility.scss +48 -26
  98. package/dist/types/behaviors/waves.d.ts +0 -15
  99. package/dist/types/components/parallax.d.ts +0 -16
  100. package/dist/types/components/range.d.ts +0 -43
  101. package/dist/types/core/bounding.d.ts +0 -6
  102. package/dist/types/core/edges.d.ts +0 -6
  103. package/src/sass/components/_parallax.scss +0 -104
  104. package/src/sass/components/_pulse.scss +0 -37
  105. package/src/sass/components/_slider.scss +0 -129
@@ -1,5 +1,10 @@
1
1
  @use "../abstracts" as *;
2
2
 
3
+ // A chip is written with the element that says what it is: <span> for a
4
+ // display or input chip, <button> for assist / suggestion, and
5
+ // <input type="checkbox"> + <label> for filter. The class styles all three
6
+ // elements, so everything below has to survive on any of them - hence the
7
+ // resets.
3
8
  .chip {
4
9
  --font-size: 14px;
5
10
  --font-size-icon: 18px;
@@ -13,20 +18,66 @@
13
18
  margin: 0;
14
19
  height: 32px;
15
20
  padding-left: var(--padding);
16
- padding-right: var(--padding);
21
+ padding-right: var(--padding);
17
22
  font-size: var(--font-size);
18
- font-weight: 500;
23
+ font-weight: 500;
19
24
  border-radius: 8px;
20
25
  align-items: center;
21
26
  user-select: none;
22
27
  vertical-align: top;
23
28
 
24
- &:focus,
29
+ // Button and label defaults that would otherwise show through.
30
+ appearance: none;
31
+ border: 0;
32
+ font-family: inherit;
33
+ text-align: inherit;
34
+
35
+ // Selected. `.active` is the pre-0.8.0 name and still works; `:focus` used
36
+ // to stand in for selection here, which meant a chip could not show focus
37
+ // and selection as the different things they are.
38
+ &.selected,
25
39
  &.active {
26
- outline: none;
27
40
  background-color: var(--md-sys-color-secondary-container);
28
41
  color: var(--md-sys-color-on-secondary-container);
29
42
  }
43
+
44
+ @include focus-visible();
45
+ }
46
+
47
+ // Interactive chips only. A display chip is a <span> and is not a control.
48
+ :is(button, label).chip {
49
+ cursor: pointer;
50
+
51
+ &:hover {
52
+ background-color: color-mix(
53
+ in oklab,
54
+ var(--md-sys-color-on-surface-variant) state-opacity("hover"),
55
+ var(--md-sys-color-surface-container-low)
56
+ );
57
+ }
58
+ }
59
+
60
+ // Filter chip. The checkbox carries the state and the form value; the label is
61
+ // the chip. Kept focusable rather than `display: none` so it stays reachable.
62
+ .chip-input {
63
+ position: absolute;
64
+ width: 1px;
65
+ height: 1px;
66
+ margin: -1px;
67
+ padding: 0;
68
+ border: 0;
69
+ overflow: hidden;
70
+ clip-path: inset(50%);
71
+ white-space: nowrap;
72
+ }
73
+
74
+ .chip-input:checked + .chip {
75
+ background-color: var(--md-sys-color-secondary-container);
76
+ color: var(--md-sys-color-on-secondary-container);
77
+ }
78
+
79
+ .chip-input:focus-visible + .chip {
80
+ @include focus-ring();
30
81
  }
31
82
 
32
83
  .chip.outlined {
@@ -49,6 +100,7 @@
49
100
  color: var(--md-sys-color-on-surface-variant);
50
101
  }
51
102
 
103
+ // The delete affordance is a real <button>, so it needs the button reset too.
52
104
  .chip .close {
53
105
  border-radius: 50%;
54
106
  height: 24px;
@@ -58,10 +110,17 @@
58
110
  justify-content: center;
59
111
  align-content: center;
60
112
  cursor: pointer;
113
+ appearance: none;
114
+ border: 0;
115
+ background: none;
116
+ color: inherit;
117
+ font: inherit;
118
+
119
+ @include focus-visible();
61
120
  }
62
121
 
63
122
  .chip .close:hover {
64
- background-color: color-mix(in oklab, var(--md-sys-color-on-surface-variant) 8%, transparent);
123
+ background-color: color-mix(in oklab, var(--md-sys-color-on-surface-variant) state-opacity("hover"), transparent);
65
124
  }
66
125
 
67
126
  //--------------------------
@@ -1,6 +1,6 @@
1
1
  @use "../abstracts" as *;
2
2
 
3
- .datepicker-container {
3
+ :is(.datepicker-container, .date-picker-container) {
4
4
  display: flex;
5
5
  flex-direction: column;
6
6
  max-width: 325px;
@@ -8,7 +8,7 @@
8
8
  background-color: var(--md-sys-color-surface);
9
9
  }
10
10
 
11
- .datepicker-controls {
11
+ :is(.datepicker-controls, .date-picker-controls) {
12
12
  display: flex;
13
13
  justify-content: space-between;
14
14
  width: 280px;
@@ -86,7 +86,7 @@
86
86
 
87
87
 
88
88
  /* Date Display */
89
- .datepicker-date-display {
89
+ :is(.datepicker-date-display, .date-picker-date-display) {
90
90
  flex: 1 auto;
91
91
  // @removed since v2.2.1-dev regarding Material M3 standards
92
92
  // background-color: var(--md-sys-color-primary);
@@ -118,11 +118,11 @@
118
118
 
119
119
 
120
120
  /* Calendar */
121
- .datepicker-calendar-container {
121
+ :is(.datepicker-calendar-container, .date-picker-calendar-container) {
122
122
  flex: 2.5 auto;
123
123
  }
124
124
 
125
- .datepicker-table {
125
+ :is(.datepicker-table, .date-picker-table) {
126
126
  width: 280px;
127
127
  font-size: 1rem;
128
128
  margin: 0 auto;
@@ -145,7 +145,7 @@
145
145
  color: var(--md-sys-color-on-surface-variant);
146
146
  }
147
147
 
148
- .datepicker-day {
148
+ :is(.datepicker-day, .date-picker-day) {
149
149
  padding: 0;
150
150
  color: var(--md-sys-color-on-background);
151
151
  // border-radius: 50%;
@@ -162,17 +162,17 @@
162
162
  }
163
163
 
164
164
  // @todo
165
- .datepicker-day.has-event {}
165
+ :is(.datepicker-day, .date-picker-day).has-event {}
166
166
  // @todo
167
- .datepicker-day.is-inrange {}
167
+ :is(.datepicker-day, .date-picker-day).is-inrange {}
168
168
  // @todo
169
- .datepicker-day.is-startrange {}
169
+ :is(.datepicker-day, .date-picker-day).is-startrange {}
170
170
  // @todo
171
- .datepicker-day.is-endrange {}
171
+ :is(.datepicker-day, .date-picker-day).is-endrange {}
172
172
 
173
- .datepicker-day.is-daterange-start,
174
- .datepicker-day.is-daterange-end,
175
- .datepicker-day.is-daterange {
173
+ :is(.datepicker-day, .date-picker-day).is-daterange-start,
174
+ :is(.datepicker-day, .date-picker-day).is-daterange-end,
175
+ :is(.datepicker-day, .date-picker-day).is-daterange {
176
176
  position: relative;
177
177
 
178
178
  &:before {
@@ -186,20 +186,20 @@
186
186
  }
187
187
  }
188
188
 
189
- .datepicker-day.is-daterange-start:before,
190
- .datepicker-day.is-daterange-end:before {
189
+ :is(.datepicker-day, .date-picker-day).is-daterange-start:before,
190
+ :is(.datepicker-day, .date-picker-day).is-daterange-end:before {
191
191
  width: 50%;
192
192
  }
193
193
 
194
- .datepicker-day.is-daterange-start:before {
194
+ :is(.datepicker-day, .date-picker-day).is-daterange-start:before {
195
195
  left: 50%;
196
196
  }
197
197
 
198
- .datepicker-day.is-daterange .datepicker-day-button:before {
198
+ :is(.datepicker-day, .date-picker-day).is-daterange :is(.datepicker-day-button, .date-picker-day-button):before {
199
199
  background-color: var(--md-sys-color-primary-container);
200
200
  }
201
201
 
202
- .datepicker-day-button {
202
+ :is(.datepicker-day-button, .date-picker-day-button) {
203
203
  background-color: transparent;
204
204
  border: none;
205
205
  line-height: 34px;
@@ -239,7 +239,7 @@
239
239
  }
240
240
 
241
241
  /* Footer */
242
- .datepicker-footer {
242
+ :is(.datepicker-footer, .date-picker-footer) {
243
243
  width: 280px;
244
244
  margin: 0 auto;
245
245
  padding-bottom: 5px;
@@ -247,15 +247,15 @@
247
247
  justify-content: space-between;
248
248
  }
249
249
 
250
- .datepicker-cancel,
251
- .datepicker-clear,
252
- .datepicker-today,
253
- .datepicker-done {
250
+ :is(.datepicker-cancel, .date-picker-cancel),
251
+ :is(.datepicker-clear, .date-picker-clear),
252
+ :is(.datepicker-today, .date-picker-today),
253
+ :is(.datepicker-done, .date-picker-done) {
254
254
  color: var(--md-sys-color-primary);
255
255
  padding: 0 1rem;
256
256
  }
257
257
 
258
- .datepicker-clear {
258
+ :is(.datepicker-clear, .date-picker-clear) {
259
259
  color: var(--md-sys-color-error);
260
260
  }
261
261
 
@@ -39,7 +39,7 @@ dialog {
39
39
  @include z-depth("3");
40
40
 
41
41
  &::backdrop {
42
- background-color: color-mix(in oklab, var(--md-sys-color-scrim) 32%, transparent);
42
+ background-color: var(--md-comp-scrim-color);
43
43
  backdrop-filter: blur(var(--md-comp-basic-dialog-scrim-blur));
44
44
  }
45
45
  }
@@ -59,7 +59,7 @@ dialog[open] {
59
59
  }
60
60
 
61
61
  // Optional leading icon (M3 basic dialog).
62
- dialog > i:first-child {
62
+ dialog > #{$icon}:first-child {
63
63
  flex: 0 0 auto;
64
64
  align-self: center;
65
65
  margin: 24px 24px 0;
@@ -78,7 +78,7 @@ dialog > :is(h1, h2, h3, h4, h5, h6) {
78
78
  @include typescale("headline-small");
79
79
  }
80
80
 
81
- dialog > i:first-child + :is(h1, h2, h3, h4, h5, h6) {
81
+ dialog > #{$icon}:first-child + :is(h1, h2, h3, h4, h5, h6) {
82
82
  padding-top: 16px;
83
83
  text-align: center;
84
84
  }
@@ -98,7 +98,7 @@ dialog > p {
98
98
  @include typescale("body-medium");
99
99
  }
100
100
 
101
- dialog > i:first-child + :is(h1, h2, h3, h4, h5, h6) + p {
101
+ dialog > #{$icon}:first-child + :is(h1, h2, h3, h4, h5, h6) + p {
102
102
  text-align: center;
103
103
  }
104
104
 
@@ -150,11 +150,12 @@ dialog:not(
150
150
  .right-sheet,
151
151
  .left,
152
152
  .left-sheet,
153
- .sidenav,
154
- .sidenav-overlay
153
+ .floating-sheet,
154
+ :is(.sidenav, .navigation-drawer),
155
+ :is(.sidenav-overlay, .navigation-drawer-overlay)
155
156
  )
156
157
  > :is(form, nav):last-child
157
- > :is(button, a.button):not(.tonal, .outlined, .elevated, .filled) {
158
+ > :is(button, a.button):not(.tonal, .outlined, .elevated, .filled, .icon-button) {
158
159
  @include btn-flat;
159
160
  }
160
161
 
@@ -215,7 +216,12 @@ dialog.max {
215
216
  }
216
217
 
217
218
  // Close is a 48dp icon button. 4dp bar inset + 12dp to the glyph = 16dp.
218
- > header > :is(button, a):has(> i:only-child) {
219
+ // `:only-child` counts elements, not text nodes, so `<button><span icon/>
220
+ // Save</button>` reads as icon-only and would be squeezed into the 48dp
221
+ // circle. CSS cannot see the text node; `.button` is the author's opt-out,
222
+ // and sends such an action to the general button rules instead. Same
223
+ // escape hatch the app bar already has.
224
+ > header > :is(button, a):not(.button, .icon-button):has(> #{$icon}:only-child) {
219
225
  display: inline-flex;
220
226
  flex: 0 0 48px;
221
227
  align-items: center;
@@ -231,7 +237,7 @@ dialog.max {
231
237
  color: var(--md-sys-color-on-surface-variant);
232
238
  box-shadow: none;
233
239
 
234
- > i {
240
+ > #{$icon} {
235
241
  font-size: 24px;
236
242
  width: 24px;
237
243
  height: 24px;
@@ -239,7 +245,7 @@ dialog.max {
239
245
  }
240
246
 
241
247
  &:hover {
242
- background-color: color-mix(in oklab, var(--md-sys-color-on-surface) 8%, transparent);
248
+ background-color: color-mix(in oklab, var(--md-sys-color-on-surface) state-opacity("hover"), transparent);
243
249
  }
244
250
 
245
251
  &:focus-visible {
@@ -249,7 +255,7 @@ dialog.max {
249
255
  }
250
256
 
251
257
  // Confirming action in the header is a text button, not an icon.
252
- > header :is(button, a.button):not(:has(> i:only-child)) {
258
+ > header :is(button, a.button):not(:has(> #{$icon}:only-child)) {
253
259
  @include btn-flat;
254
260
  width: auto;
255
261
  min-width: 0;
@@ -0,0 +1,130 @@
1
+ @use "../abstracts" as *;
2
+
3
+ // Drag handles.
4
+ //
5
+ // Anatomy is the HTML. A .drag-handle is the handle, and which element it is
6
+ // says what it is: a <span aria-hidden="true"> when it is only an affordance -
7
+ // the usual case - and a <button> with a name when something is genuinely wired
8
+ // to it. semantics.json enforces exactly that split, which is what lets the
9
+ // interactive states below key off `button` and be right every time.
10
+ //
11
+ // **Nothing here drags.** The handle is CSS; the dragging is the page's, and so
12
+ // is the keyboard path to whatever the drag achieves. A handle that answers
13
+ // only a pointer puts its outcome out of reach of everyone not using one, so
14
+ // the framework ships no reordering behaviour to hang off this and the docs say
15
+ // plainly that a drag-only reorder is unreachable. The decorative spelling is
16
+ // the default; the button spelling has to earn its name.
17
+ //
18
+ // M3 Expressive tokenised the *vertical* handle - the one sitting in the gutter
19
+ // between two panes, Compose's VerticalDragHandle: a 4x48dp bar in a 24dp
20
+ // container that swells to 12x52dp on a medium corner while it is held. That is
21
+ // md.comp.drag-handle and it is what this partial draws.
22
+ //
23
+ // The bottom sheet's grabber is a different component with a different token
24
+ // set - md.comp.sheet-bottom.docked.drag-handle, a horizontal 32x4dp bar at 40%
25
+ // on-surface-variant - so a .drag-handle inside a sheet takes the sheet's
26
+ // geometry from _bottom-sheet.scss rather than carrying a `horizontal` modifier
27
+ // here. One class, two hosts, and neither one's values invented.
28
+ //
29
+ // Tokens follow md.comp.drag-handle.* (DSP 34.0.21):
30
+ // container 24dp wide - the hit target, not the bar
31
+ // bar 4 x 48dp, corner.full, outline
32
+ // pressed 12 x 52dp, corner.medium, on-surface
33
+ // state layer inverse-on-surface, 8% hover / 10% focus
34
+ // elevation level0 at rest and pressed alike, so there is nothing to draw
35
+ //
36
+ // Two departures, both deliberate. M3 publishes a container *width* and no
37
+ // container height, leaving the container to take whatever height it is given;
38
+ // this pins it to the pressed bar so that holding the handle cannot reflow what
39
+ // is beside it, which a stretched container would also achieve but only where
40
+ // the host happens to stretch it. And Compose distinguishes a *dragged* state
41
+ // from a pressed one - draggedSize, draggedShape, draggedColor, checked before
42
+ // isPressed - which the web token set does not publish and CSS could not select
43
+ // anyway: there is no "is being dragged" pseudo-class, and nothing here drags.
44
+ // `:active` covers both, at the pressed values.
45
+ .drag-handle {
46
+ --md-comp-drag-handle-container-width: 24px;
47
+ --md-comp-drag-handle-width: 4px;
48
+ --md-comp-drag-handle-height: 48px;
49
+ --md-comp-drag-handle-shape: 9999px;
50
+ --md-comp-drag-handle-color: var(--md-sys-color-outline);
51
+ --md-comp-drag-handle-pressed-width: 12px;
52
+ --md-comp-drag-handle-pressed-height: 52px;
53
+ --md-comp-drag-handle-pressed-shape: 12px;
54
+ --md-comp-drag-handle-pressed-color: var(--md-sys-color-on-surface);
55
+ --md-comp-drag-handle-state-layer-color: var(--md-sys-color-inverse-on-surface);
56
+
57
+ box-sizing: border-box;
58
+ display: inline-flex;
59
+ flex: 0 0 auto;
60
+ align-items: center;
61
+ justify-content: center;
62
+ // The container is the hit target, and it is sized to the *pressed* bar
63
+ // rather than the resting one so that swelling the bar moves nothing beside
64
+ // it. M3 gives the container a width and no height for the same reason.
65
+ inline-size: var(--md-comp-drag-handle-container-width);
66
+ block-size: var(--md-comp-drag-handle-pressed-height);
67
+ margin: 0;
68
+ padding: 0;
69
+ border: 0;
70
+ border-radius: var(--md-comp-drag-handle-shape);
71
+ background-color: transparent;
72
+ cursor: grab;
73
+ appearance: none;
74
+ -webkit-tap-highlight-color: transparent;
75
+
76
+ // The bar. It is a pseudo-element and not the box itself because the box is
77
+ // the hit target: 4dp of anything is not something a pointer can be asked to
78
+ // find, and the state layer needs a surface the bar's own shape does not clip.
79
+ &::before {
80
+ content: "";
81
+ display: block;
82
+ inline-size: var(--md-comp-drag-handle-width);
83
+ block-size: var(--md-comp-drag-handle-height);
84
+ border-radius: var(--md-comp-drag-handle-shape);
85
+ background-color: var(--md-comp-drag-handle-color);
86
+ transition: inline-size .15s ease, block-size .15s ease, border-radius .15s ease,
87
+ background-color .15s ease;
88
+ }
89
+ }
90
+
91
+ // Interactive states belong to the button spelling alone. A decorative handle
92
+ // is not a control and must not light up under a pointer that merely crosses
93
+ // it - and because the rule keying this is `button`, the semantics rule holding
94
+ // every other element to aria-hidden is what keeps the two in step.
95
+ button.drag-handle {
96
+ &:hover {
97
+ background-color: color-mix(
98
+ in oklab,
99
+ var(--md-comp-drag-handle-state-layer-color) state-opacity("hover"),
100
+ transparent
101
+ );
102
+ }
103
+
104
+ &:focus-visible {
105
+ background-color: color-mix(
106
+ in oklab,
107
+ var(--md-comp-drag-handle-state-layer-color) state-opacity("focus"),
108
+ transparent
109
+ );
110
+ }
111
+
112
+ @include focus-visible;
113
+
114
+ &:active {
115
+ cursor: grabbing;
116
+ }
117
+
118
+ &:active::before {
119
+ inline-size: var(--md-comp-drag-handle-pressed-width);
120
+ block-size: var(--md-comp-drag-handle-pressed-height);
121
+ border-radius: var(--md-comp-drag-handle-pressed-shape);
122
+ background-color: var(--md-comp-drag-handle-pressed-color);
123
+ }
124
+ }
125
+
126
+ @media (prefers-reduced-motion: reduce) {
127
+ .drag-handle::before {
128
+ transition: none;
129
+ }
130
+ }