@expressivecss/expressive 0.7.0 → 0.9.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 (122) hide show
  1. package/CHANGELOG.md +1313 -1
  2. package/README.md +76 -30
  3. package/dist/css/expressive.css +4896 -1777
  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/fonts/LICENSE-material-symbols +93 -0
  8. package/dist/fonts/LICENSE-noto-sans +93 -0
  9. package/dist/fonts/LICENSE-roboto +93 -0
  10. package/dist/fonts/material-symbols-outlined.woff2 +0 -0
  11. package/dist/fonts/material-symbols-rounded.woff2 +0 -0
  12. package/dist/fonts/material-symbols-sharp.woff2 +0 -0
  13. package/dist/fonts/noto-sans-latin-400.woff2 +0 -0
  14. package/dist/fonts/noto-sans-latin-500.woff2 +0 -0
  15. package/dist/fonts/roboto-latin-400.woff2 +0 -0
  16. package/dist/fonts/roboto-latin-500.woff2 +0 -0
  17. package/dist/js/expressive.cjs +1673 -1338
  18. package/dist/js/expressive.cjs.map +4 -4
  19. package/dist/js/expressive.js +1673 -1338
  20. package/dist/js/expressive.js.map +4 -4
  21. package/dist/js/expressive.min.js +13 -13
  22. package/dist/js/expressive.min.js.map +4 -4
  23. package/dist/js/expressive.mjs +1673 -1338
  24. package/dist/js/expressive.mjs.map +4 -4
  25. package/dist/types/behaviors/bottomSheets.d.ts +19 -11
  26. package/dist/types/behaviors/sheetDrag.d.ts +40 -0
  27. package/dist/types/behaviors/sideSheets.d.ts +2 -9
  28. package/dist/types/components/appBar.d.ts +26 -0
  29. package/dist/types/components/autocomplete.d.ts +15 -3
  30. package/dist/types/components/buttonGroup.d.ts +39 -0
  31. package/dist/types/components/buttons.d.ts +5 -1
  32. package/dist/types/components/cards.d.ts +11 -13
  33. package/dist/types/components/carousel.d.ts +87 -53
  34. package/dist/types/components/chips.d.ts +21 -1
  35. package/dist/types/components/expandingCard.d.ts +31 -0
  36. package/dist/types/components/index.d.ts +23 -20
  37. package/dist/types/components/lightbox.d.ts +1 -0
  38. package/dist/types/components/menu.d.ts +11 -1
  39. package/dist/types/components/{sidenav.d.ts → navigationDrawer.d.ts} +16 -16
  40. package/dist/types/components/registry.d.ts +18 -8
  41. package/dist/types/components/slider.d.ts +26 -85
  42. package/dist/types/components/snackbar.d.ts +20 -5
  43. package/dist/types/components/tabs.d.ts +12 -0
  44. package/dist/types/components/timepicker.d.ts +12 -10
  45. package/dist/types/components/tooltip.d.ts +1 -0
  46. package/dist/types/core/utils.d.ts +64 -53
  47. package/dist/types/index.d.ts +8 -9
  48. package/package.json +39 -3
  49. package/src/sass/README.md +61 -11
  50. package/src/sass/abstracts/_breakpoints.scss +17 -19
  51. package/src/sass/abstracts/_mixins.scss +155 -21
  52. package/src/sass/abstracts/_variables.scss +69 -5
  53. package/src/sass/base/_fonts.scss +63 -0
  54. package/src/sass/base/_global.scss +6 -24
  55. package/src/sass/base/_grid.scss +15 -12
  56. package/src/sass/base/_normalize.scss +34 -38
  57. package/src/sass/base/_typography.scss +4 -18
  58. package/src/sass/components/_badges.scss +3 -5
  59. package/src/sass/components/_banners.scss +298 -0
  60. package/src/sass/components/_bottom-app-bar.scss +180 -0
  61. package/src/sass/components/_bottom-sheet.scss +12 -4
  62. package/src/sass/components/_button-groups.scss +358 -0
  63. package/src/sass/components/_buttons.scss +245 -92
  64. package/src/sass/components/_cards.scss +514 -78
  65. package/src/sass/components/_carousel.scss +562 -68
  66. package/src/sass/components/_chips.scss +64 -5
  67. package/src/sass/components/_datepicker.scss +24 -24
  68. package/src/sass/components/_dialog.scss +17 -11
  69. package/src/sass/components/_drag-handle.scss +130 -0
  70. package/src/sass/components/_expanding-card.scss +226 -0
  71. package/src/sass/components/_fab-menu.scss +232 -0
  72. package/src/sass/components/_floating-sheet.scss +86 -0
  73. package/src/sass/components/_icon-buttons.scss +172 -0
  74. package/src/sass/components/_icons-material-design.scss +22 -10
  75. package/src/sass/components/_index.scss +22 -6
  76. package/src/sass/components/_list.scss +16 -13
  77. package/src/sass/components/_loading-indicator.scss +106 -0
  78. package/src/sass/components/_menu.scss +335 -61
  79. package/src/sass/components/_navbar.scss +245 -45
  80. package/src/sass/components/_navigation-bar.scss +22 -11
  81. package/src/sass/components/{_sidenav.scss → _navigation-drawer.scss} +123 -33
  82. package/src/sass/components/_navigation-rail.scss +31 -23
  83. package/src/sass/components/_page-footer.scss +19 -16
  84. package/src/sass/components/_pagination.scss +24 -6
  85. package/src/sass/components/_panes.scss +52 -11
  86. package/src/sass/components/{_preloader.scss → _progress.scss} +11 -3
  87. package/src/sass/components/_scrim.scss +37 -0
  88. package/src/sass/components/_search.scss +259 -0
  89. package/src/sass/components/_segmented-buttons.scss +188 -0
  90. package/src/sass/components/_side-sheet.scss +2 -2
  91. package/src/sass/components/_snackbar.scss +20 -15
  92. package/src/sass/components/_split-button.scss +196 -0
  93. package/src/sass/components/_tabs.scss +7 -8
  94. package/src/sass/components/_timepicker.scss +67 -58
  95. package/src/sass/components/_toolbar.scss +206 -80
  96. package/src/sass/components/_transitions.scss +8 -1
  97. package/src/sass/components/forms/_checkboxes.scss +49 -12
  98. package/src/sass/components/forms/_fieldset.scss +4 -4
  99. package/src/sass/components/forms/_file-input.scss +8 -1
  100. package/src/sass/components/forms/_index.scss +1 -1
  101. package/src/sass/components/forms/_input-fields.scss +50 -44
  102. package/src/sass/components/forms/_radio-buttons.scss +42 -10
  103. package/src/sass/components/forms/_select.scss +38 -1
  104. package/src/sass/components/forms/{_range.scss → _slider.scss} +8 -9
  105. package/src/sass/components/forms/_switches.scss +6 -7
  106. package/src/sass/expressive.scss +5 -0
  107. package/src/sass/tokens/_index.scss +4 -1
  108. package/src/sass/tokens/_reference.scss +70 -61
  109. package/src/sass/tokens/_state.scss +24 -0
  110. package/src/sass/tokens/_theme.scss +6 -3
  111. package/src/sass/tokens/_vibrant.scss +76 -0
  112. package/src/sass/utilities/_colors.scss +14 -1
  113. package/src/sass/utilities/_helpers.scss +5 -5
  114. package/src/sass/utilities/_visibility.scss +48 -26
  115. package/dist/types/behaviors/waves.d.ts +0 -15
  116. package/dist/types/components/parallax.d.ts +0 -16
  117. package/dist/types/components/range.d.ts +0 -43
  118. package/dist/types/core/bounding.d.ts +0 -6
  119. package/dist/types/core/edges.d.ts +0 -6
  120. package/src/sass/components/_parallax.scss +0 -104
  121. package/src/sass/components/_pulse.scss +0 -37
  122. package/src/sass/components/_slider.scss +0 -129
