Skip to content

Commit afa6400

Browse files
authored
refactor(material/snack-bar): switch to tokens API (#26933)
Reworks the snack bar to use the new tokens API.
1 parent 464aaf6 commit afa6400

File tree

5 files changed

+215
-102
lines changed

5 files changed

+215
-102
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
@use 'sass:map';
2+
@use '../../token-utils';
3+
@use '../../../theming/theming';
4+
5+
// The prefix used to generate the fully qualified name for tokens in this file.
6+
$prefix: (mat, snack-bar);
7+
8+
// Tokens that can't be configured through Angular Material's current theming API,
9+
// but may be in a future version of the theming API.
10+
@function get-unthemable-tokens() {
11+
@return ();
12+
}
13+
14+
// Tokens that can be configured through Angular Material's color theming API.
15+
@function get-color-tokens($config) {
16+
$is-dark: map.get($config, is-dark);
17+
$accent: map.get($config, accent);
18+
19+
@return (
20+
button-color: if($is-dark, rgba(0, 0, 0, 0.87), theming.get-color-from-palette($accent, text))
21+
);
22+
}
23+
24+
// Tokens that can be configured through Angular Material's typography theming API.
25+
@function get-typography-tokens($config) {
26+
@return ();
27+
}
28+
29+
// Tokens that can be configured through Angular Material's density theming API.
30+
@function get-density-tokens($config) {
31+
@return ();
32+
}
33+
34+
// Combines the tokens generated by the above functions into a single map with placeholder values.
35+
// This is used to create token slots.
36+
@function get-token-slots() {
37+
@return token-utils.merge-all(
38+
get-unthemable-tokens(),
39+
get-color-tokens(token-utils.$placeholder-color-config),
40+
get-typography-tokens(token-utils.$placeholder-typography-config),
41+
get-density-tokens(token-utils.$placeholder-density-config)
42+
);
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
@use 'sass:map';
2+
@use 'sass:meta';
3+
@use 'sass:color';
4+
@use '../../../theming/theming';
5+
@use '../../token-utils';
6+
@use '../../../typography/typography-utils';
7+
@use '../../../mdc-helpers/mdc-helpers';
8+
9+
// The prefix used to generate the fully qualified name for tokens in this file.
10+
$prefix: (mdc, snackbar);
11+
12+
// Tokens that can't be configured through Angular Material's current theming API,
13+
// but may be in a future version of the theming API.
14+
//
15+
// Tokens that are available in MDC, but not used in Angular Material should be mapped to `null`.
16+
// `null` indicates that we are intentionally choosing not to emit a slot or value for the token in
17+
// our CSS.
18+
@function get-unthemable-tokens() {
19+
@return (
20+
// Sets the snack bar border radius.
21+
container-shape: 4px,
22+
23+
// =============================================================================================
24+
// = TOKENS NOT USED IN ANGULAR MATERIAL =
25+
// =============================================================================================
26+
// Removed to match the previous appearance.
27+
supporting-text-tracking: null,
28+
29+
// Excluded because they target the wrong DOM node. See the
30+
// comments on the elevation of `.mat-mdc-snack-bar-container`.
31+
container-elevation: null,
32+
container-shadow-color: null,
33+
34+
action-focus-label-text-color: null,
35+
action-focus-state-layer-color: null,
36+
action-focus-state-layer-opacity: null,
37+
action-hover-label-text-color: null,
38+
action-hover-state-layer-color: null,
39+
action-hover-state-layer-opacity: null,
40+
action-label-text-color: null,
41+
action-label-text-font: null,
42+
action-label-text-size: null,
43+
action-label-text-tracking: null,
44+
action-label-text-weight: null,
45+
action-pressed-label-text-color: null,
46+
action-pressed-state-layer-color: null,
47+
action-pressed-state-layer-opacity: null,
48+
icon-color: null,
49+
icon-focus-icon-color: null,
50+
icon-focus-state-layer-color: null,
51+
icon-focus-state-layer-opacity: null,
52+
icon-hover-icon-color: null,
53+
icon-hover-state-layer-color: null,
54+
icon-hover-state-layer-opacity: null,
55+
icon-pressed-icon-color: null,
56+
icon-pressed-state-layer-color: null,
57+
icon-pressed-state-layer-opacity: null,
58+
icon-size: null,
59+
);
60+
}
61+
62+
// Tokens that can be configured through Angular Material's color theming API.
63+
@function get-color-tokens($config) {
64+
$background-palette: map.get($config, background);
65+
$is-dark: map.get($config, is-dark);
66+
$surface: theming.get-color-from-palette($background-palette, card);
67+
68+
@return (
69+
container-color: if(meta.type-of($surface) == color,
70+
color.mix(if($is-dark, #fff, #000), $surface, 80%), $surface),
71+
supporting-text-color: if(meta.type-of($surface) == color, rgba($surface, 0.87), $surface)
72+
);
73+
}
74+
75+
// Tokens that can be configured through Angular Material's typography theming API.
76+
@function get-typography-tokens($config) {
77+
// TODO(crisbeto): The earlier implementation of the snack bar used MDC's APIs to create the
78+
// typography tokens. As a result, we unintentionally allowed `null` typography configs to be
79+
// passed in. Since there a lot of apps that now depend on this pattern, we need this temporary
80+
// fallback.
81+
@if ($config == null) {
82+
$config: mdc-helpers.private-fallback-typography-from-mdc();
83+
}
84+
85+
@return (
86+
supporting-text-font: typography-utils.font-family($config, body-2)
87+
or typography-utils.font-family($config),
88+
supporting-text-line-height: typography-utils.line-height($config, body-2),
89+
supporting-text-size: typography-utils.font-size($config, body-2),
90+
supporting-text-weight: typography-utils.font-weight($config, body-2),
91+
);
92+
}
93+
94+
// Tokens that can be configured through Angular Material's density theming API.
95+
@function get-density-tokens($config) {
96+
@return ();
97+
}
98+
99+
// Combines the tokens generated by the above functions into a single map with placeholder values.
100+
// This is used to create token slots.
101+
@function get-token-slots() {
102+
@return token-utils.merge-all(
103+
get-unthemable-tokens(),
104+
get-color-tokens(token-utils.$placeholder-color-config),
105+
get-typography-tokens(token-utils.$placeholder-typography-config),
106+
get-density-tokens(token-utils.$placeholder-density-config)
107+
);
108+
}

src/material/core/tokens/tests/test-validate-tokens.scss

+7
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
@use '@material/radio/radio-theme' as mdc-radio-theme;
1212
@use '@material/tab-indicator/tab-indicator-theme' as mdc-tab-indicator-theme;
1313
@use '@material/tab/tab-theme' as mdc-tab-theme;
14+
@use '@material/snackbar/snackbar-theme' as mdc-snackbar-theme;
1415
@use '@material/theme/validate' as mdc-validate;
1516

1617
@use '../m2/mdc/circular-progress' as tokens-mdc-circular-progress;
@@ -23,6 +24,7 @@
2324
@use '../m2/mdc/radio' as tokens-mdc-radio;
2425
@use '../m2/mdc/tab-indicator' as tokens-mdc-tab-indicator;
2526
@use '../m2/mdc/tab' as tokens-mdc-tab;
27+
@use '../m2/mdc/snack-bar' as tokens-mdc-snack-bar;
2628

2729
@mixin validate-slots($component, $slots, $reference) {
2830
@debug 'Validating #{$component}...';
@@ -86,3 +88,8 @@
8688
$slots: tokens-mdc-tab.get-token-slots(),
8789
$reference: mdc-tab-theme.$secondary-light-theme
8890
);
91+
@include validate-slots(
92+
$component: 'm2.mdc.snackbar',
93+
$slots: tokens-mdc-snack-bar.get-token-slots(),
94+
$reference: mdc-snackbar-theme.$light-theme
95+
);

src/material/snack-bar/_snack-bar-theme.scss

+13-44
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,29 @@
1+
@use '@material/snackbar/snackbar-theme' as mdc-snackbar-theme;
12
@use '../core/theming/theming';
2-
@use '../core/mdc-helpers/mdc-helpers';
33
@use '../core/typography/typography';
4-
@use '../core/typography/typography-utils';
5-
@use '@material/theme/theme-color' as mdc-theme-color;
6-
@use '@material/snackbar/snackbar-theme' as mdc-snackbar-theme;
7-
@use 'sass:color';
8-
@use 'sass:map';
9-
@use 'sass:meta';
4+
@use '../core/tokens/token-utils';
5+
@use '../core/tokens/m2/mdc/snack-bar' as tokens-mdc-snack-bar;
6+
@use '../core/tokens/m2/mat/snack-bar' as tokens-mat-snack-bar;
107

118

129
@mixin color($config-or-theme) {
1310
$config: theming.get-color-config($config-or-theme);
14-
$is-dark-theme: map.get($config, is-dark);
15-
$accent: map.get($config, accent);
16-
17-
@include mdc-helpers.using-mdc-theme($config) {
18-
.mat-mdc-snack-bar-container {
19-
$button-color: if(
20-
$is-dark-theme,
21-
mdc-theme-color.prop-value(text-primary-on-light),
22-
theming.get-color-from-palette($accent, text)
23-
);
24-
--mat-mdc-snack-bar-button-color: #{$button-color};
25-
$on-surface: mdc-theme-color.prop-value(on-surface);
26-
$surface: mdc-theme-color.prop-value(surface);
2711

28-
@include mdc-snackbar-theme.theme((
29-
container-color: if(
30-
meta.type-of($on-surface) == color and meta.type-of($surface) == color,
31-
color.mix($on-surface, $surface, 80%),
32-
$on-surface
33-
),
34-
supporting-text-color: if(
35-
meta.type-of($surface) == color,
36-
rgba($surface, mdc-theme-color.text-emphasis(high)),
37-
$surface
38-
)
39-
));
40-
}
12+
.mat-mdc-snack-bar-container {
13+
@include mdc-snackbar-theme.theme(tokens-mdc-snack-bar.get-color-tokens($config));
14+
@include token-utils.create-token-values(
15+
tokens-mat-snack-bar.$prefix,
16+
tokens-mat-snack-bar.get-color-tokens($config)
17+
);
4118
}
4219
}
4320

4421
@mixin typography($config-or-theme) {
4522
$config: typography.private-typography-to-2018-config(
4623
theming.get-typography-config($config-or-theme));
47-
@include mdc-helpers.using-mdc-typography($config) {
48-
@if ($config) {
49-
.mat-mdc-snack-bar-container {
50-
@include mdc-snackbar-theme.theme((
51-
supporting-text-font: typography-utils.font-family($config, body-2),
52-
supporting-text-line-height: typography-utils.line-height($config, body-2),
53-
supporting-text-size: typography-utils.font-size($config, body-2),
54-
supporting-text-weight: typography-utils.font-weight($config, body-2),
55-
));
56-
}
57-
}
24+
25+
.mat-mdc-snack-bar-container {
26+
@include mdc-snackbar-theme.theme(tokens-mdc-snack-bar.get-typography-tokens($config));
5827
}
5928
}
6029

src/material/snack-bar/snack-bar-container.scss

+44-58
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
1-
@use 'sass:map';
21
@use '@material/snackbar/snackbar' as mdc-snackbar;
32
@use '@material/snackbar/snackbar-theme' as mdc-snackbar-theme;
3+
@use '@material/theme/custom-properties' as mdc-custom-properties;
4+
@use '../core/tokens/m2/mdc/snack-bar' as tokens-mdc-snack-bar;
5+
@use '../core/tokens/m2/mat/snack-bar' as tokens-mat-snack-bar;
6+
@use '../core/tokens/token-utils';
47
@use '../core/mdc-helpers/mdc-helpers';
58

6-
@include mdc-helpers.disable-mdc-fallback-declarations {
7-
// Include the styles without the animations since we
8-
// reuse the same animation as the non-MDC version.
9-
@include mdc-snackbar.static-styles($query: mdc-helpers.$mdc-base-styles-without-animation-query);
10-
}
11-
129
@mixin _container-min-width {
1310
$min-width: mdc-snackbar-theme.$min-width;
1411
$mobile-breakpoint: mdc-snackbar-theme.$mobile-breakpoint;
@@ -28,8 +25,12 @@
2825
}
2926
}
3027

31-
.mat-mdc-snack-bar-container {
32-
@include mdc-helpers.disable-mdc-fallback-declarations {
28+
@include mdc-custom-properties.configure($emit-fallback-values: false, $emit-fallback-vars: false) {
29+
// Include the styles without the animations since we
30+
// reuse the same animation as the non-MDC version.
31+
@include mdc-snackbar.static-styles($query: mdc-helpers.$mdc-base-styles-without-animation-query);
32+
33+
.mat-mdc-snack-bar-container {
3334
@include _container-min-width;
3435
@include mdc-snackbar-theme.max-width(
3536
mdc-snackbar-theme.$max-width,
@@ -45,57 +46,42 @@
4546
// a white background behind the rounded corners, because the `border-radius` is on the
4647
// surface element.
4748
@include mdc-snackbar-theme.elevation(mdc-snackbar-theme.$elevation);
48-
49-
@include mdc-snackbar-theme.theme-styles(map.merge(mdc-snackbar-theme.$light-theme, (
50-
container-color: inherit,
51-
supporting-text-color: inherit,
52-
container-shape: mdc-snackbar-theme.$shape-radius,
53-
supporting-text-font: inherit,
54-
supporting-text-line-height: inherit,
55-
supporting-text-size: inherit,
56-
supporting-text-weight: inherit,
57-
58-
// Removed to match the previous appearance.
59-
supporting-text-tracking: null,
60-
61-
// We're not using any of these since the button styling goes through `mat-button`.
62-
action-label-text-color: null,
63-
action-focus-state-layer-opacity: null,
64-
action-hover-state-layer-opacity: null,
65-
action-pressed-state-layer-opacity: null,
66-
icon-focus-state-layer-opacity: null,
67-
icon-hover-state-layer-opacity: null,
68-
icon-pressed-state-layer-opacity: null,
69-
)));
70-
}
71-
72-
// MDC sets the position as fixed and sets the container on the bottom center of the page (or
73-
// otherwise can be set to be "leading"). Our overlay handles a more advanced configuration
74-
// of positions, so we'll defer logic there.
75-
position: static;
76-
77-
// The `mat-mdc-button` and `:not(:disabled)` here are redundant, but we need them to increase
78-
// the specificity over the button styles that may bleed in from the rest of the app.
79-
.mat-mdc-button.mat-mdc-snack-bar-action:not(:disabled) {
80-
// MDC's `action-label-text-color` should be able to do this, but the button theme has a
81-
// higher specificity so it ends up overriding it. Define our own variable that we can
82-
// use to control the color instead.
83-
color: var(--mat-mdc-snack-bar-button-color, transparent);
84-
85-
// Darken the ripples in the button so they're visible against the dark background.
86-
--mat-mdc-button-persistent-ripple-color: currentColor;
87-
88-
.mat-ripple-element {
89-
background-color: currentColor;
90-
opacity: 0.1;
49+
@include mdc-snackbar-theme.theme-styles(tokens-mdc-snack-bar.get-token-slots());
50+
@include mdc-snackbar-theme.theme(tokens-mdc-snack-bar.get-unthemable-tokens());
51+
52+
// MDC sets the position as fixed and sets the container on the bottom center of the page (or
53+
// otherwise can be set to be "leading"). Our overlay handles a more advanced configuration
54+
// of positions, so we'll defer logic there.
55+
position: static;
56+
57+
// The `mat-mdc-button` and `:not(:disabled)` here are redundant, but we need them to increase
58+
// the specificity over the button styles that may bleed in from the rest of the app.
59+
.mat-mdc-button.mat-mdc-snack-bar-action:not(:disabled) {
60+
// MDC's `action-label-text-color` should be able to do this, but the button theme has a
61+
// higher specificity so it ends up overriding it. Define our own variable that we can
62+
// use to control the color instead.
63+
@include token-utils.use-tokens(
64+
tokens-mat-snack-bar.$prefix,
65+
tokens-mat-snack-bar.get-token-slots()
66+
) {
67+
@include token-utils.create-token-slot(color, button-color);
68+
}
69+
70+
// Darken the ripples in the button so they're visible against the dark background.
71+
--mat-mdc-button-persistent-ripple-color: currentColor;
72+
73+
.mat-ripple-element {
74+
background-color: currentColor;
75+
opacity: 0.1;
76+
}
9177
}
92-
}
9378

94-
// MDC uses this pseudo element to work around an issue with their live announcer, but it
95-
// can cause additional space for long snack bar messages (see #26685). Since we don't use
96-
// MDC's announcer, we can hide the element.
97-
.mdc-snackbar__label::before {
98-
display: none;
79+
// MDC uses this pseudo element to work around an issue with their live announcer, but it
80+
// can cause additional space for long snack bar messages (see #26685). Since we don't use
81+
// MDC's announcer, we can hide the element.
82+
.mdc-snackbar__label::before {
83+
display: none;
84+
}
9985
}
10086
}
10187

0 commit comments

Comments
 (0)