@expressivecss/expressive 0.3.0 → 0.4.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 (74) hide show
  1. package/README.md +1 -1
  2. package/dist/css/expressive.css +1011 -1339
  3. package/dist/css/expressive.css.map +1 -1
  4. package/dist/css/expressive.min.css +1 -1
  5. package/dist/css/expressive.min.css.map +1 -1
  6. package/dist/js/expressive.cjs +701 -853
  7. package/dist/js/expressive.cjs.map +4 -4
  8. package/dist/js/expressive.js +701 -853
  9. package/dist/js/expressive.js.map +4 -4
  10. package/dist/js/expressive.min.js +10 -20
  11. package/dist/js/expressive.min.js.map +4 -4
  12. package/dist/js/expressive.mjs +701 -854
  13. package/dist/js/expressive.mjs.map +4 -4
  14. package/dist/types/components/autocomplete.d.ts +2 -9
  15. package/dist/types/components/buttons.d.ts +2 -2
  16. package/dist/types/components/cards.d.ts +3 -6
  17. package/dist/types/components/carousel.d.ts +3 -3
  18. package/dist/types/components/characterCounter.d.ts +3 -5
  19. package/dist/types/components/chips.d.ts +2 -2
  20. package/dist/types/components/collapsible.d.ts +6 -23
  21. package/dist/types/components/datepicker.d.ts +2 -8
  22. package/dist/types/components/dropdown.d.ts +11 -2
  23. package/dist/types/components/index.d.ts +0 -1
  24. package/dist/types/components/lightbox.d.ts +2 -2
  25. package/dist/types/components/parallax.d.ts +2 -8
  26. package/dist/types/components/pushpin.d.ts +2 -2
  27. package/dist/types/components/range.d.ts +2 -2
  28. package/dist/types/components/registry.d.ts +0 -4
  29. package/dist/types/components/scrollspy.d.ts +26 -50
  30. package/dist/types/components/select.d.ts +20 -6
  31. package/dist/types/components/sidenav.d.ts +40 -55
  32. package/dist/types/components/slider.d.ts +2 -2
  33. package/dist/types/components/tabs.d.ts +2 -2
  34. package/dist/types/components/tapTarget.d.ts +2 -2
  35. package/dist/types/components/timepicker.d.ts +2 -4
  36. package/dist/types/components/tooltip.d.ts +2 -2
  37. package/dist/types/core/component.d.ts +4 -4
  38. package/dist/types/index.d.ts +1 -1
  39. package/package.json +2 -2
  40. package/src/sass/README.md +2 -6
  41. package/src/sass/abstracts/_breakpoints.scss +0 -16
  42. package/src/sass/abstracts/_variables.scss +0 -21
  43. package/src/sass/base/_grid.scss +0 -19
  44. package/src/sass/components/_badges.scss +1 -1
  45. package/src/sass/components/_buttons.scss +22 -54
  46. package/src/sass/components/_cards.scss +50 -123
  47. package/src/sass/components/_carousel.scss +4 -6
  48. package/src/sass/components/_collapsible.scss +13 -15
  49. package/src/sass/components/_collection.scss +9 -27
  50. package/src/sass/components/_datepicker.scss +1 -15
  51. package/src/sass/components/_dropdown.scss +110 -14
  52. package/src/sass/components/_index.scss +1 -0
  53. package/src/sass/components/_modal.scss +13 -31
  54. package/src/sass/components/_navbar.scss +9 -6
  55. package/src/sass/components/_page-footer.scss +134 -15
  56. package/src/sass/components/_parallax.scss +7 -28
  57. package/src/sass/components/_preloader.scss +145 -300
  58. package/src/sass/components/_scrollspy.scss +18 -0
  59. package/src/sass/components/_sidenav.scss +226 -120
  60. package/src/sass/components/_table_of_contents.scss +30 -14
  61. package/src/sass/components/_tabs.scss +54 -35
  62. package/src/sass/components/_tapTarget.scss +2 -6
  63. package/src/sass/components/_toast.scss +10 -11
  64. package/src/sass/components/_toolbar.scss +2 -2
  65. package/src/sass/components/_tooltip.scss +4 -9
  66. package/src/sass/components/_transitions.scss +0 -5
  67. package/src/sass/components/forms/_index.scss +0 -5
  68. package/src/sass/components/forms/_input-fields.scss +49 -52
  69. package/src/sass/components/forms/_select.scss +175 -60
  70. package/src/sass/components/forms/_switches.scss +21 -8
  71. package/src/sass/utilities/_helpers.scss +1 -3
  72. package/src/sass/utilities/_typescale.scss +0 -5
  73. package/src/sass/utilities/_visibility.scss +2 -2
  74. package/dist/types/components/modal.d.ts +0 -45