@@ -137,7 +137,7 @@ $_sheet: "dialog.side-sheet, dialog.right, dialog.right-sheet, dialog.left, dial
137
137
  color: var(--md-sys-color-on-surface-variant);
138
138
  box-shadow: none;
139
139
 
140
- > i {
140
+ > #{$icon} {
141
141
  font-size: 24px;
142
142
  width: 24px;
143
143
  height: 24px;
@@ -145,7 +145,7 @@ $_sheet: "dialog.side-sheet, dialog.right, dialog.right-sheet, dialog.left, dial
145
145
  }
146
146
 
147
147
  &:hover {
148
- background-color: color-mix(in oklab, var(--md-sys-color-on-surface) 8%, transparent);
148
+ background-color: color-mix(in oklab, var(--md-sys-color-on-surface) state-opacity("hover"), transparent);
149
149
  }
150
150
 
151
151
  &:focus-visible {
@@ -14,7 +14,7 @@
14
14
  // action label-large / inverse-primary text button
15
15
  // close 24dp icon, inverse-on-surface
16
16
  // inset 8dp compact, 24dp from the bottom on larger screens
17
- // placement bottom of the UI; centered from the small breakpoint
17
+ // placement bottom of the UI; centered from the Medium breakpoint
18
18
  //
19
19
  // Snackbars must not interrupt browsing: the container is pointer-events
20
20
  // none except on the bar, and the live region is polite. One at a time
@@ -48,7 +48,7 @@ $_bar: ".snackbar";
48
48
  pointer-events: auto;
49
49
  }
