@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.
- package/CHANGELOG.md +1313 -1
- package/README.md +76 -30
- package/dist/css/expressive.css +4896 -1777
- package/dist/css/expressive.css.map +1 -1
- package/dist/css/expressive.min.css +1 -1
- package/dist/css/expressive.min.css.map +1 -1
- package/dist/fonts/LICENSE-material-symbols +93 -0
- package/dist/fonts/LICENSE-noto-sans +93 -0
- package/dist/fonts/LICENSE-roboto +93 -0
- package/dist/fonts/material-symbols-outlined.woff2 +0 -0
- package/dist/fonts/material-symbols-rounded.woff2 +0 -0
- package/dist/fonts/material-symbols-sharp.woff2 +0 -0
- package/dist/fonts/noto-sans-latin-400.woff2 +0 -0
- package/dist/fonts/noto-sans-latin-500.woff2 +0 -0
- package/dist/fonts/roboto-latin-400.woff2 +0 -0
- package/dist/fonts/roboto-latin-500.woff2 +0 -0
- package/dist/js/expressive.cjs +1673 -1338
- package/dist/js/expressive.cjs.map +4 -4
- package/dist/js/expressive.js +1673 -1338
- package/dist/js/expressive.js.map +4 -4
- package/dist/js/expressive.min.js +13 -13
- package/dist/js/expressive.min.js.map +4 -4
- package/dist/js/expressive.mjs +1673 -1338
- package/dist/js/expressive.mjs.map +4 -4
- package/dist/types/behaviors/bottomSheets.d.ts +19 -11
- package/dist/types/behaviors/sheetDrag.d.ts +40 -0
- package/dist/types/behaviors/sideSheets.d.ts +2 -9
- package/dist/types/components/appBar.d.ts +26 -0
- package/dist/types/components/autocomplete.d.ts +15 -3
- package/dist/types/components/buttonGroup.d.ts +39 -0
- package/dist/types/components/buttons.d.ts +5 -1
- package/dist/types/components/cards.d.ts +11 -13
- package/dist/types/components/carousel.d.ts +87 -53
- package/dist/types/components/chips.d.ts +21 -1
- package/dist/types/components/expandingCard.d.ts +31 -0
- package/dist/types/components/index.d.ts +23 -20
- package/dist/types/components/lightbox.d.ts +1 -0
- package/dist/types/components/menu.d.ts +11 -1
- package/dist/types/components/{sidenav.d.ts → navigationDrawer.d.ts} +16 -16
- package/dist/types/components/registry.d.ts +18 -8
- package/dist/types/components/slider.d.ts +26 -85
- package/dist/types/components/snackbar.d.ts +20 -5
- package/dist/types/components/tabs.d.ts +12 -0
- package/dist/types/components/timepicker.d.ts +12 -10
- package/dist/types/components/tooltip.d.ts +1 -0
- package/dist/types/core/utils.d.ts +64 -53
- package/dist/types/index.d.ts +8 -9
- package/package.json +39 -3
- package/src/sass/README.md +61 -11
- package/src/sass/abstracts/_breakpoints.scss +17 -19
- package/src/sass/abstracts/_mixins.scss +155 -21
- package/src/sass/abstracts/_variables.scss +69 -5
- package/src/sass/base/_fonts.scss +63 -0
- package/src/sass/base/_global.scss +6 -24
- package/src/sass/base/_grid.scss +15 -12
- package/src/sass/base/_normalize.scss +34 -38
- package/src/sass/base/_typography.scss +4 -18
- package/src/sass/components/_badges.scss +3 -5
- package/src/sass/components/_banners.scss +298 -0
- package/src/sass/components/_bottom-app-bar.scss +180 -0
- package/src/sass/components/_bottom-sheet.scss +12 -4
- package/src/sass/components/_button-groups.scss +358 -0
- package/src/sass/components/_buttons.scss +245 -92
- package/src/sass/components/_cards.scss +514 -78
- package/src/sass/components/_carousel.scss +562 -68
- package/src/sass/components/_chips.scss +64 -5
- package/src/sass/components/_datepicker.scss +24 -24
- package/src/sass/components/_dialog.scss +17 -11
- package/src/sass/components/_drag-handle.scss +130 -0
- package/src/sass/components/_expanding-card.scss +226 -0
- package/src/sass/components/_fab-menu.scss +232 -0
- package/src/sass/components/_floating-sheet.scss +86 -0
- package/src/sass/components/_icon-buttons.scss +172 -0
- package/src/sass/components/_icons-material-design.scss +22 -10
- package/src/sass/components/_index.scss +22 -6
- package/src/sass/components/_list.scss +16 -13
- package/src/sass/components/_loading-indicator.scss +106 -0
- package/src/sass/components/_menu.scss +335 -61
- package/src/sass/components/_navbar.scss +245 -45
- package/src/sass/components/_navigation-bar.scss +22 -11
- package/src/sass/components/{_sidenav.scss → _navigation-drawer.scss} +123 -33
- package/src/sass/components/_navigation-rail.scss +31 -23
- package/src/sass/components/_page-footer.scss +19 -16
- package/src/sass/components/_pagination.scss +24 -6
- package/src/sass/components/_panes.scss +52 -11
- package/src/sass/components/{_preloader.scss → _progress.scss} +11 -3
- package/src/sass/components/_scrim.scss +37 -0
- package/src/sass/components/_search.scss +259 -0
- package/src/sass/components/_segmented-buttons.scss +188 -0
- package/src/sass/components/_side-sheet.scss +2 -2
- package/src/sass/components/_snackbar.scss +20 -15
- package/src/sass/components/_split-button.scss +196 -0
- package/src/sass/components/_tabs.scss +7 -8
- package/src/sass/components/_timepicker.scss +67 -58
- package/src/sass/components/_toolbar.scss +206 -80
- package/src/sass/components/_transitions.scss +8 -1
- package/src/sass/components/forms/_checkboxes.scss +49 -12
- package/src/sass/components/forms/_fieldset.scss +4 -4
- package/src/sass/components/forms/_file-input.scss +8 -1
- package/src/sass/components/forms/_index.scss +1 -1
- package/src/sass/components/forms/_input-fields.scss +50 -44
- package/src/sass/components/forms/_radio-buttons.scss +42 -10
- package/src/sass/components/forms/_select.scss +38 -1
- package/src/sass/components/forms/{_range.scss → _slider.scss} +8 -9
- package/src/sass/components/forms/_switches.scss +6 -7
- package/src/sass/expressive.scss +5 -0
- package/src/sass/tokens/_index.scss +4 -1
- package/src/sass/tokens/_reference.scss +70 -61
- package/src/sass/tokens/_state.scss +24 -0
- package/src/sass/tokens/_theme.scss +6 -3
- package/src/sass/tokens/_vibrant.scss +76 -0
- package/src/sass/utilities/_colors.scss +14 -1
- package/src/sass/utilities/_helpers.scss +5 -5
- package/src/sass/utilities/_visibility.scss +48 -26
- package/dist/types/behaviors/waves.d.ts +0 -15
- package/dist/types/components/parallax.d.ts +0 -16
- package/dist/types/components/range.d.ts +0 -43
- package/dist/types/core/bounding.d.ts +0 -6
- package/dist/types/core/edges.d.ts +0 -6
- package/src/sass/components/_parallax.scss +0 -104
- package/src/sass/components/_pulse.scss +0 -37
- package/src/sass/components/_slider.scss +0 -129
package/src/sass/README.md
CHANGED
|
@@ -65,21 +65,26 @@ fine — it crosses no boundary.
|
|
|
65
65
|
|
|
66
66
|
## Breakpoints
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
The five Material 3 window size classes live in
|
|
69
|
+
`abstracts/_breakpoints.scss` and are reached through mixins:
|
|
69
70
|
|
|
70
71
|
```scss
|
|
71
|
-
@include bp-up("
|
|
72
|
-
@include bp-down("
|
|
73
|
-
@include bp-between("
|
|
72
|
+
@include bp-up("medium") { } // width >= 600px
|
|
73
|
+
@include bp-down("expanded") { } // width < 840px
|
|
74
|
+
@include bp-between("expanded", "large") { } // 840px–1199px
|
|
74
75
|
```
|
|
75
76
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
77
|
+
| M3 class | Width | Grid prefix |
|
|
78
|
+
| --- | --- | --- |
|
|
79
|
+
| Compact | `< 600px` | `.s` (default) |
|
|
80
|
+
| Medium | `600–839px` | `.m` |
|
|
81
|
+
| Expanded | `840–1199px` | `.l` |
|
|
82
|
+
| Large | `1200–1599px` | `.xl` |
|
|
83
|
+
| Extra-large | `>= 1600px` | `.xxl` |
|
|
84
|
+
|
|
85
|
+
The grid prefixes remain unchanged for markup compatibility. At Extra-large,
|
|
86
|
+
`.container` lifts its 1280px cap to 1920px. Range syntax uses exclusive
|
|
87
|
+
comparators, so each boundary is written once without fractional max-widths.
|
|
83
88
|
|
|
84
89
|
## Theming
|
|
85
90
|
|
|
@@ -126,6 +131,51 @@ only has to set `color-scheme: dark` and all 30 tokens re-resolve — and settin
|
|
|
126
131
|
`color-scheme` on any subtree re-themes just that subtree. `color-scheme` is
|
|
127
132
|
load-bearing now; changing it is not cosmetic.
|
|
128
133
|
|
|
134
|
+
## Shadow roots
|
|
135
|
+
|
|
136
|
+
The sheet is supported as a shadow root's **only** stylesheet — adopted into
|
|
137
|
+
`shadowRoot.adoptedStyleSheets` with no copy in the document. So a rule that
|
|
138
|
+
declares a token pairs its anchor with the `:host` twin that reaches the same
|
|
139
|
+
elements from inside a shadow tree:
|
|
140
|
+
|
|
141
|
+
```scss
|
|
142
|
+
// no - :root matches the document element and nothing else, so this token
|
|
143
|
+
// is simply absent in a shadow-only load, and every declaration reading it
|
|
144
|
+
// is then invalid at computed-value time
|
|
145
|
+
:root { --md-comp-nav-drawer-width: 300px; }
|
|
146
|
+
|
|
147
|
+
// yes
|
|
148
|
+
:root, :host { --md-comp-nav-drawer-width: 300px; }
|
|
149
|
+
:root[theme='dark'], :host([theme='dark']) { color-scheme: dark; }
|
|
150
|
+
[vibrant], :host([vibrant]) { … }
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
An anchor is anything that can only match a document root or a descendant of
|
|
154
|
+
one, which is `html` and `body` as well: a shadow root has neither inside it.
|
|
155
|
+
`base/_grid.scss` put `--gap-size` on `body` and `.row` reads it without a
|
|
156
|
+
fallback, so a `.row` in a shadow-only load had an invalid `gap` rather than a
|
|
157
|
+
default one.
|
|
158
|
+
|
|
159
|
+
`color-scheme` counts as a token here: `light-dark()` resolves against the
|
|
160
|
+
element's used `color-scheme`, so an unpaired `:root[theme='dark']` means a
|
|
161
|
+
shadow tree cannot be pinned to a theme the way a page can.
|
|
162
|
+
|
|
163
|
+
`:host` matches nothing in a document, so pairing an anchor cannot change a
|
|
164
|
+
normal load. It is not weightless, though: bare `:host` matches `:root` at
|
|
165
|
+
(0,1,0), but `:host(<compound>)` adds its argument's weight on top, so
|
|
166
|
+
`:host([vibrant])` is (0,2,0) against `[vibrant]`'s (0,1,0). The two arms match
|
|
167
|
+
disjoint elements and never compete, so nothing in the sheet turns on it — but
|
|
168
|
+
do not reach for the pairing as a way to keep specificity flat.
|
|
169
|
+
|
|
170
|
+
`tests/shadow-dom.test.js` fails any rule that skips the pairing; the commitment
|
|
171
|
+
behind it is `adr/0002-shadow-only-stylesheet-adoption.md`. That commitment
|
|
172
|
+
covers everything *inside* the shadow root, portals included: the six places that
|
|
173
|
+
create an element at runtime (Tooltip, Snackbar, Lightbox's caption, the drawer's
|
|
174
|
+
drag target, and the Datepicker's two menus) route through `Utils.portalRoot()`
|
|
175
|
+
and append into the originating shadow root, so no document-level copy is needed.
|
|
176
|
+
The cost is that they no longer escape an ancestor's `overflow` or stacking
|
|
177
|
+
context — the ADR states what that re-anchors.
|
|
178
|
+
|
|
129
179
|
## Translucent colors
|
|
130
180
|
|
|
131
181
|
```scss
|
|
@@ -1,38 +1,36 @@
|
|
|
1
1
|
@use "sass:map";
|
|
2
2
|
@use "sass:list";
|
|
3
3
|
|
|
4
|
-
//
|
|
4
|
+
// Material 3 window size classes, expressed as media-query boundaries.
|
|
5
5
|
//
|
|
6
6
|
// The old form built a query by interpolating a whole string:
|
|
7
7
|
//
|
|
8
8
|
// $medium-and-down: "only screen and (max-width : 992.99px)";
|
|
9
9
|
// @media #{$medium-and-down} { ... }
|
|
10
10
|
//
|
|
11
|
-
// which needed the `.99` because
|
|
12
|
-
//
|
|
13
|
-
//
|
|
11
|
+
// which needed the `.99` because inclusive max/min queries both match on the
|
|
12
|
+
// boundary pixel. Range syntax has an exclusive comparator, so fractional
|
|
13
|
+
// values are unnecessary and every boundary is stated once:
|
|
14
14
|
//
|
|
15
|
-
// @include bp-down("
|
|
15
|
+
// @include bp-down("expanded") { ... } -> @media (width < 840px)
|
|
16
16
|
//
|
|
17
17
|
// `only screen` went with them: it existed to hide queries from Netscape 4
|
|
18
18
|
// and IE, and `screen` alone excludes print anyway - which these rules never
|
|
19
19
|
// wanted to do, since a hidden element should stay hidden on paper.
|
|
20
20
|
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
// expanded 840 ≤ width < 1200 "large" is 993, close enough
|
|
29
|
-
// large 1200 ≤ width < 1600 "xlarge"
|
|
30
|
-
// extra-large width ≥ 1600 "xxlarge"
|
|
21
|
+
// Compact starts at zero and is the unwrapped/default style. The remaining
|
|
22
|
+
// entries are the exact lower bounds from the M3 adaptive-layout spec:
|
|
23
|
+
// compact 0 ≤ width < 600
|
|
24
|
+
// medium 600 ≤ width < 840
|
|
25
|
+
// expanded 840 ≤ width < 1200
|
|
26
|
+
// large 1200 ≤ width < 1600
|
|
27
|
+
// extra-large width ≥ 1600
|
|
31
28
|
$breakpoints: (
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
29
|
+
"compact": 0px,
|
|
30
|
+
"medium": 600px,
|
|
31
|
+
"expanded": 840px,
|
|
32
|
+
"large": 1200px,
|
|
33
|
+
"extra-large": 1600px,
|
|
36
34
|
) !default;
|
|
37
35
|
|
|
38
36
|
@function breakpoint($name) {
|
|
@@ -1,14 +1,36 @@
|
|
|
1
|
+
@use "sass:list";
|
|
2
|
+
@use "sass:map";
|
|
1
3
|
@use "./elevation" as *;
|
|
2
4
|
@use "./variables" as *;
|
|
3
5
|
|
|
6
|
+
$_state-layers: ("hover", "focus", "pressed", "dragged");
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
|
|
8
|
+
/// A state layer opacity, as the percentage `color-mix()` wants.
|
|
9
|
+
///
|
|
10
|
+
/// The tokens hold numbers (0.08), because an overlay uses one as `opacity`
|
|
11
|
+
/// directly. A tint mixed with `color-mix()` needs a percentage, so it is
|
|
12
|
+
/// multiplied here rather than at forty-odd call sites.
|
|
13
|
+
///
|
|
14
|
+
/// Validated for the same reason `z-depth()` validates: a typo would produce a
|
|
15
|
+
/// `var()` naming a token nothing declares, which invalidates the whole
|
|
16
|
+
/// declaration at computed-value time - silently, and in a way nothing but the
|
|
17
|
+
/// rendered page would show.
|
|
18
|
+
@function state-opacity($state) {
|
|
19
|
+
@if not list.index($_state-layers, $state) {
|
|
20
|
+
@error "Unknown state layer #{$state}. Expected one of #{$_state-layers}.";
|
|
21
|
+
}
|
|
22
|
+
@return calc(var(--md-sys-state-#{$state}-state-layer-opacity) * 100%);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
// Common-button geometry. M3 Expressive's default size is small: 40dp,
|
|
27
|
+
// label-large, corner.full, 16dp inset, 8dp icon gap. The size axis moves
|
|
28
|
+
// those tokens; the style mixins only change color and elevation.
|
|
7
29
|
@mixin btn(
|
|
8
30
|
$height: var(--md-comp-filled-button-container-height, 40px),
|
|
9
|
-
$border-radius: var(--md-comp-filled-button-container-shape,
|
|
10
|
-
$padding-left: var(--md-comp-filled-button-leading-space,
|
|
11
|
-
$padding-right: var(--md-comp-filled-button-trailing-space,
|
|
31
|
+
$border-radius: var(--md-comp-filled-button-container-shape, 9999px),
|
|
32
|
+
$padding-left: var(--md-comp-filled-button-leading-space, 16px),
|
|
33
|
+
$padding-right: var(--md-comp-filled-button-trailing-space, 16px),
|
|
12
34
|
$font-size: var(--md-sys-typescale-label-large-font-size),
|
|
13
35
|
) {
|
|
14
36
|
box-sizing: border-box;
|
|
@@ -36,8 +58,8 @@
|
|
|
36
58
|
transition: background-color .2s ease-out, box-shadow .2s ease-out;
|
|
37
59
|
}
|
|
38
60
|
|
|
39
|
-
// State layers
|
|
40
|
-
//
|
|
61
|
+
// State layers mix the on-color into the container in oklab; the opacities come
|
|
62
|
+
// from md.sys.state via `state-opacity()`. Filled and tonal have no elevation.
|
|
41
63
|
|
|
42
64
|
@mixin btn-filled {
|
|
43
65
|
color: var(--md-comp-filled-button-label-text-color, var(--md-sys-color-on-primary));
|
|
@@ -46,7 +68,7 @@
|
|
|
46
68
|
&:hover {
|
|
47
69
|
background-color: color-mix(
|
|
48
70
|
in oklab,
|
|
49
|
-
var(--md-sys-color-on-primary)
|
|
71
|
+
var(--md-sys-color-on-primary) state-opacity("hover"),
|
|
50
72
|
var(--md-sys-color-primary)
|
|
51
73
|
);
|
|
52
74
|
}
|
|
@@ -55,7 +77,7 @@
|
|
|
55
77
|
&:focus {
|
|
56
78
|
background-color: color-mix(
|
|
57
79
|
in oklab,
|
|
58
|
-
var(--md-sys-color-on-primary)
|
|
80
|
+
var(--md-sys-color-on-primary) state-opacity("focus"),
|
|
59
81
|
var(--md-sys-color-primary)
|
|
60
82
|
);
|
|
61
83
|
}
|
|
@@ -70,7 +92,7 @@
|
|
|
70
92
|
&:hover {
|
|
71
93
|
background-color: color-mix(
|
|
72
94
|
in oklab,
|
|
73
|
-
var(--md-sys-color-on-secondary-container)
|
|
95
|
+
var(--md-sys-color-on-secondary-container) state-opacity("hover"),
|
|
74
96
|
var(--md-sys-color-secondary-container)
|
|
75
97
|
);
|
|
76
98
|
}
|
|
@@ -79,7 +101,7 @@
|
|
|
79
101
|
&:focus {
|
|
80
102
|
background-color: color-mix(
|
|
81
103
|
in oklab,
|
|
82
|
-
var(--md-sys-color-on-secondary-container)
|
|
104
|
+
var(--md-sys-color-on-secondary-container) state-opacity("focus"),
|
|
83
105
|
var(--md-sys-color-secondary-container)
|
|
84
106
|
);
|
|
85
107
|
}
|
|
@@ -94,13 +116,13 @@
|
|
|
94
116
|
|
|
95
117
|
&:hover {
|
|
96
118
|
@include z-depth("2");
|
|
97
|
-
background-color: color-mix(in oklab, var(--md-sys-color-primary)
|
|
119
|
+
background-color: color-mix(in oklab, var(--md-sys-color-primary) state-opacity("hover"), var(--md-sys-color-surface-container-low));
|
|
98
120
|
}
|
|
99
121
|
|
|
100
122
|
&:active,
|
|
101
123
|
&:focus {
|
|
102
124
|
@include z-depth("1");
|
|
103
|
-
background-color: color-mix(in oklab, var(--md-sys-color-primary)
|
|
125
|
+
background-color: color-mix(in oklab, var(--md-sys-color-primary) state-opacity("focus"), var(--md-sys-color-surface-container-low));
|
|
104
126
|
}
|
|
105
127
|
|
|
106
128
|
@include focus-visible();
|
|
@@ -109,15 +131,19 @@
|
|
|
109
131
|
@mixin btn-outlined {
|
|
110
132
|
background-color: transparent;
|
|
111
133
|
color: var(--md-sys-color-primary);
|
|
112
|
-
|
|
134
|
+
// The width is not subtracted from the inset the way `.icon-button.outlined`
|
|
135
|
+
// subtracts it: there it keeps a round control as wide as it is tall, and a
|
|
136
|
+
// common button's width is its content's. `border-box` already keeps the
|
|
137
|
+
// height right.
|
|
138
|
+
border: var(--md-comp-filled-button-outline-width, 1px) solid var(--md-sys-color-outline);
|
|
113
139
|
|
|
114
140
|
&:hover {
|
|
115
|
-
background-color: color-mix(in oklab, var(--md-sys-color-primary)
|
|
141
|
+
background-color: color-mix(in oklab, var(--md-sys-color-primary) state-opacity("hover"), transparent);
|
|
116
142
|
}
|
|
117
143
|
|
|
118
144
|
&:active,
|
|
119
145
|
&:focus {
|
|
120
|
-
background-color: color-mix(in oklab, var(--md-sys-color-primary)
|
|
146
|
+
background-color: color-mix(in oklab, var(--md-sys-color-primary) state-opacity("focus"), transparent);
|
|
121
147
|
}
|
|
122
148
|
|
|
123
149
|
@include focus-visible();
|
|
@@ -130,12 +156,12 @@
|
|
|
130
156
|
border-color: transparent;
|
|
131
157
|
|
|
132
158
|
&:hover {
|
|
133
|
-
background-color: color-mix(in oklab, var(--md-sys-color-primary)
|
|
159
|
+
background-color: color-mix(in oklab, var(--md-sys-color-primary) state-opacity("hover"), transparent);
|
|
134
160
|
}
|
|
135
161
|
|
|
136
162
|
&:active,
|
|
137
163
|
&:focus {
|
|
138
|
-
background-color: color-mix(in oklab, var(--md-sys-color-primary)
|
|
164
|
+
background-color: color-mix(in oklab, var(--md-sys-color-primary) state-opacity("focus"), transparent);
|
|
139
165
|
}
|
|
140
166
|
|
|
141
167
|
@include focus-visible();
|
|
@@ -151,21 +177,129 @@
|
|
|
151
177
|
cursor: default;
|
|
152
178
|
}
|
|
153
179
|
|
|
180
|
+
// Off the screen but not out of the document: still focusable, still read, and
|
|
181
|
+
// still a form control. `display: none` and `visibility: hidden` both take the
|
|
182
|
+
// element out of the tab order, which is the wrong trade for a control whose
|
|
183
|
+
// label is drawn elsewhere - a hidden radio that cannot be focused has no
|
|
184
|
+
// arrow-key model left.
|
|
185
|
+
//
|
|
186
|
+
// _chips.scss and _navigation-rail.scss each carry their own copy of this and
|
|
187
|
+
// predate the mixin; fold them in when either is next touched.
|
|
188
|
+
@mixin visually-hidden {
|
|
189
|
+
position: absolute;
|
|
190
|
+
width: 1px;
|
|
191
|
+
height: 1px;
|
|
192
|
+
margin: -1px;
|
|
193
|
+
padding: 0;
|
|
194
|
+
overflow: hidden;
|
|
195
|
+
clip-path: inset(50%);
|
|
196
|
+
white-space: nowrap;
|
|
197
|
+
}
|
|
198
|
+
|
|
154
199
|
// Focus with Keyboard
|
|
200
|
+
// The ring itself, for the rare selector that cannot be written as
|
|
201
|
+
// `&:focus-visible` on the focused element - a filter chip draws its ring on
|
|
202
|
+
// the <label>, but the focus is on the checkbox before it.
|
|
203
|
+
@mixin focus-ring {
|
|
204
|
+
outline: 3px solid var(--md-sys-color-secondary);
|
|
205
|
+
outline-offset: 2px;
|
|
206
|
+
}
|
|
207
|
+
|
|
155
208
|
@mixin focus-visible {
|
|
156
209
|
&:focus-visible {
|
|
157
|
-
|
|
158
|
-
outline-offset: 2px;
|
|
210
|
+
@include focus-ring();
|
|
159
211
|
}
|
|
160
212
|
}
|
|
161
213
|
|
|
162
214
|
// M3 type role. Every property must name a token tokens/_reference.scss
|
|
163
215
|
// actually defines — a missing var() drops the whole declaration.
|
|
164
216
|
@mixin typescale($role) {
|
|
165
|
-
font-family: var(--md-sys-typescale-#{$role}-font-family-name)
|
|
217
|
+
font-family: var(--md-sys-typescale-#{$role}-font-family-name);
|
|
166
218
|
font-size: var(--md-sys-typescale-#{$role}-font-size);
|
|
167
219
|
font-weight: var(--md-sys-typescale-#{$role}-font-weight);
|
|
168
220
|
line-height: var(--md-sys-typescale-#{$role}-line-height);
|
|
169
221
|
letter-spacing: var(--md-sys-typescale-#{$role}-letter-spacing);
|
|
170
222
|
}
|
|
171
223
|
|
|
224
|
+
/// The state layer some controls draw as a ring rather than an overlay.
|
|
225
|
+
///
|
|
226
|
+
/// Checkboxes and radio buttons have no room for an overlay pseudo-element, so
|
|
227
|
+
/// they grow a `box-shadow` from the control's edge out to the state layer
|
|
228
|
+
/// size. `color-mix()` wants a percentage where the token is a number, hence
|
|
229
|
+
/// the `* 100%`.
|
|
230
|
+
@mixin state-layer-ring($outer, $inner, $color, $state) {
|
|
231
|
+
box-shadow: 0 0 0 calc((#{$outer} - #{$inner}) / 2)
|
|
232
|
+
color-mix(
|
|
233
|
+
in oklab,
|
|
234
|
+
#{$color} state-opacity($state),
|
|
235
|
+
transparent
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/// The button size tokens for one rung of `$button-sizes`.
|
|
240
|
+
///
|
|
241
|
+
/// Tokens only. The type role is emitted beside the call, never from here:
|
|
242
|
+
/// `:root` takes this mixin for the default size, and a typescale on :root
|
|
243
|
+
/// would set the page's font.
|
|
244
|
+
@mixin button-size($name) {
|
|
245
|
+
$s: map.get($button-sizes, $name);
|
|
246
|
+
|
|
247
|
+
--md-comp-filled-button-container-height: #{map.get($s, height)};
|
|
248
|
+
--md-comp-filled-button-icon-size: #{map.get($s, icon)};
|
|
249
|
+
--md-comp-filled-button-leading-space: #{map.get($s, space)};
|
|
250
|
+
--md-comp-filled-button-trailing-space: #{map.get($s, space)};
|
|
251
|
+
--md-comp-filled-button-icon-label-space: #{map.get($s, gap)};
|
|
252
|
+
--md-comp-filled-button-outline-width: #{map.get($s, outline)};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/// The icon-button tokens for one rung of `$icon-button-sizes`.
|
|
256
|
+
@mixin icon-button-size($name) {
|
|
257
|
+
$s: map.get($icon-button-sizes, $name);
|
|
258
|
+
|
|
259
|
+
--md-comp-icon-button-container-height: #{map.get($s, height)};
|
|
260
|
+
--md-comp-icon-button-icon-size: #{map.get($s, icon)};
|
|
261
|
+
--md-comp-icon-button-leading-space: #{map.get($s, space)};
|
|
262
|
+
--md-comp-icon-button-trailing-space: #{map.get($s, space)};
|
|
263
|
+
--md-comp-icon-button-narrow-space: #{map.get($s, narrow)};
|
|
264
|
+
--md-comp-icon-button-wide-space: #{map.get($s, wide)};
|
|
265
|
+
--md-comp-icon-button-container-shape-square: #{map.get($s, square)};
|
|
266
|
+
--md-comp-icon-button-pressed-container-shape: #{map.get($s, pressed)};
|
|
267
|
+
--md-comp-icon-button-outline-width: #{map.get($s, outline)};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Media-covering triggers share the card state layer and focus indicator.
|
|
271
|
+
@mixin card-media-trigger {
|
|
272
|
+
position: absolute;
|
|
273
|
+
inset: 0;
|
|
274
|
+
z-index: 1;
|
|
275
|
+
display: block;
|
|
276
|
+
width: 100%;
|
|
277
|
+
height: 100%;
|
|
278
|
+
min-width: 0;
|
|
279
|
+
min-height: 0;
|
|
280
|
+
margin: 0;
|
|
281
|
+
padding: 0;
|
|
282
|
+
background: none;
|
|
283
|
+
border: 0;
|
|
284
|
+
border-radius: inherit;
|
|
285
|
+
box-shadow: none;
|
|
286
|
+
color: transparent;
|
|
287
|
+
cursor: pointer;
|
|
288
|
+
|
|
289
|
+
&:hover {
|
|
290
|
+
background-color: color-mix(in oklab, var(--md-comp-card-state-layer-color) calc(var(--md-comp-card-hover-state-layer-opacity) * 100%), transparent);
|
|
291
|
+
box-shadow: none;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
&:focus-visible {
|
|
295
|
+
background-color: color-mix(in oklab, var(--md-comp-card-state-layer-color) calc(var(--md-comp-card-focus-state-layer-opacity) * 100%), transparent);
|
|
296
|
+
box-shadow: none;
|
|
297
|
+
outline: var(--md-comp-card-focus-indicator-thickness) solid var(--md-comp-card-focus-indicator-color);
|
|
298
|
+
outline-offset: calc(-1 * var(--md-comp-card-focus-indicator-thickness));
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
&:active {
|
|
302
|
+
background-color: color-mix(in oklab, var(--md-comp-card-state-layer-color) calc(var(--md-comp-card-pressed-state-layer-opacity) * 100%), transparent);
|
|
303
|
+
box-shadow: none;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
@use "sass:math";
|
|
2
2
|
|
|
3
|
+
// "Is this child an icon, or is it the label?" - a question several components
|
|
4
|
+
// ask, and every one of them used to answer it by element: <i> was the icon,
|
|
5
|
+
// <span> was the label. That broke the day the canonical icon became a <span>,
|
|
6
|
+
// silently, in five selectors at once: icon+label buttons lost their leading
|
|
7
|
+
// inset, icon-only toolbar actions stopped being 48dp circles and picked up the
|
|
8
|
+
// text-action pill instead.
|
|
9
|
+
//
|
|
10
|
+
// So the answer lives in one place. `i` stays in the list for markup written
|
|
11
|
+
// before 0.8.0. Interpolate it inside :is() - never as a bare comma list, which
|
|
12
|
+
// breaks out of its context (see $_bar in components/_navbar).
|
|
13
|
+
$icon: ":is(i, .material-symbols, .material-symbols-outlined, .material-symbols-rounded, .material-symbols-sharp, .material-icons)" !default;
|
|
14
|
+
|
|
15
|
+
// The label beside an icon: any span that is not itself an icon.
|
|
16
|
+
$icon-label: "span:not(.material-symbols):not(.material-symbols-outlined):not(.material-symbols-rounded):not(.material-symbols-sharp):not(.material-icons)" !default;
|
|
17
|
+
|
|
3
18
|
// Colors
|
|
4
19
|
//
|
|
5
20
|
// The 2014 Material palette ($colors + colorFunc()) is gone. It generated 532
|
|
@@ -38,7 +53,7 @@ $menu-item-height: 50px !default;
|
|
|
38
53
|
|
|
39
54
|
// Media query ranges now live in abstracts/_breakpoints.scss, which states
|
|
40
55
|
// each boundary once and emits range syntax - see the note there about why
|
|
41
|
-
// the
|
|
56
|
+
// the old fractional max-width values are gone.
|
|
42
57
|
|
|
43
58
|
// Grid
|
|
44
59
|
$gutter-width: 1.5rem !default;
|
|
@@ -46,12 +61,22 @@ $element-top-margin: math.div($gutter-width, 3) !default;
|
|
|
46
61
|
$element-bottom-margin: math.div($gutter-width * 2, 3) !default;
|
|
47
62
|
|
|
48
63
|
// Typography
|
|
49
|
-
|
|
64
|
+
// M3's typeface tokens choose Roboto by default. Noto Sans is the first
|
|
65
|
+
// fallback so unsupported scripts keep a compatible visual style.
|
|
66
|
+
$font-stack: var(--md-ref-typeface-plain), var(--md-ref-typeface-fallback) !default;
|
|
67
|
+
|
|
68
|
+
// Self-hosted woff2 directory, relative to the compiled CSS file. The npm
|
|
69
|
+
// build writes CSS to dist/css/ and fonts to dist/fonts/. Override when
|
|
70
|
+
// compiling Sass to a different location. Set $expressive-include-fonts to
|
|
71
|
+
// false to emit no @font-face rules (for example when the page already
|
|
72
|
+
// loads these families from elsewhere).
|
|
73
|
+
$expressive-font-path: "../fonts" !default;
|
|
74
|
+
$expressive-include-fonts: true !default;
|
|
50
75
|
$text-color: var(--md-sys-color-on-background) !default;
|
|
51
76
|
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
$root-font-size:
|
|
77
|
+
// Modern browsers default to 16px. M3's web conversion is SP_SIZE / 16 = rem;
|
|
78
|
+
// flow text uses the same basis without overriding the user's browser setting.
|
|
79
|
+
$root-font-size: 16px !default;
|
|
55
80
|
|
|
56
81
|
// Flow text: font-size interpolates between these two across the viewport
|
|
57
82
|
// range below. This used to be a @while loop emitting 21 media queries.
|
|
@@ -59,3 +84,42 @@ $flow-text-min: 1.2rem !default;
|
|
|
59
84
|
$flow-text-max: 1.68rem !default; // the old loop's ceiling: 1.2rem * (1 + .02 * 20)
|
|
60
85
|
$flow-text-from: 360px !default;
|
|
61
86
|
$flow-text-to: 960px !default; // the old loop's last step: 360px + 20 * 30px
|
|
87
|
+
|
|
88
|
+
// The common-button size axis, md.comp.button.{xsmall…xlarge} (DSP 34.0.21).
|
|
89
|
+
//
|
|
90
|
+
// size height icon space gap label outline
|
|
91
|
+
// xsmall 32 20 12 8 label-large 1
|
|
92
|
+
// small 40 20 16 8 label-large 1 <- default
|
|
93
|
+
// medium 56 24 24 8 title-medium 1
|
|
94
|
+
// large 96 32 48 12 headline-small 2
|
|
95
|
+
// xlarge 136 40 64 16 headline-large 3
|
|
96
|
+
//
|
|
97
|
+
// `space` is both the leading and the trailing inset. Expressive dropped the
|
|
98
|
+
// old "24dp, but 16dp next to an icon" asymmetry, so a button is symmetric at
|
|
99
|
+
// every size, whatever is inside it. The round shape is corner.full at every
|
|
100
|
+
// size, so it is declared once by the component rather than five times here.
|
|
101
|
+
// `outline` is the one number the style axis reads back: M3 thickens an
|
|
102
|
+
// outlined button's border with its size.
|
|
103
|
+
//
|
|
104
|
+
// It lives in abstracts because two components read it - `_buttons.scss` for
|
|
105
|
+
// the size classes, `_button-groups.scss` to size the items of a group from
|
|
106
|
+
// the group itself. A partial may not @use another partial, so a table two of
|
|
107
|
+
// them share has to be here.
|
|
108
|
+
$button-sizes: (
|
|
109
|
+
"xsmall": (height: 32px, icon: 20px, space: 12px, gap: 8px, outline: 1px, label: "label-large"),
|
|
110
|
+
"small": (height: 40px, icon: 20px, space: 16px, gap: 8px, outline: 1px, label: "label-large"),
|
|
111
|
+
"medium": (height: 56px, icon: 24px, space: 24px, gap: 8px, outline: 1px, label: "title-medium"),
|
|
112
|
+
"large": (height: 96px, icon: 32px, space: 48px, gap: 12px, outline: 2px, label: "headline-small"),
|
|
113
|
+
"xlarge": (height: 136px, icon: 40px, space: 64px, gap: 16px, outline: 3px, label: "headline-large"),
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
// Icon-button geometry is shared with button groups. A group-size class must
|
|
117
|
+
// reach both common buttons and icon buttons without asking authors to repeat
|
|
118
|
+
// the size on every child.
|
|
119
|
+
$icon-button-sizes: (
|
|
120
|
+
"xsmall": (height: 32px, icon: 20px, space: 6px, narrow: 4px, wide: 10px, square: 12px, pressed: 8px, outline: 1px),
|
|
121
|
+
"small": (height: 40px, icon: 24px, space: 8px, narrow: 4px, wide: 14px, square: 12px, pressed: 8px, outline: 1px),
|
|
122
|
+
"medium": (height: 56px, icon: 24px, space: 16px, narrow: 12px, wide: 24px, square: 16px, pressed: 12px, outline: 1px),
|
|
123
|
+
"large": (height: 96px, icon: 32px, space: 32px, narrow: 16px, wide: 48px, square: 28px, pressed: 16px, outline: 2px),
|
|
124
|
+
"xlarge": (height: 136px, icon: 40px, space: 48px, narrow: 32px, wide: 72px, square: 28px, pressed: 16px, outline: 3px),
|
|
125
|
+
);
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
@use "../abstracts" as *;
|
|
2
|
+
|
|
3
|
+
// Self-hosted faces for the families the tokens and icon classes name.
|
|
4
|
+
// Unlayered on purpose: @font-face inside a cascade layer is unreliable.
|
|
5
|
+
// $expressive-font-path is relative to the compiled CSS (dist/css -> dist/fonts).
|
|
6
|
+
|
|
7
|
+
@if $expressive-include-fonts {
|
|
8
|
+
@font-face {
|
|
9
|
+
font-family: "Material Symbols Outlined";
|
|
10
|
+
font-style: normal;
|
|
11
|
+
font-weight: 100 700;
|
|
12
|
+
font-display: block;
|
|
13
|
+
src: url("#{$expressive-font-path}/material-symbols-outlined.woff2") format("woff2");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@font-face {
|
|
17
|
+
font-family: "Material Symbols Rounded";
|
|
18
|
+
font-style: normal;
|
|
19
|
+
font-weight: 100 700;
|
|
20
|
+
font-display: block;
|
|
21
|
+
src: url("#{$expressive-font-path}/material-symbols-rounded.woff2") format("woff2");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@font-face {
|
|
25
|
+
font-family: "Material Symbols Sharp";
|
|
26
|
+
font-style: normal;
|
|
27
|
+
font-weight: 100 700;
|
|
28
|
+
font-display: block;
|
|
29
|
+
src: url("#{$expressive-font-path}/material-symbols-sharp.woff2") format("woff2");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@font-face {
|
|
33
|
+
font-family: "Roboto";
|
|
34
|
+
font-style: normal;
|
|
35
|
+
font-weight: 400;
|
|
36
|
+
font-display: swap;
|
|
37
|
+
src: url("#{$expressive-font-path}/roboto-latin-400.woff2") format("woff2");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@font-face {
|
|
41
|
+
font-family: "Roboto";
|
|
42
|
+
font-style: normal;
|
|
43
|
+
font-weight: 500;
|
|
44
|
+
font-display: swap;
|
|
45
|
+
src: url("#{$expressive-font-path}/roboto-latin-500.woff2") format("woff2");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@font-face {
|
|
49
|
+
font-family: "Noto Sans";
|
|
50
|
+
font-style: normal;
|
|
51
|
+
font-weight: 400;
|
|
52
|
+
font-display: swap;
|
|
53
|
+
src: url("#{$expressive-font-path}/noto-sans-latin-400.woff2") format("woff2");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@font-face {
|
|
57
|
+
font-family: "Noto Sans";
|
|
58
|
+
font-style: normal;
|
|
59
|
+
font-weight: 500;
|
|
60
|
+
font-display: swap;
|
|
61
|
+
src: url("#{$expressive-font-path}/noto-sans-latin-500.woff2") format("woff2");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -35,30 +35,13 @@ a {
|
|
|
35
35
|
// Blockquote
|
|
36
36
|
blockquote { margin: 20px 0; padding-left: 1.5rem; border-left: 5px solid var(--md-sys-color-primary); }
|
|
37
37
|
|
|
38
|
-
//
|
|
38
|
+
// <i> as an element - italics. The icon modifiers (.left, .right, .tiny ..)
|
|
39
|
+
// used to live here, keyed on `i`, which silently tied them to one element:
|
|
40
|
+
// writing an icon as the canonical <span class="material-symbols"> dropped
|
|
41
|
+
// them. They now hang off the icon class list in components/_icons-material-design,
|
|
42
|
+
// where the size modifiers already were, so they follow the class instead.
|
|
39
43
|
i {
|
|
40
44
|
line-height: inherit;
|
|
41
|
-
&.left {
|
|
42
|
-
float: left;
|
|
43
|
-
margin-left: -8px;
|
|
44
|
-
// margin-right: 15px;
|
|
45
|
-
}
|
|
46
|
-
&.right {
|
|
47
|
-
float: right;
|
|
48
|
-
// margin-left: 15px;
|
|
49
|
-
}
|
|
50
|
-
&.tiny {
|
|
51
|
-
font-size: 1rem;
|
|
52
|
-
}
|
|
53
|
-
&.small {
|
|
54
|
-
font-size: 2rem;
|
|
55
|
-
}
|
|
56
|
-
&.medium {
|
|
57
|
-
font-size: 4rem;
|
|
58
|
-
}
|
|
59
|
-
&.large {
|
|
60
|
-
font-size: 6rem;
|
|
61
|
-
}
|
|
62
45
|
}
|
|
63
46
|
|
|
64
47
|
// Tables
|
|
@@ -113,7 +96,7 @@ td, th {
|
|
|
113
96
|
}
|
|
114
97
|
|
|
115
98
|
// Responsive Table
|
|
116
|
-
@include bp-down("
|
|
99
|
+
@include bp-down("expanded") {
|
|
117
100
|
|
|
118
101
|
table.responsive-table {
|
|
119
102
|
width: 100%;
|
|
@@ -178,4 +161,3 @@ td, th {
|
|
|
178
161
|
}
|
|
179
162
|
}
|
|
180
163
|
}
|
|
181
|
-
|