@@ -1,36 +1,154 @@
1
1
  @use "../abstracts" as *;
2
2
 
3
+ // Navigation drawer.
4
+ //
5
+ // Overlay is a modal <dialog> (JS wraps a <ul class="sidenav"> in
6
+ // dialog.sidenav-overlay, or the author writes <dialog class="sidenav">).
7
+ // showModal() puts it on the top layer; ::backdrop is the scrim; Escape
8
+ // and a scrim tap close it. Drag writes --md-comp-nav-drawer-shift;
9
+ // this file does the slide.
10
+ //
11
+ // .sidenav-fixed is a breakpoint, not a JS open(): at `large` the host
12
+ // is display: contents so the list is a docked sidebar. Below that it
13
+ // is the same overlay drawer as any other sidenav.
14
+ //
15
+ // Tokens:
16
+ // --md-comp-nav-drawer-width 300px
17
+ // --md-comp-nav-drawer-item-height 48px
18
+ // --md-comp-nav-drawer-duration 250ms
19
+ // --md-comp-nav-drawer-easing
20
+ // --md-comp-nav-drawer-shift 0px (JS, px from fully open)
21
+ // --md-comp-nav-drawer-open 1 (JS, 0–1 scrim opacity)
22
+ // --md-comp-nav-drawer-font-size 14px
23
+ // --md-comp-nav-drawer-padding 16px
24
+ // --md-comp-nav-drawer-line-height item height
25
+
26
+ :root {
27
+ --md-comp-nav-drawer-width: 300px;
28
+ }
29
+
30
+ dialog.sidenav-overlay,
31
+ dialog.sidenav {
32
+ --md-comp-nav-drawer-item-height: 48px;
33
+ --md-comp-nav-drawer-duration: 250ms;
34
+ --md-comp-nav-drawer-easing: ease;
35
+ --md-comp-nav-drawer-shift: 0px;
36
+ --md-comp-nav-drawer-open: 1;
37
+ --md-comp-nav-drawer-font-size: 14px;
38
+ --md-comp-nav-drawer-padding: 16px;
39
+ --md-comp-nav-drawer-line-height: var(--md-comp-nav-drawer-item-height);
40
+
41
+ box-sizing: border-box;
42
+ width: var(--md-comp-nav-drawer-width);
43
+ min-width: 0;
44
+ max-width: none;
45
+ max-height: none;
46
+ height: 100dvh;
47
+ margin: 0;
48
+ padding: 0;
49
+ border: none;
50
+ border-radius: 0;
51
+ inset: 0 auto 0 0;
52
+ overflow: hidden;
53
+ color: var(--md-sys-color-on-surface);
54
+ background-color: var(--md-sys-color-surface);
55
+ outline: none;
56
+ @include z-depth("1");
57
+
58
+ transform: translateX(calc(-100% + var(--md-comp-nav-drawer-shift)));
59
+ transition:
60
+ transform var(--md-comp-nav-drawer-duration) var(--md-comp-nav-drawer-easing),
61
+ display var(--md-comp-nav-drawer-duration) allow-discrete,
62
+ overlay var(--md-comp-nav-drawer-duration) allow-discrete;
63
+
64
+ &:modal {
65
+ display: flex;
66
+ flex-direction: column;
67
+ transform: translateX(var(--md-comp-nav-drawer-shift));
68
+ }
69
+
70
+ &.right-aligned {
71
+ inset: 0 0 0 auto;
72
+ transform: translateX(calc(100% - var(--md-comp-nav-drawer-shift)));
73
+
74
+ &:modal {
75
+ transform: translateX(calc(-1 * var(--md-comp-nav-drawer-shift)));
76
+ }
77
+ }
78
+
79
+ &.dragging {
80
+ transition: none;
81
+ }
82
+
83
+ &::backdrop {
84
+ background-color: color-mix(in oklab, var(--md-sys-color-scrim) 32%, transparent);
85
+ opacity: var(--md-comp-nav-drawer-open, 1);
86
+ backdrop-filter: none;
87
+ transition:
88
+ opacity var(--md-comp-nav-drawer-duration) var(--md-comp-nav-drawer-easing),
89
+ display var(--md-comp-nav-drawer-duration) allow-discrete,
90
+ overlay var(--md-comp-nav-drawer-duration) allow-discrete;
91
+ }
92
+ }
93
+
94
+ @starting-style {
95
+ :is(dialog.sidenav-overlay, dialog.sidenav):modal {
96
+ transform: translateX(-100%);
97
+ }
98
+
99
+ :is(dialog.sidenav-overlay, dialog.sidenav).right-aligned:modal {
100
+ transform: translateX(100%);
101
+ }
102
+
103
+ :is(dialog.sidenav-overlay, dialog.sidenav):modal::backdrop {
104
+ opacity: 0;
105
+ }
106
+ }
107
+
108
+ @media (prefers-reduced-motion: reduce) {
109
+ dialog.sidenav-overlay,
110
+ dialog.sidenav {
111
+ transition: none;
112
+
113
+ &::backdrop {
114
+ transition: none;
115
+ }
116
+ }
117
+ }
118
+
119
+ // Docked sidebar: the host must not be a box, or it would steal the
120
+ // list's position:fixed. Closed, so it is not :modal and does not
121
+ // lock the page.
122
+ @include bp-up("large") {
123
+ dialog.sidenav-overlay:has(> .sidenav-fixed):not(:modal) {
124
+ display: contents;
125
+ }
126
+ }
127
+
3
128
  .sidenav {
4
- --sidenav-width: 300px;
5
- --sidenav-font-size: 14px;
6
- --sidenav-padding: 16px;
7
- --sidenav-item-height: 48px;
8
- --sidenav-line-height: var(--sidenav-item-height);
129
+ --md-comp-nav-drawer-item-height: 48px;
130
+ --md-comp-nav-drawer-font-size: 14px;
131
+ --md-comp-nav-drawer-padding: 16px;
132
+ --md-comp-nav-drawer-line-height: var(--md-comp-nav-drawer-item-height);
9
133
 
134
+ box-sizing: border-box;
10
135
  position: fixed;
11
- width: var(--sidenav-width);
12
- left: 0;
13
- top: 0;
136
+ width: var(--md-comp-nav-drawer-width);
137
+ inset: 0 auto 0 0;
14
138
  margin: 0;
15
- transform: translateX(-100%);
16
- height: 100vh;
17
139
  padding: 0;
140
+ height: 100dvh;
18
141
  z-index: 999;
19
142
  overflow-y: auto;
20
- will-change: transform;
21
- backface-visibility: hidden;
22
- transform: translateX(-105%);
143
+ overscroll-behavior: contain;
23
144
  user-select: none;
24
-
25
- color: var(--md-sys-color-on-secondary-container);
145
+ color: var(--md-sys-color-on-surface);
26
146
  background-color: var(--md-sys-color-surface);
147
+ transform: translateX(-100%);
27
148
  @include z-depth("1");
28
149
 
29
- // Right Align
30
150
  &.right-aligned {
31
- right: 0;
32
- transform: translateX(105%);
33
- left: auto;
151
+ inset: 0 0 0 auto;
34
152
  transform: translateX(100%);
35
153
  }
36
154
 
@@ -38,92 +156,79 @@
38
156
  margin: 0;
39
157
  }