50
50
 
51
- @include bp-up("small") {
51
+ @include bp-up("medium") {
52
52
  --md-comp-snackbar-container-margin-leading: 24px;
53
53
  --md-comp-snackbar-container-margin-trailing: 24px;
54
54
  --md-comp-snackbar-container-margin-bottom: 24px;
@@ -78,6 +78,11 @@ $_bar: ".snackbar";
78
78
  --md-comp-snackbar-icon-color: var(--md-sys-color-inverse-on-surface);
79
79
 
80
80
  box-sizing: border-box;
81
+ // Swipe-to-dismiss is horizontal, so claim that axis and leave vertical
82
+ // panning to the page. Without this the browser takes a horizontal swipe for
83
+ // itself and cancels the drag mid-gesture: `preventDefault()` on a
84
+ // `pointermove` cannot stop it the way it stopped a `touchmove`.
85
+ touch-action: pan-y;
81
86
  display: flex;
82
87
  align-items: center;
83
88
  gap: 8px;
@@ -95,7 +100,7 @@ $_bar: ".snackbar";
95
100
  @include z-depth("3");
96
101
  cursor: default;
97
102
 
98
- @include bp-up("small") {
103
+ @include bp-up("medium") {
99
104
  width: max-content;
100
105
  min-width: min(344px, 100%);
101
106
  }
@@ -116,7 +121,7 @@ $_bar: ".snackbar";
116
121
  }
117
122
 
118
123
  // Optional leading icon (BeerCSS). M3 snackbars have no leading icon.
119
- > i:first-child {
124
+ > #{$icon}:first-child {
120
125
  flex: 0 0 auto;
121
126
  font-size: 24px;
122
127
  width: 24px;
@@ -126,7 +131,7 @@ $_bar: ".snackbar";
126
131
  }
127
132
 
128
133
  // Inline links in the supporting text use the action color.
129
- a:not(.button) {
134
+ a:not(.button, .icon-button) {
130
135
  color: var(--md-comp-snackbar-action-label-text-color);
131
136
  }
132
137
 
@@ -145,8 +150,8 @@ $_bar: ".snackbar";
145
150
  }
146
151
 
147
152
  // Text action — inverse-primary, not the filled common-button default.
148
- > :is(button, a.button):not(.circle),
149
- > nav > :is(button, a.button):not(.circle) {
153
+ > :is(button, a.button):not(.circle, .icon-button),
154
+ > nav > :is(button, a.button):not(.circle, .icon-button) {
150
155
  flex: 0 0 auto;
151
156
  height: 40px;
152
157
  min-width: 48px;
@@ -159,12 +164,12 @@ $_bar: ".snackbar";
159
164
  @include typescale("label-large");
160
165
 
161
166
  &:hover {
162
- background-color: color-mix(in oklab, var(--md-sys-color-inverse-primary) 8%, transparent);
167
+ background-color: color-mix(in oklab, var(--md-sys-color-inverse-primary) state-opacity("hover"), transparent);
163
168
  }
164
169
 
165
170
  &:active,
166
171
  &:focus {
167
- background-color: color-mix(in oklab, var(--md-sys-color-inverse-primary) 10%, transparent);
172
+ background-color: color-mix(in oklab, var(--md-sys-color-inverse-primary) state-opacity("focus"), transparent);
168
173
  }
169
174
 
170
175
  &:focus-visible {
@@ -186,7 +191,7 @@ $_bar: ".snackbar";
186
191
  color: var(--md-comp-snackbar-icon-color);
187
192
  box-shadow: none;
188
193
 
189
- > i {
194
+ > #{$icon} {
190
195
  font-size: 24px;
191
196
  width: 24px;
192
197
  height: 24px;
@@ -194,12 +199,12 @@ $_bar: ".snackbar";
194
199
  }
195
200
 
196
201
  &:hover {
197
- background-color: color-mix(in oklab, var(--md-sys-color-inverse-on-surface) 8%, transparent);
202
+ background-color: color-mix(in oklab, var(--md-sys-color-inverse-on-surface) state-opacity("hover"), transparent);
198
203
  }
199
204
 
200
205
  &:active,
201
206
  &:focus {
202
- background-color: color-mix(in oklab, var(--md-sys-color-inverse-on-surface) 10%, transparent);
207
+ background-color: color-mix(in oklab, var(--md-sys-color-inverse-on-surface) state-opacity("focus"), transparent);
203
208
  }
204
209
 
205
210
  &:focus-visible {
@@ -226,7 +231,7 @@ $_bar: ".snackbar";
226
231
 
227
232
  // Static overlay. In-flow without this (docs previews). Not AutoInit —
228
233
  // add and remove the class yourself, or use the constructor for timing.
229
- // Default: bottom of the UI, centered from the small breakpoint.
234
+ // Default: bottom of the UI, centered from the Medium breakpoint.
230
235
  #{$_bar}.active {
231
236
  position: fixed;
232
237
  z-index: 10000;
@@ -238,7 +243,7 @@ $_bar: ".snackbar";
238
243
  width: max-content;
239
244
  max-width: min(672px, calc(100% - 48px));
240
245
 
241
- @include bp-down("small") {
246
+ @include bp-down("medium") {
242
247
  left: 8px;
243
248
  right: 8px;
244
249
  bottom: max(8px, env(safe-area-inset-bottom, 0px));
@@ -251,7 +256,7 @@ $_bar: ".snackbar";
251
256
  top: 24px;
252
257
  bottom: auto;
253
258
 
254
- @include bp-down("small") {
259
+ @include bp-down("medium") {
255
260
  top: 8px;
256
261
  left: 8px;
257
262
  right: 8px;
@@ -0,0 +1,196 @@
1
+ @use "sass:map";
2
+ @use "../abstracts" as *;
3
+
4
+ // Split buttons.
5
+ //
6
+ // Anatomy is the HTML. A <div class="split-button"> holding two buttons: a
7
+ // leading one that performs the action, and a trailing `.menu-trigger` that
8
+ // opens a <menu> of related ones. They sit 2dp apart, the outside of the pair
9
+ // is round, and the seam between them is square-ish until something happens
10
+ // to it.
11
+ //
12
+ // Both halves are ordinary buttons, so every style class works on them and
13
+ // nothing here restates a colour. What this partial owns is the pair: the
14
+ // seam, the two insets that differ from a lone button's, and the larger
15
+ // trailing icon.
16
+ //
17
+ // No composite role is declared. The two halves are two Tab stops doing two
18
+ // different things - `toolbar` and its nine siblings promise an arrow-key
19
+ // model this is not (SEMANTICS rule 2). The menu the trailing half opens has
20
+ // its own keyboard contract, which Menu implements and owns.
21
+ //
22
+ // `aria-expanded` on the trailing half is Menu's to write, not the author's
23
+ // (SEMANTICS rule 1) - which is exactly why the expanded shape can be drawn
24
+ // from a selector with no script of this component's own.
25
+ //
26
+ // Tokens are md.comp.split-button.{xsmall … xlarge} (DSP 34.0.21). Five
27
+ // families collapse to one here the way the button's own ladder does: the
28
+ // size class rewrites the tokens and the two halves map them onto the button
29
+ // tokens once, below.
30
+ //
31
+ // size height lead-start lead-end trail-space trail-icon inner swollen
32
+ // xsmall 32 12 10 13 22 4 8
33
+ // small 40 16 12 13 22 4 12
34
+ // medium 56 24 24 15 26 4 12
35
+ // large 96 48 48 29 38 8 20
36
+ // xlarge 136 64 64 43 50 12 20
37
+ //
38
+ // `lead-start` is the button ladder's own `space` at every size, so it is not
39
+ // restated - `button-size()` sets it and the leading half keeps it. Only the
40
+ // inset facing the seam is the split button's, and only at the two smallest
41
+ // sizes does it actually differ. `between-space` is 2dp in all five families,
42
+ // so it is written once for the same reason.
43
+
44
+ // The two halves. The trailing one *is* the menu trigger: `.menu-trigger`
45
+ // with a target is the JS contract Menu claims, and a split button whose
46
+ // trailing half opened nothing would be a lone button with a decoration
47
+ // glued to it. semantics.json enforces that.
48
+ //
49
+ // `:where()`, not `:is()`: `:is()` takes the specificity of its heaviest
50
+ // argument, so `a.button` would make `> $_half:hover` a class heavier than
51
+ // `> $_trail[aria-expanded]` - and hovering an open split button would
52
+ // collapse the seam Material says stays round for as long as the menu is up.
53
+ //
54
+ // `.icon-button` is deliberately not a half. It is its own component with its
55
+ // own size ladder, insets, colours and icon sizing, all set on the element by
56
+ // its own rule - so the container could not size it by handing it tokens, and
57
+ // the seam here would fight the shape it already draws. Button groups refuse
58
+ // it for the same reason. The trailing half is a `.button.menu-trigger`.
59
+ $_lead: ":where(button:not(.icon-button), a.button):not(.menu-trigger)";
60
+ $_trail: ".menu-trigger:not(.icon-button)";
61
+ $_half: ":where(button:not(.icon-button), a.button)";
62
+
63
+ // md.comp.split-button.<size>.{leading-button.trailing-space,
64
+ // trailing-button.leading-space, trailing.icon-size, inner-corner,
65
+ // inner-pressed-corner}. The hovered and pressed inner corners are the same
66
+ // value in all five families, so one token carries both states.
67
+ $_sizes: (
68
+ "xsmall": (lead-end: 10px, trail-space: 13px, trail-icon: 22px, inner: 4px, swollen: 8px),
69
+ "small": (lead-end: 12px, trail-space: 13px, trail-icon: 22px, inner: 4px, swollen: 12px),
70
+ "medium": (lead-end: 24px, trail-space: 15px, trail-icon: 26px, inner: 4px, swollen: 12px),
71
+ "large": (lead-end: 48px, trail-space: 29px, trail-icon: 38px, inner: 8px, swollen: 20px),
72
+ "xlarge": (lead-end: 64px, trail-space: 43px, trail-icon: 50px, inner: 12px, swollen: 20px),
73
+ );
74
+
75
+ // The two ladders are keyed alike and read together, so a size added to one
76
+ // and not the other would emit `--token: ;` - a dropped declaration with no
77
+ // error and no visible cause. Fail the build instead.
78
+ @if map.keys($_sizes) != map.keys($button-sizes) {
79
+ @error "$_sizes and $button-sizes must name the same five sizes; got " +
80
+ map.keys($_sizes) + " against " + map.keys($button-sizes) + ".";
81
+ }
82
+
83
+ @mixin _split-size($name) {
84
+ $s: map.get($_sizes, $name);
85
+
86
+ --md-comp-split-button-leading-button-trailing-space: #{map.get($s, lead-end)};
87
+ --md-comp-split-button-trailing-button-space: #{map.get($s, trail-space)};
88
+ --md-comp-split-button-trailing-icon-size: #{map.get($s, trail-icon)};
89
+ --md-comp-split-button-inner-corner-corner-size: #{map.get($s, inner)};
90
+ --md-comp-split-button-inner-pressed-corner-corner-size: #{map.get($s, swollen)};
91
+ }
92
+
93
+ .split-button {
94
+ --md-comp-split-button-between-space: 2px;
95
+ --md-comp-split-button-container-shape: 9999px;
96
+
97
+ @include button-size("small");
98
+ @include _split-size("small");
99
+
100
+ display: inline-flex;
101
+ align-items: center;
102
+ gap: var(--md-comp-split-button-between-space);
103
+
104
+ // The size axis. Height, icon size, the leading inset and the outline width
105
+ // are the button ladder's, so `button-size()` sets them on the container and
106
+ // both halves inherit; the five values that are the split button's own are
107
+ // beside it. The type role cannot travel that way - `btn` states a font-size
108
+ // of its own - so it is set on the halves.
109
+ @each $name, $s in $button-sizes {
110
+ &.#{$name} {
111
+ @include button-size($name);
112
+ @include _split-size($name);
113
+
114
+ > #{$_half} {
115
+ @include typescale(map.get($s, label));
116
+ }
117
+ }
118
+ }
119
+
120
+ // The seam goes through a variable so that hovering, pressing or opening
121
+ // changes one value rather than restating `border-radius`. A shorthand under
122
+ // `:hover` would take the round outer corners with it, which is the bug
123
+ // `_button-groups.scss` records.
124
+ > #{$_half} {
125
+ --_inner: var(--md-comp-split-button-inner-corner-corner-size);
126
+
127
+ // `btn` transitions background-color and box-shadow; restating the
128
+ // property list replaces it wholesale, so both have to be named again or
129
+ // an `.elevated` split button snaps its shadow where every other elevated
130
+ // button eases it.
131
+ transition: border-radius .2s ease-out, background-color .2s ease-out,
132
+ box-shadow .2s ease-out;
133
+
134
+ // M3 gives hovered and pressed the same swollen corner.
135
+ &:hover,
136
+ &:active {
137
+ --_inner: var(--md-comp-split-button-inner-pressed-corner-corner-size);
138
+ }
139
+ }
140
+
141
+ // Logical, so the outside of the pair is the outside of the pair in RTL too:
142
+ // the flex row reverses and start follows it.
143
+ > #{$_lead} {
144
+ --md-comp-filled-button-trailing-space: var(--md-comp-split-button-leading-button-trailing-space);
145
+
146
+ border-start-start-radius: var(--md-comp-split-button-container-shape);
147
+ border-end-start-radius: var(--md-comp-split-button-container-shape);
148
+ border-start-end-radius: var(--_inner);
149
+ border-end-end-radius: var(--_inner);
150
+ }
151
+
152
+ > #{$_trail} {
153
+ // Symmetric, and narrower than a lone button's - the trailing half is a
154
+ // glyph in a box, not a label. The width falls out of the two insets and
155
+ // the icon: 48dp at the two smallest sizes, square at the three largest.
156
+ --md-comp-filled-button-leading-space: var(--md-comp-split-button-trailing-button-space);
157
+ --md-comp-filled-button-trailing-space: var(--md-comp-split-button-trailing-button-space);
158
+ // `_buttons.scss` sizes a button's icon from this token, so rewriting it
159
+ // here is the whole of the override. M3 draws the trailing glyph larger
160
+ // than the button ladder's icon at every size (22 against 20, up to 50
161
+ // against 40).
162
+ --md-comp-filled-button-icon-size: var(--md-comp-split-button-trailing-icon-size);
163
+
164
+ border-start-end-radius: var(--md-comp-split-button-container-shape);
165
+ border-end-end-radius: var(--md-comp-split-button-container-shape);
166
+ border-start-start-radius: var(--_inner);
167
+ border-end-start-radius: var(--_inner);
168
+
169
+ // Activation. md.comp.split-button.<size>.trailing.inner-selected-corner
170
+ // is 50%, and Compose resolves a percentage corner against the shape's
171
+ // *smaller* dimension - 20dp on the 48x40 half at `small`, which is what
172
+ // `9999px` clamps to. The two agree at every size, and the outer corners
173
+ // are the same 50% written the same way. So the pair reads as two
174
+ // separated round buttons for as long as the menu is open. Menu writes the
175
+ // attribute; nothing here is scripted.
176
+ &[aria-expanded="true"] {
177
+ --_inner: var(--md-comp-split-button-container-shape);
178
+ }
179
+
180
+ > #{$icon} {
181
+ transition: transform .2s ease-out;
182
+ }
183
+
184
+ &[aria-expanded="true"] > #{$icon} {
185
+ transform: rotate(180deg);
186
+ }
187
+ }
188
+ }
189
+
190
+ // The corner morph and the spin are the only motion this component has.
191
+ @media (prefers-reduced-motion: reduce) {
192
+ .split-button > #{$_half},
193
+ .split-button > #{$_trail} > #{$icon} {
194
+ transition: none;
195
+ }
196
+ }
@@ -83,10 +83,9 @@ $_tab: "> a, > .tab > a";
83
83
  cursor: pointer;
84
84
  appearance: none;
85
85
  @include typescale("title-small");
86
- font-family: $font-stack;
87
86
  transition: color .2s ease, background-color .2s ease;
88
87
 
89
- > i {
88
+ > #{$icon} {
90
89
  flex: 0 0 auto;
91
90
  font-size: var(--md-comp-primary-tab-icon-size);
92
91
  width: var(--md-comp-primary-tab-icon-size);
@@ -97,13 +96,13 @@ $_tab: "> a, > .tab > a";
97
96
 
98
97
  &:hover {
99
98
  color: var(--md-sys-color-on-surface);
100
- background-color: color-mix(in oklab, var(--md-sys-color-on-surface) 8%, transparent);
99
+ background-color: color-mix(in oklab, var(--md-sys-color-on-surface) state-opacity("hover"), transparent);
101
100
  }
102
101
 
103
102
  &:focus-visible,
104
103
  &:focus {
105
104
  color: var(--md-sys-color-on-surface);
106
- background-color: color-mix(in oklab, var(--md-sys-color-on-surface) 10%, transparent);
105
+ background-color: color-mix(in oklab, var(--md-sys-color-on-surface) state-opacity("focus"), transparent);
107
106
  outline: none;
108
107
  }
109
108
 
@@ -113,12 +112,12 @@ $_tab: "> a, > .tab > a";
113
112
  }
114
113
 
115
114
  &.active:hover {
116
- background-color: color-mix(in oklab, var(--md-sys-color-primary) 8%, transparent);
115
+ background-color: color-mix(in oklab, var(--md-sys-color-primary) state-opacity("hover"), transparent);
117
116
  }
118
117
 
119
118
  &.active:focus-visible,
120
119
  &.active:focus {
121
- background-color: color-mix(in oklab, var(--md-sys-color-primary) 10%, transparent);
120
+ background-color: color-mix(in oklab, var(--md-sys-color-primary) state-opacity("focus"), transparent);
122
121
  }
123
122
 
124
123
  // Disabled — on the <a>. The wrapping `.tab.disabled` case is
@@ -135,7 +134,7 @@ $_tab: "> a, > .tab > a";
135
134
  // Stacked icon + label is 64dp (primary).
136
135
  &:not(.horizontal) {
137
136
  #{$_tab} {
138
- &:has(> i) {
137
+ &:has(> #{$icon}) {
139
138
  min-height: var(--md-comp-primary-tab-with-icon-container-height);
140
139
  padding-block: 8px;
141
140
  }
@@ -210,7 +209,7 @@ $_tab: "> a, > .tab > a";
210
209
  }
211
210
  }
212
211
 
213
- @include bp-down("large") {
212
+ @include bp-down("expanded") {
214
213
  .tabs {
215
214
  > :is(a, .tab) {
216
215
  flex: 1 1 0;