Skip to content

Commit cfdfa9a

Browse files
authored
fix(multiple): derive all token values from theme (#28664)
During the transition to tokens, we ended up with a bunch of places with hardcoded values in the form of `if($is-dark, #fff, #000)`. This is problematic for custom palettes, because the value is always hardcoded. These changes attempt to derive the same values from the palette directly. Fixes #26056.
1 parent 26b376e commit cfdfa9a

25 files changed

+257
-139
lines changed

src/material/core/theming/_inspection.scss

+2-2
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ $_typography-properties: (font, font-family, line-height, font-size, letter-spac
7575
@function get-theme-color($theme, $args...) {
7676
$version: get-theme-version($theme);
7777
$args-count: list.length($args);
78-
@if $args-count != 1 and $args-count != 2 {
79-
@error #{'Expected 2 or 3 arguments. Got:'} $args-count + 1;
78+
@if $args-count != 1 and $args-count != 2 and $args-count != 3 {
79+
@error #{'Expected between 2 and 4 arguments. Got:'} $args-count + 1;
8080
}
8181

8282
@if $version == 0 {

src/material/core/theming/tests/theming-inspection-api.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ describe('theming inspection api', () => {
328328
color: mat.get-theme-color($theme);
329329
}
330330
`),
331-
).toThrowError(/Expected 2 or 3 arguments. Got: 1/);
331+
).toThrowError(/Expected between 2 and 4 arguments\. Got: 1/);
332332
});
333333

334334
it('should get typography properties from theme', () => {

src/material/core/tokens/_token-utils.scss

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
@use '../theming/theming';
1010
@use '../typography/typography';
1111

12+
// Indicates whether we're building internally. Used for backwards compatibility.
13+
$private-is-internal-build: false;
14+
1215
$_placeholder-color-palette: theming.define-palette(palette.$red-palette);
1316

1417
// Placeholder color config that can be passed to token getter functions when generating token

src/material/core/tokens/m2/mat/_datepicker.scss

+1-2
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ $private-default-overlap-color: #a8dab5;
5050
$preview-outline-color: $divider-color;
5151
$today-disabled-outline-color: null;
5252
$is-dark: inspection.get-theme-type($theme) == dark;
53-
$on-surface: if($is-dark, #fff, #000);
5453

5554
$primary-color: inspection.get-theme-color($theme, primary);
5655
$range-tokens: get-range-color-tokens(private-get-range-background-color($primary-color));
@@ -75,7 +74,7 @@ $private-default-overlap-color: #a8dab5;
7574
@return sass-utils.merge-all($calendar-tokens, $toggle-tokens, $range-tokens, (
7675
toggle-icon-color: $inactive-icon-color,
7776
calendar-body-label-text-color: $secondary-text-color,
78-
calendar-period-button-text-color: $on-surface,
77+
calendar-period-button-text-color: inspection.get-theme-color($theme, foreground, text, 1),
7978
calendar-period-button-icon-color: $inactive-icon-color,
8079
calendar-navigation-button-icon-color: $inactive-icon-color,
8180
calendar-header-divider-color: $divider-color,

src/material/core/tokens/m2/mat/_fab-small.scss

+30-13
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ $prefix: (mat, fab-small);
1818
// Tokens that can be configured through Angular Material's color theming API.
1919
@function get-color-tokens($theme) {
2020
$is-dark: inspection.get-theme-type($theme) == dark;
21-
$on-surface: if($is-dark, #fff, #000);
2221
$ripple-opacities: if($is-dark,
2322
mdc-ripple-theme.$light-ink-opacities,
2423
mdc-ripple-theme.$dark-ink-opacities
@@ -29,13 +28,13 @@ $prefix: (mat, fab-small);
2928
foreground-color: inspection.get-theme-color($theme, foreground, base),
3029

3130
// Color of the element that shows the hover, focus and pressed states.
32-
state-layer-color: $on-surface,
31+
state-layer-color: inspection.get-theme-color($theme, foreground, base),
3332

3433
// Color of the element that shows the hover, focus and pressed states while disabled.
35-
disabled-state-layer-color: $on-surface,
34+
disabled-state-layer-color: inspection.get-theme-color($theme, foreground, base),
3635

3736
// Color of the ripple element.
38-
ripple-color: rgba($on-surface, 0.1),
37+
ripple-color: inspection.get-theme-color($theme, foreground, base, 0.1),
3938

4039
// Opacity of the ripple when the button is hovered.
4140
hover-state-layer-opacity: map.get($ripple-opacities, hover),
@@ -48,24 +47,42 @@ $prefix: (mat, fab-small);
4847

4948
// MDC doesn't have tokens for disabled FABs so we need to implemented them ourselves.
5049
// Background color of the container when the FAB is disabled.
51-
disabled-state-container-color: rgba($on-surface, 0.12),
50+
disabled-state-container-color: inspection.get-theme-color($theme, background, disabled-button,
51+
0.12),
5252

5353
// Color of the icons and projected text when the FAB is disabled.
54-
disabled-state-foreground-color: rgba($on-surface, if($is-dark, 0.5, 0.38)),
54+
disabled-state-foreground-color: inspection.get-theme-color($theme, foreground, disabled-button,
55+
if($is-dark, 0.5, 0.38)),
5556
);
5657
}
5758

5859
// Generates the mapping for the properties that change based on the FAB palette color.
5960
@function private-get-color-palette-color-tokens($theme, $palette-name) {
60-
$is-dark: inspection.get-theme-type($theme) == dark;
61-
$container-color: inspection.get-theme-color($theme, $palette-name);
62-
$contrast-tone: mdc-helpers.variable-safe-contrast-tone($container-color, $is-dark);
63-
$color: if($contrast-tone == 'dark', #000, #fff);
61+
// Ideally we would derive all values directly from the theme, but it causes a lot of regressions
62+
// internally. For now we fall back to the old hardcoded behavior only for internal apps.
63+
$foreground-color: null;
64+
$state-layer-color: null;
65+
$ripple-color: null;
66+
67+
@if (token-utils.$private-is-internal-build) {
68+
$is-dark: inspection.get-theme-type($theme) == dark;
69+
$container-color: inspection.get-theme-color($theme, $palette-name);
70+
$contrast-tone: mdc-helpers.variable-safe-contrast-tone($container-color, $is-dark);
71+
$color: if($contrast-tone == 'dark', #000, #fff);
72+
$foreground-color: $color;
73+
$state-layer-color: $color;
74+
$ripple-color: rgba($color, 0.1);
75+
}
76+
@else {
77+
$foreground-color: inspection.get-theme-color($theme, $palette-name, default-contrast, 1);
78+
$state-layer-color: inspection.get-theme-color($theme, $palette-name, default-contrast, 1);
79+
$ripple-color: inspection.get-theme-color($theme, $palette-name, default-contrast, 0.1);
80+
}
6481

6582
@return (
66-
foreground-color: $color,
67-
state-layer-color: $color,
68-
ripple-color: rgba($color, 0.1),
83+
foreground-color: $foreground-color,
84+
state-layer-color: $state-layer-color,
85+
ripple-color: $ripple-color,
6986
);
7087
}
7188

src/material/core/tokens/m2/mat/_fab.scss

+30-13
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ $prefix: (mat, fab);
1818
// Tokens that can be configured through Angular Material's color theming API.
1919
@function get-color-tokens($theme) {
2020
$is-dark: inspection.get-theme-type($theme) == dark;
21-
$on-surface: if($is-dark, #fff, #000);
2221
$ripple-opacities: if($is-dark,
2322
mdc-ripple-theme.$light-ink-opacities,
2423
mdc-ripple-theme.$dark-ink-opacities
@@ -29,13 +28,13 @@ $prefix: (mat, fab);
2928
foreground-color: inspection.get-theme-color($theme, foreground, base),
3029

3130
// Color of the element that shows the hover, focus and pressed states.
32-
state-layer-color: $on-surface,
31+
state-layer-color: inspection.get-theme-color($theme, foreground, base),
3332

3433
// Color of the element that shows the hover, focus and pressed states while disabled.
35-
disabled-state-layer-color: $on-surface,
34+
disabled-state-layer-color: inspection.get-theme-color($theme, foreground, base),
3635

3736
// Color of the ripple element.
38-
ripple-color: rgba($on-surface, 0.1),
37+
ripple-color: inspection.get-theme-color($theme, foreground, base, 0.1),
3938

4039
// Opacity of the ripple when the button is hovered.
4140
hover-state-layer-opacity: map.get($ripple-opacities, hover),
@@ -48,24 +47,42 @@ $prefix: (mat, fab);
4847

4948
// MDC doesn't have tokens for disabled FABs so we need to implemented them ourselves.
5049
// Background color of the container when the FAB is disabled.
51-
disabled-state-container-color: rgba($on-surface, 0.12),
50+
disabled-state-container-color: inspection.get-theme-color($theme, background, disabled-button,
51+
0.12),
5252

5353
// Color of the icons and projected text when the FAB is disabled.
54-
disabled-state-foreground-color: rgba($on-surface, if($is-dark, 0.5, 0.38)),
54+
disabled-state-foreground-color: inspection.get-theme-color($theme, foreground, disabled-button,
55+
if($is-dark, 0.5, 0.38)),
5556
);
5657
}
5758

5859
// Generates the mapping for the properties that change based on the FAB palette color.
5960
@function private-get-color-palette-color-tokens($theme, $palette-name) {
60-
$is-dark: inspection.get-theme-type($theme) == dark;
61-
$container-color: inspection.get-theme-color($theme, $palette-name);
62-
$contrast-tone: mdc-helpers.variable-safe-contrast-tone($container-color, $is-dark);
63-
$color: if($contrast-tone == 'dark', #000, #fff);
61+
// Ideally we would derive all values directly from the theme, but it causes a lot of regressions
62+
// internally. For now we fall back to the old hardcoded behavior only for internal apps.
63+
$foreground-color: null;
64+
$state-layer-color: null;
65+
$ripple-color: null;
66+
67+
@if (token-utils.$private-is-internal-build) {
68+
$is-dark: inspection.get-theme-type($theme) == dark;
69+
$container-color: inspection.get-theme-color($theme, $palette-name);
70+
$contrast-tone: mdc-helpers.variable-safe-contrast-tone($container-color, $is-dark);
71+
$color: if($contrast-tone == 'dark', #000, #fff);
72+
$foreground-color: $color;
73+
$state-layer-color: $color;
74+
$ripple-color: rgba($color, 0.1);
75+
}
76+
@else {
77+
$foreground-color: inspection.get-theme-color($theme, $palette-name, default-contrast, 1);
78+
$state-layer-color: inspection.get-theme-color($theme, $palette-name, default-contrast, 1);
79+
$ripple-color: inspection.get-theme-color($theme, $palette-name, default-contrast, 0.1);
80+
}
6481

6582
@return (
66-
foreground-color: $color,
67-
state-layer-color: $color,
68-
ripple-color: rgba($color, 0.1),
83+
foreground-color: $foreground-color,
84+
state-layer-color: $state-layer-color,
85+
ripple-color: $ripple-color,
6986
);
7087
}
7188

src/material/core/tokens/m2/mat/_filled-button.scss

+22-10
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,20 @@ $prefix: (mat, filled-button);
2828
// Tokens that can be configured through Angular Material's color theming API.
2929
@function get-color-tokens($theme) {
3030
$is-dark: inspection.get-theme-type($theme) == dark;
31-
$on-surface: if($is-dark, #fff, #000);
3231
$ripple-opacities: if($is-dark,
3332
mdc-ripple-theme.$light-ink-opacities,
3433
mdc-ripple-theme.$dark-ink-opacities
3534
);
3635

3736
@return (
3837
// Color of the element that shows the hover, focus and pressed states.
39-
state-layer-color: $on-surface,
38+
state-layer-color: inspection.get-theme-color($theme, foreground, base),
4039

4140
// Color of the element that shows the hover, focus and pressed states while disabled.
42-
disabled-state-layer-color: $on-surface,
41+
disabled-state-layer-color: inspection.get-theme-color($theme, foreground, base),
4342

4443
// Color of the ripple element.
45-
ripple-color: rgba($on-surface, 0.1),
44+
ripple-color: inspection.get-theme-color($theme, foreground, base, 0.1),
4645

4746
// Opacity of the ripple when the button is hovered.
4847
hover-state-layer-opacity: map.get($ripple-opacities, hover),
@@ -57,14 +56,27 @@ $prefix: (mat, filled-button);
5756

5857
// Generates the mapping for the properties that change based on the button palette color.
5958
@function private-get-color-palette-color-tokens($theme, $palette-name) {
60-
$is-dark: inspection.get-theme-type($theme) == dark;
61-
$container-color: inspection.get-theme-color($theme, $palette-name);
62-
$contrast-tone: mdc-helpers.variable-safe-contrast-tone($container-color, $is-dark);
63-
$color: if($contrast-tone == 'dark', #000, #fff);
59+
$state-layer-color: null;
60+
$ripple-color: null;
61+
62+
// Ideally we would derive all values directly from the theme, but it causes a lot of regressions
63+
// internally. For now we fall back to the old hardcoded behavior only for internal apps.
64+
@if (token-utils.$private-is-internal-build) {
65+
$is-dark: inspection.get-theme-type($theme) == dark;
66+
$container-color: inspection.get-theme-color($theme, $palette-name);
67+
$contrast-tone: mdc-helpers.variable-safe-contrast-tone($container-color, $is-dark);
68+
$color: if($contrast-tone == 'dark', #000, #fff);
69+
$state-layer-color: $color;
70+
$ripple-color: rgba($color, 0.1);
71+
}
72+
@else {
73+
$state-layer-color: inspection.get-theme-color($theme, $palette-name, default-contrast, 1);
74+
$ripple-color: inspection.get-theme-color($theme, $palette-name, default-contrast, 0.1);
75+
}
6476

6577
@return (
66-
state-layer-color: $color,
67-
ripple-color: rgba($color, 0.1),
78+
state-layer-color: $state-layer-color,
79+
ripple-color: $ripple-color,
6880
);
6981
}
7082

src/material/core/tokens/m2/mat/_form-field.scss

+5-5
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ $prefix: (mat, form-field);
2020
// Tokens that can be configured through Angular Material's color theming API.
2121
@function get-color-tokens($theme) {
2222
$is-dark: inspection.get-theme-type($theme) == dark;
23-
$on-surface: if($is-dark, #fff, #000);
2423
$color-tokens: private-get-color-palette-color-tokens($theme, primary);
2524

2625
@return map.merge($color-tokens, (
2726
// MDC has a token for the enabled placeholder, but not for the disabled one.
28-
disabled-input-text-placeholder-color: rgba($on-surface, 0.38),
29-
state-layer-color: rgba($on-surface, 0.87),
27+
disabled-input-text-placeholder-color:
28+
inspection.get-theme-color($theme, foreground, icon, 0.38),
29+
state-layer-color: inspection.get-theme-color($theme, foreground, base, 0.87),
3030
error-text-color: inspection.get-theme-color($theme, warn),
3131

3232
// On dark themes we set the native `select` color to some shade of white,
@@ -56,8 +56,8 @@ $prefix: (mat, form-field);
5656

5757
// These values are taken from the MDC select implementation:
5858
// https://github.com/material-components/material-components-web/blob/master/packages/mdc-select/_select-theme.scss
59-
enabled-select-arrow-color: rgba($on-surface, 0.54),
60-
disabled-select-arrow-color: rgba($on-surface, 0.38),
59+
enabled-select-arrow-color: inspection.get-theme-color($theme, foreground, icon, 0.54),
60+
disabled-select-arrow-color: inspection.get-theme-color($theme, foreground, icon, 0.38),
6161

6262
hover-state-layer-opacity: if($is-dark, 0.08, 0.04),
6363
focus-state-layer-opacity: if($is-dark, 0.24, 0.08),

src/material/core/tokens/m2/mat/_icon-button.scss

+3-4
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,20 @@ $prefix: (mat, icon-button);
1818
// Tokens that can be configured through Angular Material's color theming API.
1919
@function get-color-tokens($theme) {
2020
$is-dark: inspection.get-theme-type($theme) == dark;
21-
$on-surface: if($is-dark, #fff, #000);
2221
$ripple-opacities: if($is-dark,
2322
mdc-ripple-theme.$light-ink-opacities,
2423
mdc-ripple-theme.$dark-ink-opacities
2524
);
2625

2726
@return (
2827
// Color of the element that shows the hover, focus and pressed states.
29-
state-layer-color: $on-surface,
28+
state-layer-color: inspection.get-theme-color($theme, foreground, base),
3029

3130
// Color of the element that shows the hover, focus and pressed states while disabled.
32-
disabled-state-layer-color: $on-surface,
31+
disabled-state-layer-color: inspection.get-theme-color($theme, foreground, base),
3332

3433
// Color of the ripple element.
35-
ripple-color: rgba($on-surface, 0.1),
34+
ripple-color: inspection.get-theme-color($theme, foreground, base, 0.1),
3635

3736
// Opacity of the ripple when the button is hovered.
3837
hover-state-layer-opacity: map.get($ripple-opacities, hover),

src/material/core/tokens/m2/mat/_menu.scss

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ $prefix: (mat, menu);
2424
// Tokens that can be configured through Angular Material's color theming API.
2525
@function get-color-tokens($theme) {
2626
$is-dark: inspection.get-theme-type($theme) == dark;
27-
$on-surface: if($is-dark, #fff, #000);
28-
$active-state-layer-color: rgba($on-surface, if($is-dark, 0.08, 0.04));
27+
$active-state-layer-color: inspection.get-theme-color($theme, foreground, base,
28+
if($is-dark, 0.08, 0.04));
2929
$text-color: inspection.get-theme-color($theme, foreground, text);
3030

3131
@return (

src/material/core/tokens/m2/mat/_option.scss

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ $prefix: (mat, option);
1515
// Tokens that can be configured through Angular Material's color theming API.
1616
@function get-color-tokens($theme, $palette-name: primary) {
1717
$is-dark: inspection.get-theme-type($theme) == dark;
18-
$on-surface: if($is-dark, #fff, #000);
19-
$active-state-layer-color: rgba($on-surface, if($is-dark, 0.08, 0.04));
18+
$active-state-layer-color: inspection.get-theme-color($theme, foreground, base,
19+
if($is-dark, 0.08, 0.04));
2020

2121
@return (
2222
selected-state-label-text-color: inspection.get-theme-color($theme, $palette-name),

src/material/core/tokens/m2/mat/_outlined-button.scss

+3-4
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,20 @@ $prefix: (mat, outlined-button);
2828
// Tokens that can be configured through Angular Material's color theming API.
2929
@function get-color-tokens($theme) {
3030
$is-dark: inspection.get-theme-type($theme) == dark;
31-
$on-surface: if($is-dark, #fff, #000);
3231
$ripple-opacities: if($is-dark,
3332
mdc-ripple-theme.$light-ink-opacities,
3433
mdc-ripple-theme.$dark-ink-opacities
3534
);
3635

3736
@return (
3837
// Color of the element that shows the hover, focus and pressed states.
39-
state-layer-color: $on-surface,
38+
state-layer-color: inspection.get-theme-color($theme, foreground, base),
4039

4140
// Color of the element that shows the hover, focus and pressed states while disabled.
42-
disabled-state-layer-color: $on-surface,
41+
disabled-state-layer-color: inspection.get-theme-color($theme, foreground, base),
4342

4443
// Color of the ripple element.
45-
ripple-color: rgba($on-surface, 0.1),
44+
ripple-color: inspection.get-theme-color($theme, foreground, base, 0.1),
4645

4746
// Opacity of the ripple when the button is hovered.
4847
hover-state-layer-opacity: map.get($ripple-opacities, hover),

0 commit comments

Comments
 (0)