40
158
 
41
- /* Hover only on top row */
42
- /*a:hover {
43
- //color: red;
44
- //background-color: var(--md-sys-color-on-secondary-container);
45
- //md.sys.color.on-secondary-container
46
- }*/
47
- a:focus {
48
- background-color: rgba(0, 0, 0, 0.12);
159
+ li {
160
+ list-style: none;
161
+ display: grid;
162
+ align-content: center;
49
163
  }
50
164
 
51
- li.active > a:not(.collapsible-header):not(.btn):not(.btn-large):not(.btn-small):not(.btn-flat):not(.btn-large):not(.btn-floating) {
52
- //color: var(--md-sys-color-on-secondary-container); //var(--md-sys-color-on-secondary-container);
53
- //background-color: var(--md-sys-color-secondary-container);
165
+ li.active > a:not(.button, .circle, .extra) {
54
166
  background-color: color-mix(in oklab, var(--md-sys-color-secondary) 10%, transparent);
55
167
  }
56
168
 
57
- .collapsible-body > ul { padding-left: 10px; }
58
-
59
- li {
60
- list-style: none;
61
- display: grid;
62
- align-content: center;
169
+ details > div > ul {
170
+ padding-left: 10px;
63
171
  }
172
+
64
173
  li > a {
65
- /* https://stackoverflow.com/questions/5848090/full-width-hover-background-for-nested-lists */
66
- //padding-left: calc(100% + var(--sidenav-padding));
67
- //margin-left: -100%;
68
- margin: 0 12px;
69
- padding: 0 var(--sidenav-padding);
70
- //padding-left: var(--sidenav-padding);
71
- // Truncate too long text
72
- /*
73
- min-width: 0;
74
- white-space: nowrap;
75
- overflow: hidden;
76
- text-overflow: ellipsis;
77
- */
78
174
  display: flex;
79
- height: var(--sidenav-item-height);
80
- font-size: var(--sidenav-font-size);
81
- font-weight: 500;
82
175
  align-items: center;
176
+ margin: 0 12px;
177
+ padding: 0 var(--md-comp-nav-drawer-padding);
178
+ height: var(--md-comp-nav-drawer-item-height);
83
179
  overflow: hidden;
84
180
  border-radius: 100px;
181
+ font-size: var(--md-comp-nav-drawer-font-size);
182
+ font-weight: 500;
85
183
 
86
- &:not(.btn):not(.button):not(.btn-flat):not(.btn-large):not(.btn-floating) {
184
+ &:not(.button, .circle, .extra) {
87
185
  color: var(--md-sys-color-on-secondary-container);
186
+
88
187
  &:hover {
89
- //color: var(--md-sys-color-on-secondary-container);
90
- //md.sys.color.on-secondary-container
91
188
  background-color: color-mix(in oklab, var(--md-sys-color-on-surface) 8%, transparent);
92
189
  }
190
+
191
+ @include focus-visible;
93
192
  }
94
193
 
95
- &.btn, &.button, &.btn-large, &.btn-flat, &.btn-floating {
194
+ &.button,
195
+ &.circle,
196
+ &.extra {
96
197
  margin: 10px 15px;
97
198
  }
98
199
 
99
- /* TODO: Use special class in future like "mw-icon" */
100
- & > :is(.material-symbols, .material-symbols-outlined, .material-symbols-rounded, .material-symbols-sharp, .material-icons) {
200
+ > :is(
201
+ .material-symbols,
202
+ .material-symbols-outlined,
203
+ .material-symbols-rounded,
204
+ .material-symbols-sharp,
205
+ .material-icons
206
+ ) {
101
207
  display: inline-flex;
102
- vertical-align: middle;
208
+ flex-shrink: 0;
103
209
  margin-right: 12px;
104
210
  }
105
211
  }
106
212
 
107
213
  .divider {
108
- margin: calc(var(--sidenav-padding) * 0.5) 0 0 0;
214
+ margin: calc(var(--md-comp-nav-drawer-padding) * 0.5) 0 0;
109
215
  }
110
216
 
111
217
  .subheader {
112
218
  cursor: initial;
113
219
  pointer-events: none;
114
- color: red;
115
- font-size: var(--sidenav-font-size);
220
+ color: var(--md-sys-color-on-surface-variant);
221
+ font-size: var(--md-comp-nav-drawer-font-size);
116
222
  font-weight: 500;
117
- line-height: var(--sidenav-line-height);
223
+ line-height: var(--md-comp-nav-drawer-line-height);
118
224
  }
119
225
 
120
- // Remove this? is very custom
121
226
  .user-view {
122
227
  position: relative;
123
- padding: calc(var(--sidenav-padding) * 2) calc(var(--sidenav-padding) * 2) 0;
124
- margin-bottom: calc(var(--sidenav-padding) * 0.5);
228
+ padding: calc(var(--md-comp-nav-drawer-padding) * 2) calc(var(--md-comp-nav-drawer-padding) * 2) 0;
229
+ margin-bottom: calc(var(--md-comp-nav-drawer-padding) * 0.5);
125
230
 
126
- & > a {
231
+ > a {
127
232
  height: auto;
128
233
  padding: 0;
129
234
 
@@ -135,14 +240,13 @@
135
240
  .background {
136
241
  overflow: hidden;
137
242
  position: absolute;
138
- top: 0;
139
- right: 0;
140
- bottom: 0;
141
- left: 0;
243
+ inset: 0;
142
244
  z-index: -1;
143
245
  }
144
246
 
145
- .circle, .name, .email {
247
+ .circle,
248
+ .name,
249
+ .email {
146
250
  display: block;
147
251
  }
148
252
 
@@ -153,8 +257,8 @@
153
257
 
154
258
  .name,
155
259
  .email {
156
- font-size: var(--sidenav-font-size);
157
- line-height: calc(var(--sidenav-line-height) * 0.5);
260
+ font-size: var(--md-comp-nav-drawer-font-size);
261
+ line-height: calc(var(--md-comp-nav-drawer-line-height) * 0.5);
158
262
  }
159
263
 
160
264
  .name {
@@ -169,68 +273,70 @@
169
273
  }
170
274
  }
171
275
 
172
- // Touch interaction
173
- .drag-target {
276
+ // Overlay only: the dialog is the panel, so the list fills it. When the
277
+ // host is `display: contents` (a docked .sidenav-fixed) this must not
278
+ // apply — static positioning would drop the list into the header flow
279
+ // and it would read as part of the app bar.
280
+ dialog.sidenav-overlay:modal > .sidenav {
281
+ position: static;
282
+ inset: auto;
283
+ width: 100%;
174
284
  height: 100%;
175
- position: fixed;
176
- top: 0;
177
- left: 0;
178
- z-index: 998;
179
-
180
- &.right-aligned {
181
- right: 0;
182
- }
285
+ transform: none;
286
+ box-shadow: none;
287
+ background-color: transparent;
288
+ z-index: auto;
183
289
  }
184
290
 
185
- // Fixed Sidenav shown
186
291
  .sidenav.sidenav-fixed {
187
- left: 0;
188
- transform: translateX(0);
189
- position: fixed;
292
+ transform: none;
190
293
 
191
294
  &.right-aligned {
192
- right: 0;
193
- left: auto;
295
+ transform: none;
194
296
  }
195
297
  }
196
298
 
197
- // Fixed Sidenav hide on smaller
198
299
  @include bp-down("large") {
199
- .sidenav {
200
- &.sidenav-fixed {
201
- transform: translateX(-105%);
300
+ // Uninitialized / no-js: keep a fixed drawer off-canvas. Once JS wraps
301
+ // it, the host's display:none hides the closed drawer and this must
302
+ // not also hide the list inside an open dialog.
303
+ .sidenav.sidenav-fixed {
304
+ transform: translateX(-100%);
202
305
 
203
- &.right-aligned {
204
- transform: translateX(105%);
205
- }
206
- }
207
- > a {
208
- padding: 0 var(--sidenav-padding);
209
- }
210
- .user-view {
211
- padding: var(--sidenav-padding) var(--sidenav-padding) 0;
306
+ &.right-aligned {
307
+ transform: translateX(100%);
212
308
  }
213
309
  }
214
- }
215
310
 
216
- .sidenav .collapsible-body,
217
- .sidenav details::details-content {
218
- padding: 0;
311
+ .sidenav .user-view {
312
+ padding: var(--md-comp-nav-drawer-padding) var(--md-comp-nav-drawer-padding) 0;
313
+ }
219
314
  }
220
315
 
221
- .sidenav-overlay {
316
+ .drag-target {
222
317
  position: fixed;
223
- top: 0;
224
- left: 0;
225
- right: 0;
226
- opacity: 0;
227
- height: 120vh;
228
- background-color: rgba(0, 0, 0, .5);
229
- z-index: 997;
230
- display: none;
318
+ inset: 0 auto 0 0;
319
+ width: var(--md-comp-nav-drawer-drag-width, 10px);
320
+ height: 100%;
321
+ z-index: 998;
322
+ touch-action: pan-y;
323
+
324
+ &.right-aligned {
325
+ inset: 0 0 0 auto;
326
+ }
327
+ }
328
+
329
+ @include bp-up("large") {
330
+ .drag-target[data-fixed] {
331
+ display: none;
332
+ pointer-events: none;
333
+ }
334
+ }
335
+
336
+ .sidenav details > :not(summary) {
337
+ padding: 0;
231
338
  }
232
339
 
233
- // Sidenav collapsible styling (Taken from Collapsible.scss)
234
340
  .sidenav,
235
341
  .sidenav.sidenav-fixed {
236
342
  .collapsible {
@@ -240,12 +346,12 @@
240
346
  background: transparent;
241
347
  box-shadow: none;
242
348
  }
243
- .collapsible-header,
349
+
244
350
  summary {
245
351
  border: none;
246
352
  }
247
- .collapsible-body,
248
- details::details-content {
353
+
354
+ details > :not(summary) {
249
355
  border: none;
250
356
  }
251
357
  }
@@ -1,7 +1,21 @@
1
1
  @use "../abstracts" as *;
2
2
 
3
+ // Table of contents. Paired with .scrollspy sections. The current link
4
+ // is .active and aria-current="true".
5
+ //
6
+ // Tokens:
7
+ // --md-comp-toc-label-color
8
+ // --md-comp-toc-active-color
9
+ // --md-comp-toc-indicator
10
+
3
11
  .table-of-contents {
12
+ --md-comp-toc-label-color: var(--md-sys-color-on-surface-variant);
13
+ --md-comp-toc-active-color: var(--md-sys-color-primary);
14
+ --md-comp-toc-indicator: 2px;
15
+
4
16
  list-style: none;
17
+ margin: 0;
18
+ padding: 0;
5
19
 
6
20
  &.fixed {
7
21
  position: fixed;
@@ -12,23 +26,25 @@
12
26
  }
13
27
 
14
28
  a {
15
- display: inline-block;
16
- font-weight: 400;
17
- color: var(--md-sys-color-secondary);
18
- padding-left: 16px;
19
- height: 2rem;
20
- line-height: 2rem;
21
- border-left: 1px solid var(--md-sys-color-outline-variant);
22
-
29
+ display: inline-flex;
30
+ align-items: center;
31
+ box-sizing: border-box;
32
+ min-height: 2rem;
33
+ padding-inline-start: 16px;
34
+ border-inline-start: var(--md-comp-toc-indicator) solid transparent;
35
+ color: var(--md-comp-toc-label-color);
36
+ text-decoration: none;
37
+ @include typescale("label-large");
38
+ @include focus-visible;
39
+
23
40
  &:hover {
24
- color: var(--md-sys-color-on-background);
25
- padding-left: 15px;
41
+ color: var(--md-comp-toc-active-color);
26
42
  }
27
- &.active {
28
- color: var(--md-sys-color-primary);
43
+
44
+ &:is(.active, [aria-current="true"]) {
45
+ color: var(--md-comp-toc-active-color);
29
46
  font-weight: 500;
30
- padding-left: 14px;
31
- border-left: 2px solid var(--md-sys-color-primary);
47
+ border-inline-start-color: var(--md-comp-toc-active-color);
32
48
  }
33
49
  }
34
50
  }
@@ -4,9 +4,8 @@
4
4
  //
5
5
  // Anatomy is the HTML. A <nav class="tabs"> of <a href="#panel"> is the
6
6
  // bar. A heading or <span> is the label; a leading <i> is the icon.
7
- // .active is the selected tab. ul.tabs > li.tab > a stays as an alias.
8
- // Helpers: .tabs-secondary, .horizontal / .tabs-horizontal, .max /
9
- // .tabs-fixed-width, .transparent / .tabs-transparent.
7
+ // .active is the selected tab. Helpers: .tabs-secondary, .horizontal,
8
+ // .max, .transparent.
10
9
  //
11
10
  // Tokens follow https://m3.material.io/components/tabs/specs
12
11
  // primary surface, 48dp (64 with stacked icon), title-small
@@ -18,7 +17,13 @@
18
17
  // divider 1dp outline-variant
19
18
  // disabled 38%
20
19
 
21
- $_tab: "> :is(a, .tab > a, li.tab > a)";
20
+ // Comma list of tab-link selectors. Always nest suffixes inside
21
+ // `#{$_tab} { &.foo {} }` — interpolating `#{$_tab}.foo` only
22
+ // attaches `.foo` to the last alternative and leaves `> a` bare
23
+ // (same bug as the icon size classes). That compiled the disabled
24
+ // rule as `.tabs > a, .tabs > .tab > a.disabled`, so every
25
+ // `nav.tabs > a` got pointer-events:none and could not switch panels.
26
+ $_tab: "> a, > .tab > a";
22
27
 
23
28
  .tabs {
24
29
  --md-comp-primary-tab-container-color: var(--md-sys-color-surface);
@@ -49,7 +54,7 @@ $_tab: "> :is(a, .tab > a, li.tab > a)";
49
54
  display: none;
50
55
  }
51
56
 
52
- > :is(.tab, li.tab) {
57
+ > .tab {
53
58
  display: flex;
54
59
  flex: 0 0 auto;
55
60
  list-style: none;
@@ -115,22 +120,40 @@ $_tab: "> :is(a, .tab > a, li.tab > a)";
115
120
  &.active:focus {
116
121
  background-color: color-mix(in oklab, var(--md-sys-color-primary) 10%, transparent);
117
122
  }
123
+
124
+ // Disabled — on the <a>. The wrapping `.tab.disabled` case is
125
+ // below; it cannot live in this block.
126
+ &.disabled,
127
+ &[aria-disabled="true"] {
128
+ color: color-mix(in oklab, var(--md-sys-color-on-surface) 38%, transparent);
129
+ background-color: transparent;
130
+ cursor: default;
131
+ pointer-events: none;
132
+ }
118
133
  }
119
134
 
120
135
  // Stacked icon + label is 64dp (primary).
121
- &:not(.horizontal, .tabs-horizontal) #{$_tab}:has(> i) {
122
- min-height: var(--md-comp-primary-tab-with-icon-container-height);
123
- padding-block: 8px;
136
+ &:not(.horizontal) {
137
+ #{$_tab} {
138
+ &:has(> i) {
139
+ min-height: var(--md-comp-primary-tab-with-icon-container-height);
140
+ padding-block: 8px;
141
+ }
142
+ }
124
143
  }
125
144
 
126
145
  // CSS indicator when the JS sliding bar is not present.
127
- &:not(:has(> .indicator)) #{$_tab}.active::after {
128
- content: "";
129
- position: absolute;
130
- inset: auto 0 0 0;
131
- height: var(--md-comp-primary-tab-active-indicator-height);
132
- background-color: var(--md-comp-primary-tab-active-indicator-color);
133
- border-radius: var(--md-comp-primary-tab-active-indicator-shape);
146
+ &:not(:has(> .indicator)) {
147
+ #{$_tab} {
148
+ &.active::after {
149
+ content: "";
150
+ position: absolute;
151
+ inset: auto 0 0 0;
152
+ height: var(--md-comp-primary-tab-active-indicator-height);
153
+ background-color: var(--md-comp-primary-tab-active-indicator-color);
154
+ border-radius: var(--md-comp-primary-tab-active-indicator-shape);
155
+ }
156
+ }
134
157
  }
135
158
 
136
159
  > .indicator {
@@ -145,10 +168,7 @@ $_tab: "> :is(a, .tab > a, li.tab > a)";
145
168
  margin: 0;
146
169
  }
147
170
 
148
- // Disabled — on the <a> or on the wrapping .tab.
149
- #{$_tab}.disabled,
150
- #{$_tab}[aria-disabled="true"],
151
- > :is(.tab, li.tab).disabled > a {
171
+ > .tab.disabled > a {
152
172
  color: color-mix(in oklab, var(--md-sys-color-on-surface) 38%, transparent);
153
173
  background-color: transparent;
154
174
  cursor: default;
@@ -169,8 +189,7 @@ $_tab: "> :is(a, .tab > a, li.tab > a)";
169
189
  }
170
190
 
171
191
  // Inline icon + label (primary can do this too).
172
- .tabs.horizontal,
173
- .tabs.tabs-horizontal {
192
+ .tabs.horizontal {
174
193
  #{$_tab} {
175
194
  flex-direction: row;
176
195
  gap: 8px;
@@ -180,9 +199,8 @@ $_tab: "> :is(a, .tab > a, li.tab > a)";
180
199
  }
181
200
 
182
201
  // Equal-width tabs. Default on compact so a few destinations still fill.
183
- .tabs.max,
184
- .tabs.tabs-fixed-width {
185
- > :is(a, .tab, li.tab) {
202
+ .tabs.max {
203
+ > :is(a, .tab) {
186
204
  flex: 1 1 0;
187
205
  min-width: 0;
188
206
  }
@@ -194,7 +212,7 @@ $_tab: "> :is(a, .tab > a, li.tab > a)";
194
212
 
195
213
  @include bp-down("large") {
196
214
  .tabs {
197
- > :is(a, .tab, li.tab) {
215
+ > :is(a, .tab) {
198
216
  flex: 1 1 0;
199
217
  min-width: 0;
200
218
  }
@@ -207,8 +225,7 @@ $_tab: "> :is(a, .tab > a, li.tab > a)";
207
225
  }
208
226
 
209
227
  // On a colored app bar (header.primary > .tabs).
210
- .tabs.transparent,
211
- .tabs.tabs-transparent {
228
+ .tabs.transparent {
212
229
  --md-comp-primary-tab-container-color: transparent;
213
230
  --md-comp-primary-tab-label-text-color: color-mix(
214
231
  in oklab,
@@ -219,14 +236,16 @@ $_tab: "> :is(a, .tab > a, li.tab > a)";
219
236
  --md-comp-primary-tab-active-indicator-color: var(--md-sys-color-on-primary);
220
237
  border-block-end-color: color-mix(in oklab, var(--md-sys-color-on-primary) 20%, transparent);
221
238
 
222
- #{$_tab}:hover,
223
- #{$_tab}:focus {
224
- color: var(--md-sys-color-on-primary);
225
- background-color: color-mix(in oklab, var(--md-sys-color-on-primary) 8%, transparent);
226
- }
239
+ #{$_tab} {
240
+ &:hover,
241
+ &:focus {
242
+ color: var(--md-sys-color-on-primary);
243
+ background-color: color-mix(in oklab, var(--md-sys-color-on-primary) 8%, transparent);
244
+ }
227
245
 
228
- #{$_tab}.active:hover,
229
- #{$_tab}.active:focus {
230
- background-color: color-mix(in oklab, var(--md-sys-color-on-primary) 10%, transparent);
246
+ &.active:hover,
247
+ &.active:focus {
248
+ background-color: color-mix(in oklab, var(--md-sys-color-on-primary) 10%, transparent);
249
+ }
231
250
  }
232
251
  }