Skip to content

Commit 60aa52c

Browse files
committed
feat(material-experimental/theming): Support defining M3 theme objects
Adds support for defining a new type of theme object with more explicitly private internals. This new style theme object will be used for M3 themes.
1 parent 3f544ef commit 60aa52c

File tree

10 files changed

+757
-23
lines changed

10 files changed

+757
-23
lines changed

.ng-dev/commit-message.mts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export const commitMessage: CommitMessageConfig = {
4141
'google-maps',
4242
'material-experimental/column-resize',
4343
'material-experimental/theming-next',
44+
'material-experimental/theming',
4445
'material-experimental/menubar',
4546
'material-experimental/popover-edit',
4647
'material-experimental/selection',

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

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -23,26 +23,9 @@ dev-app {
2323

2424
@include mat.core();
2525

26-
$theme: mat.define-light-theme((
27-
color: (
28-
primary: mat.define-palette(mat.$indigo-palette),
29-
accent: mat.define-palette(mat.$pink-palette),
30-
),
31-
typography: mat.define-typography-config(),
32-
density: 0,
33-
));
26+
$m3-theme: matx.define-theme();
3427

35-
// Apply all tokens (derived from `$theme`) to the `html` element. This ensures that all components
36-
// on the page will inherit these tokens.
37-
html {
38-
@include matx.theme(
39-
matx.token-defaults(matx.get-m3-tokens()),
40-
matx.card(),
41-
matx.checkbox(),
42-
);
43-
}
28+
@debug $m3-theme;
4429

45-
// TODO(mmalerba): Figure out a consistent solution for handling dark themes & color palette
46-
// variants across M2 & M3 (likely by implementing `matx.system-colors`). As a reference, see the
47-
// prior version of this file that showed a possible way to accomplish this in M2:
48-
// https://github.com/angular/components/blob/5f5c5160dc20331619fc6729aa2ad78ac84af1c3/src/dev-app/theme-token-api.scss
30+
// @include mat.checkbox-theme($m3-theme);
31+
// @include mat.card-theme($m3-theme);

src/material-experimental/BUILD.bazel

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,19 @@ ts_library(
1818
)
1919

2020
sass_library(
21-
name = "theming_next_scss_lib",
21+
name = "theming_scss_lib",
2222
srcs = MATERIAL_EXPERIMENTAL_SCSS_LIBS + [
23+
"//src/material-experimental/theming:theming_scss_lib",
2324
"//src/material-experimental/theming-next:theming_next_scss_lib",
2425
],
2526
)
2627

2728
sass_library(
2829
name = "sass_lib",
2930
srcs = ["_index.scss"],
30-
deps = [":theming_next_scss_lib"],
31+
deps = [
32+
":theming_scss_lib",
33+
],
3134
)
3235

3336
# Generate the material-experimental `package.json` by adding all MDC dependencies

src/material-experimental/_index.scss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,8 @@
55
popover-edit-typography, popover-edit-density, popover-edit-theme;
66

77
// Token-based theming API
8+
@forward './theming/definition' show define-theme, define-colors, define-typography, define-density;
9+
@forward './theming/m3-palettes' as m3-* show $m3-red-palette, $m3-orange-palette,
10+
$m3-yellow-palette, $m3-green-palette, $m3-blue-palette, $m3-indigo-palette, $m3-violet-palette;
811

912
// Additional public APIs for individual components
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
load("//tools:defaults.bzl", "sass_library")
2+
3+
package(default_visibility = ["//visibility:public"])
4+
5+
sass_library(
6+
name = "theming_scss_lib",
7+
srcs = glob(["**/_*.scss"]),
8+
deps = ["//src/material:sass_lib"],
9+
)
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
@use 'sass:list';
2+
@use 'sass:map';
3+
@use 'sass:meta';
4+
5+
/// Validates that the object's type matches any of the expected types.
6+
/// @param {any} $obj The object to test
7+
/// @param {List} $types The expected types
8+
/// @return {String} null if no error, else the unexpected type.
9+
@function validate-type($obj, $types...) {
10+
$result: meta.type-of($obj);
11+
// A zero-length list is the same as a zero-length map.
12+
@if ($result == list and list.index($types, map) and list.length($obj) == 0) {
13+
@return null;
14+
}
15+
@return if(list.index($types, $result), null, $result);
16+
}
17+
18+
/// Validates that a map's keys contains only keys from the allowed keys list.
19+
/// @param {Map} $map The map to test
20+
/// @param {List} $keys The allowed map keys
21+
/// @return {List} null if no error, else the list of unexpected keys.
22+
@function validate-only-keys($map, $keys...) {
23+
@each $key in $keys {
24+
$map: map.remove($map, $key);
25+
}
26+
$invalid: map.keys($map);
27+
@return if(list.length($invalid) > 0, $invalid, null);
28+
}
29+
30+
/// Validates a theme config.
31+
/// @param {Map} $config The config to test.
32+
/// @return {List} null if no error, else the error message
33+
@function validate-theme-config($config) {
34+
$err: validate-type($config, 'map', 'null');
35+
@if $err {
36+
@return (#{'$config'} #{'should be a color configuraiton object. Got:'} $config);
37+
}
38+
$err: validate-only-keys($config or (), color, typography, density);
39+
@if $err {
40+
@return (#{'$config'} #{'has unexpected properties:'} $err);
41+
}
42+
$err: validate-color-config(map.get($config, color));
43+
@if $err {
44+
@return list.set-nth($err, 1, #{'#{list.nth($err, 1)}.color'});
45+
}
46+
$err: validate-typography-config(map.get($config, typography));
47+
@if $err {
48+
@return list.set-nth($err, 1, #{'#{list.nth($err, 1)}.typography'});
49+
}
50+
$err: validate-density-config(map.get($config, density));
51+
@if $err {
52+
@return list.set-nth($err, 1, #{'#{list.nth($err, 1)}.density'});
53+
}
54+
@return null;
55+
}
56+
57+
/// Validates a theme color config.
58+
/// @param {Map} $config The config to test.
59+
/// @return {List} null if no error, else the error message
60+
@function validate-color-config($config) {
61+
$err: validate-type($config, 'map', 'null');
62+
@if $err {
63+
@return (#{'$config'} #{'should be a color configuraiton object. Got:'} $config);
64+
}
65+
$err: validate-only-keys($config or (), theme-type, primary, secondary, tertiary);
66+
@if $err {
67+
@return (#{'$config'} #{'has unexpected properties:'} $err);
68+
}
69+
@return null;
70+
}
71+
72+
/// Validates a theme typography config.
73+
/// @param {Map} $config The config to test.
74+
/// @return {List} null if no error, else the error message
75+
@function validate-typography-config($config) {
76+
$err: validate-type($config, 'map', 'null');
77+
@if $err {
78+
@return (#{'$config'} #{'should be a typography configuraiton object. Got:'} $config);
79+
}
80+
$err: validate-only-keys($config or (),
81+
brand-family, plain-family, bold-weight, medium-weight, regular-weight);
82+
@if $err {
83+
@return (#{'$config'} #{'has unexpected properties:'} $err);
84+
}
85+
@return null;
86+
}
87+
88+
/// Validates a theme density config.
89+
/// @param {Map} $config The config to test.
90+
/// @return {List} null if no error, else the error message
91+
@function validate-density-config($config) {
92+
$err: validate-type($config, 'map', 'null');
93+
@if $err {
94+
@return (#{'$config'} #{'should be a density configuraiton object. Got:'} $config);
95+
}
96+
$err: validate-only-keys($config or (), scale);
97+
@if $err {
98+
@return (#{'$config'} #{'has unexpected properties:'} $err);
99+
}
100+
@return null;
101+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
@use 'sass:map';
2+
@use '@angular/material' as mat;
3+
4+
/// Sets all of the standard typography tokens for the given token base name to the given typography
5+
/// level.
6+
/// @param {Map} $systems The MDC system tokens
7+
/// @param {String} $base-name The token base name to get the typography tokens for
8+
/// @param {String} $typography-level The typography level to base the token values on
9+
/// @return {Map} A map containing the typography tokens for the given base token name
10+
@function _generate-typography-tokens($systems, $base-name, $typography-level) {
11+
$result: ();
12+
@each $prop in (font, line-height, size, tracking, weight) {
13+
$result: map.set($result, #{$base-name}-#{$prop},
14+
map.get($systems, md-sys-typescale, #{$typography-level}-#{$prop}));
15+
}
16+
@return $result;
17+
}
18+
19+
/// Generates custom tokens for the mat-card.
20+
/// @param {Map} $systems The MDC system tokens
21+
/// @param {Boolean} $exclude-hardcoded Whether to exclude hardcoded token values
22+
/// @return {Map} A set of custom tokens for the mat-card
23+
@function card($systems, $exclude-hardcoded) {
24+
@return mat.private-merge-all(
25+
_generate-typography-tokens($systems, title-text, title-large),
26+
_generate-typography-tokens($systems, subtitle-text, title-medium),
27+
(
28+
subtitle-text-color: map.get($systems, md-ref-color, on-surface)
29+
)
30+
);
31+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// This file contains functions used to define Angular Material theme objects.
2+
3+
@use 'sass:map';
4+
@use '@angular/material' as mat;
5+
@use './m3-palettes';
6+
@use './m3-tokens';
7+
@use './config-validation';
8+
9+
/// Map key used to store internals of theme config.
10+
$internals: _mat-theming-internals-do-not-access;
11+
/// The theme version of generated themes.
12+
$theme-version: 1;
13+
14+
/// Defines an Angular Material theme object with color, typography, and density settings.
15+
/// @param {Map} $config The theme configuration
16+
/// @return {Map} A theme object
17+
@function define-theme($config: ()) {
18+
$err: config-validation.validate-theme-config($config);
19+
@if $err {
20+
@error $err;
21+
}
22+
23+
@return mat.private-deep-merge-all(
24+
define-colors(map.get($config, color) or ()),
25+
define-typography(map.get($config, typography) or ()),
26+
define-density(map.get($config, density) or ()),
27+
($internals: (other-tokens: m3-tokens.generate-other-tokens())),
28+
);
29+
}
30+
31+
/// Defines an Angular Material theme object with color settings.
32+
/// @param {Map} $config The color configuration
33+
/// @return {Map} A theme object
34+
@function define-colors($config: ()) {
35+
$err: config-validation.validate-color-config($config);
36+
@if $err {
37+
@error $err;
38+
}
39+
40+
$type: map.get($config, theme-type) or light;
41+
$primary: map.get($config, primary) or m3-palettes.$indigo-palette;
42+
$secondary: map.get($config, secondary) or $primary;
43+
$tertiary: map.get($config, tertiary) or $secondary;
44+
45+
@return (
46+
$internals: (
47+
theme-version: $theme-version,
48+
theme-type: $type,
49+
color-tokens: m3-tokens.generate-color-tokens($type, $primary, $secondary, $tertiary,
50+
m3-palettes.$neutral-palette, m3-palettes.$red-palette)
51+
)
52+
);
53+
}
54+
55+
/// Defines an Angular Material theme object with typography settings.
56+
/// @param {Map} $config The typography configuration
57+
/// @return {Map} A theme object
58+
@function define-typography($config: ()) {
59+
$err: config-validation.validate-typography-config($config);
60+
@if $err {
61+
@error $err;
62+
}
63+
64+
$plain: map.get($config, plain-family) or Roboto, sans-serif;
65+
$brand: map.get($config, brand-family) or $plain;
66+
$bold: map.get($config, bold-weight) or 700;
67+
$medium: map.get($config, medium-weight) or 500;
68+
$regular: map.get($config, regular-weight) or 400;
69+
70+
@return (
71+
$internals: (
72+
theme-version: $theme-version,
73+
typography-tokens:
74+
m3-tokens.generate-typography-tokens($brand, $plain, $bold, $medium, $regular)
75+
)
76+
);
77+
}
78+
79+
/// Defines an Angular Material theme object with density settings.
80+
/// @param {Map} $config The density configuration
81+
/// @return {Map} A theme object
82+
@function define-density($config: ()) {
83+
$err: config-validation.validate-density-config($config);
84+
@if $err {
85+
@error $err;
86+
}
87+
88+
$density-scale: map.get($config, scale) or 0;
89+
90+
@return (
91+
$internals: (
92+
theme-version: $theme-version,
93+
density-scale: $density-scale,
94+
density-tokens: m3-tokens.generate-density-tokens($density-scale)
95+
)
96+
);
97+
}

0 commit comments

Comments
 (0)