Skip to content

Commit 6f3ff25

Browse files
committed
Incremental theme updates for light/dark, primary/accent/warn
1 parent 04e0258 commit 6f3ff25

File tree

5 files changed

+122
-48
lines changed

5 files changed

+122
-48
lines changed

src/dev-app/theme-token-api.scss

Lines changed: 73 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
@use 'sass:map';
12
@use '@angular/material' as mat;
23
@use '@angular/material-experimental';
34

@@ -32,32 +33,84 @@ $light-theme: mat.define-light-theme((
3233
density: 0
3334
));
3435

35-
$dark-theme: mat.define-dark-theme((
36-
color: (
37-
primary: mat.define-palette(mat.$blue-grey-palette),
38-
accent: mat.define-palette(mat.$amber-palette, A200, A100, A400),
39-
warn: mat.define-palette(mat.$deep-orange-palette),
40-
),
41-
typography: mat.define-typography-config(),
42-
density: 0,
36+
// Apply all checkbox tokens (derived from `$light-theme`) to the `body` element.
37+
// This ensures that all checkboxes within the body inherit these tokens.
38+
@include material-experimental.theme($tokens: mat.m2-tokens-from-theme($light-theme), $components: (
39+
material-experimental.checkbox(),
4340
));
4441

45-
// Set up light theme.
42+
// Apply tokens related to the theme type to any element with `.demo-unicorn-dark-theme`.
43+
// This ensures that checkboxes within the element inherit the new tokens for dark theme,
44+
// rather than the ones for light theme set on `body`.
45+
.demo-unicorn-dark-theme {
46+
@include material-experimental.update-theme($components: (
47+
material-experimental.checkbox((
48+
theme-type: dark
49+
)),
50+
));
51+
}
4652

47-
@include material-experimental.theme($tokens: mat.m2-tokens-from-theme($light-theme), $components: (
48-
material-experimental.card(),
49-
material-experimental.checkbox((
50-
fill-palette: mat.define-palette(mat.$purple-palette)
51-
)),
52-
));
53+
// Apply tokens related to the color palette to any element with `.mat-primary`.
54+
// This ensures that checkboxes within the element inherit the new tokens for primary color,
55+
// rather than the any color that may have been set on an element further up the hierarchy.
56+
.mat-primary {
57+
@include material-experimental.update-theme($components: (
58+
material-experimental.checkbox((
59+
color-palette: map.get($light-theme, color, primary)
60+
)),
61+
));
62+
}
5363

54-
// Set up dark theme.
64+
// Apply tokens related to the color palette to any element with `.mat-accent`.
65+
// This ensures that checkboxes within the element inherit the new tokens for accent color,
66+
// rather than the any color that may have been set on an element further up the hierarchy.
67+
.mat-accent {
68+
@include material-experimental.update-theme($components: (
69+
material-experimental.checkbox((
70+
color-palette: map.get($light-theme, color, accent)
71+
)),
72+
));
73+
}
5574

56-
.demo-unicorn-dark-theme {
57-
@include material-experimental.theme($tokens: mat.m2-tokens-from-theme($dark-theme), $components: (
75+
// Apply tokens related to the color palette to any element with `.mat-warn`.
76+
// This ensures that checkboxes within the element inherit the new tokens for warn color,
77+
// rather than the any color that may have been set on an element further up the hierarchy.
78+
.mat-warn {
79+
@include material-experimental.update-theme($components: (
5880
material-experimental.checkbox((
59-
fill-palette: mat.define-palette(mat.$purple-palette),
60-
checkmark-color: red,
81+
color-palette: map.get($light-theme, color, warn)
6182
)),
6283
));
6384
}
85+
86+
// NOTE:
87+
// A nice feature about the theme styles defined above is that they stack well. For example:
88+
//
89+
// <body class="demo-unicorn-dark-theme">
90+
// <mat-checkbox class="mat-primary">
91+
// </body>
92+
//
93+
// The above checkbox will inherit its border color from `.demo-unicorn-dark-theme` and its fill
94+
// color from `.mat-primary` resulting in a checkbox with a white border and a primary colored fill.
95+
// It also works nicely with deeper nesting that used to be problematic. For example:
96+
//
97+
// <body>
98+
// <div class="mat-warn">
99+
// <div class="mat-accent>
100+
// <div class="mat-primary">
101+
// <mat-checkbox></mat-checkbox>
102+
// </div>
103+
// </div>
104+
// </div>
105+
// </body>
106+
//
107+
// The checkbox above is correctly colored with the primary color. This works because we don't have
108+
// to rely on rule specificity to determine which style gets applied, instead each layer of
109+
// `mat-warn`, `mat-accent`, `mat-primary` defines the custom property itself, preventing it from
110+
// being inherited from the level above.
111+
//
112+
// The key to getting this nice behavior is that the the theme mixin must not emit deeply nested
113+
// styles. It should always emit the tokens at the root selector where its `@include` is.
114+
// If the tokens were instead targeted at selectors like `body mat-checkbox`,
115+
// `.mat-primary mat-checkbox`, this would break down because we would again be depending on
116+
// specificity to decide which token value is applied.

src/material-experimental/_index.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@
66

77
// Token-based theming API
88
@forward './theming/checkbox' show checkbox;
9-
@forward './theming/theming' show theme, card, checkbox;
9+
@forward './theming/theming' show theme, update-theme, card, checkbox;
1010

1111
// Additional public APIs for individual components

src/material-experimental/theming/_checkbox.scss

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,40 +14,48 @@
1414
@return false
1515
}
1616

17-
@function _get-tokens-for-fill-palette($palette) {
17+
@function _get-tokens-for-color-palette($palette) {
1818
$accent-default: mat.get-color-from-palette($palette, default);
1919
$selected-color: mat.get-color-from-palette($palette);
2020
$checkmark-color: _contrast-tone($selected-color);
2121

2222
@return (
23-
selected-checkmark-color: $checkmark-color,
24-
selected-focus-icon-color: $selected-color,
25-
selected-hover-icon-color: $selected-color,
26-
selected-icon-color: $selected-color,
27-
selected-pressed-icon-color: $selected-color,
28-
selected-focus-state-layer-color: $accent-default,
29-
selected-hover-state-layer-color: $accent-default,
30-
selected-pressed-state-layer-color: $accent-default,
23+
mdc: (
24+
checkbox: (
25+
selected-checkmark-color: $checkmark-color,
26+
selected-focus-icon-color: $selected-color,
27+
selected-hover-icon-color: $selected-color,
28+
selected-icon-color: $selected-color,
29+
selected-pressed-icon-color: $selected-color,
30+
selected-focus-state-layer-color: $accent-default,
31+
selected-hover-state-layer-color: $accent-default,
32+
selected-pressed-state-layer-color: $accent-default,
33+
)
34+
)
3135
);
3236
}
3337

34-
@function _get-tokens-for-box-theme($theme) {
35-
$is-dark: $theme == dark;
36-
$foreground: if($id-dark, white, black);
38+
@function _get-tokens-for-theme-type($type) {
39+
$is-dark: $type == dark;
40+
$foreground: if($is-dark, white, black);
3741
$disabled-color: color.adjust($foreground, $alpha: 0.38);
3842
$border-color: color.adjust($foreground, $alpha: 0.54);
3943
$active-border-color: mat.get-color-from-palette(mat.$gray-palette, if($is-dark, 200, 900));
4044

4145
@return (
42-
disabled-selected-icon-color: $disabled-color,
43-
disabled-unselected-icon-color: $disabled-color,
44-
unselected-focus-icon-color: $active-border-color,
45-
unselected-hover-icon-color: $active-border-color,
46-
unselected-icon-color: $border-color,
47-
unselected-pressed-icon-color: $border-color,
48-
unselected-focus-state-layer-color: $foreground,
49-
unselected-hover-state-layer-color: $foreground,
50-
unselected-pressed-state-layer-color: $foreground,
46+
mdc: (
47+
checkbox: (
48+
disabled-selected-icon-color: $disabled-color,
49+
disabled-unselected-icon-color: $disabled-color,
50+
unselected-focus-icon-color: $active-border-color,
51+
unselected-hover-icon-color: $active-border-color,
52+
unselected-icon-color: $border-color,
53+
unselected-pressed-icon-color: $border-color,
54+
unselected-focus-state-layer-color: $foreground,
55+
unselected-hover-state-layer-color: $foreground,
56+
unselected-pressed-state-layer-color: $foreground,
57+
)
58+
)
5159
);
5260
}
5361

@@ -80,8 +88,8 @@ $_customization-defs: map.merge(
8088
ripple-size: state-layer-size,
8189
), (mdc, checkbox)),
8290
(
83-
fill-palette: meta.get-function(_get-tokens-for-fill-palette),
84-
box-theme: meta.get-function(_get-tokens-for-box-theme),
91+
color-palette: meta.get-function(_get-tokens-for-color-palette),
92+
theme-type: meta.get-function(_get-tokens-for-theme-type),
8593
));
8694

8795
// Configure the mat-checkbox's theme.

src/material-experimental/theming/_theming.scss

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,7 @@ $_error-on-missing-dep: false;
5656
@return $components;
5757
}
5858

59-
// Takes the full list of tokens and the list of components to configure, and outputs theme styles.
60-
@mixin theme($tokens: mat.m2-tokens-from-theme(), $components) {
61-
$components: _get-transitive-deps(mat.private-normalize-args-list($components));
59+
@mixin _theme($tokens, $components) {
6260
$tokens: map.merge($tokens, (_mat-theme-type: 'tokens'));
6361

6462
// Save the original selector when back-compat is turned on. This lets us avoid nesting styles
@@ -75,6 +73,21 @@ $_error-on-missing-dep: false;
7573
}
7674
}
7775

76+
// Takes the full list of tokens and the list of components to configure, and outputs theme styles.
77+
@mixin theme($tokens: mat.m2-tokens-from-theme(), $components) {
78+
@include _theme($tokens, _get-transitive-deps(mat.private-normalize-args-list($components)));
79+
}
80+
81+
// What should we call this?
82+
// update-theme
83+
// adjust-theme
84+
// edit-theme
85+
// override-theme
86+
// retheme
87+
@mixin update-theme($components) {
88+
@include _theme((), $components);
89+
}
90+
7891
// Configure the mat-card's theme.
7992
@function card($customizations: ()) {
8093
@return (

src/material/core/tokens/m2/_index.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
// Option to save the original theme object into the tokens map, so that theme mixins can continue
2828
// using it while migration to tokens is in progress.
29-
$_enable-back-compat-theme-access: true;
29+
$_enable-back-compat-theme-access: false;
3030

3131
// Gets the tokens for the given theme, m2 tokens module, and theming system.
3232
// Valid theming systems are: unthemable, color, typography, density.

0 commit comments

Comments
 (0)