diff --git a/src/dev-app/checkbox/checkbox-demo.html b/src/dev-app/checkbox/checkbox-demo.html
index d185850f53a0..ce19e105b890 100644
--- a/src/dev-app/checkbox/checkbox-demo.html
+++ b/src/dev-app/checkbox/checkbox-demo.html
@@ -279,3 +279,9 @@
No animations
+
+
+
+ This checkbox has special styling when using the experimental token based theme
+
+
diff --git a/src/dev-app/theme-token-api.scss b/src/dev-app/theme-token-api.scss
index 284b1312f249..d26eb2d68df4 100644
--- a/src/dev-app/theme-token-api.scss
+++ b/src/dev-app/theme-token-api.scss
@@ -1,3 +1,4 @@
+@use 'sass:map';
@use '@angular/material' as mat;
@use '@angular/material-experimental' as matx;
@@ -23,7 +24,7 @@ dev-app {
@include mat.core();
-$light-theme: mat.define-light-theme((
+$theme: mat.define-light-theme((
color: (
primary: mat.define-palette(mat.$indigo-palette),
accent: mat.define-palette(mat.$pink-palette),
@@ -32,37 +33,82 @@ $light-theme: mat.define-light-theme((
density: 0,
));
-$dark-theme: mat.define-dark-theme((
- color: (
- primary: mat.define-palette(mat.$blue-grey-palette),
- accent: mat.define-palette(mat.$amber-palette, A200, A100, A400),
- warn: mat.define-palette(mat.$deep-orange-palette),
- ),
- typography: mat.define-typography-config(),
- density: 0,
-));
-
-// Set up light theme.
-
+// Apply all tokens (derived from `$theme`) to the `html` element. This ensures that all components
+// on the page will inherit these tokens.
html {
@include matx.theme(
- $tokens: mat.m2-tokens-from-theme($light-theme),
+ $tokens: mat.m2-tokens-from-theme($theme),
$components: (
matx.card(),
matx.checkbox(),
- ));
+ )
+ );
}
-// Set up dark theme.
+// Apply tokens needed for dark theme to the element with `.demo-unicorn-dark-theme`.
+// This ensures that checkboxes within the element inherit the new tokens for dark theme,
+// rather than the ones for light theme tokens set on `body`. Note that we're not setting *all* of
+// the tokens, since many (density, typography, etc) are the same between light and dark theme.
.demo-unicorn-dark-theme {
- @include matx.theme(
- $tokens: mat.m2-tokens-from-theme($dark-theme),
- $components: (
- matx.checkbox((
- (mdc, checkbox): (
- selected-checkmark-color: red,
- )
- )),
- ));
+ @include matx.retheme((
+ // TODO(mmalerba): In the future this should be configured through `matx.system-colors()`
+ matx.checkbox((theme-type: dark)),
+ matx.card((theme-type: dark)),
+ ));
+}
+
+// Apply tokens related to the color palette to any element with `.mat-primary`, `.mat-accent`, or
+// `.mat-warn` This ensures that checkboxes within the element inherit the new tokens for the
+// appropriate palette, rather than the any color that may have been set on an element further up
+// the hierarchy. Again, rather than applying *all* the tokens, we apply only the ones effected by
+// the palette color. With this setup, the palette class need not go on the component itself
+// (e.g. ), it can go on some ancestor element and the tokens will
+// flow down. If multiple elements specify different classes, the closest one to the component will
+// take precedence.
+// (e.g. I'm primary
)
+.mat-primary {
+ @include matx.retheme((
+ matx.checkbox((
+ color-palette: map.get($theme, color, primary)
+ )),
+ ));
+}
+.mat-accent {
+ @include matx.retheme((
+ matx.checkbox((
+ color-palette: map.get($theme, color, accent)
+ )),
+ ));
+}
+.mat-warn {
+ @include matx.retheme((
+ matx.checkbox((
+ color-palette: map.get($theme, color, warn)
+ )),
+ ));
+}
+
+// Apply tokens for a completely custom checkbox that appears as an unfilled red box when unchecked,
+// and a filled green box when checked.
+.demo-traffic-light-checkbox {
+ @include matx.retheme((
+ matx.checkbox((
+ checkmark-color: transparent,
+ selected-box-color: green,
+ selected-focus-box-color: green,
+ selected-hover-box-color: green,
+ selected-pressed-box-color: green,
+ selected-focus-ring-color: green,
+ selected-hover-ring-color: green,
+ selected-pressed-ring-color: green,
+ unselected-box-color: red,
+ unselected-focus-box-color: red,
+ unselected-hover-box-color: red,
+ unselected-pressed-box-color: red,
+ unselected-focus-ring-color: red,
+ unselected-hover-ring-color: red,
+ unselected-pressed-ring-color: red,
+ ))
+ ));
}
diff --git a/src/material-experimental/_index.scss b/src/material-experimental/_index.scss
index fd2a45e8fc61..8ce0e1b965fc 100644
--- a/src/material-experimental/_index.scss
+++ b/src/material-experimental/_index.scss
@@ -5,6 +5,8 @@
popover-edit-typography, popover-edit-density, popover-edit-theme;
// Token-based theming API
-@forward './theming/theming' show theme, card, checkbox;
+@forward './theming/theming' show theme, retheme;
+@forward './theming/checkbox' show checkbox;
+@forward './theming/card' show card;
// Additional public APIs for individual components
diff --git a/src/material-experimental/theming/README.md b/src/material-experimental/theming/README.md
new file mode 100644
index 000000000000..ebe43c7a4cfd
--- /dev/null
+++ b/src/material-experimental/theming/README.md
@@ -0,0 +1,61 @@
+This is an experimental theming API based on [design tokens](https://m3.material.io/foundations/design-tokens/how-to-use-tokens). It is currently in the prototype phase,
+and still being evaluated.
+
+## Design tokens
+- Design tokens are a set of variables that determine what components look like. They can affect things like color, typography, desnity, elevation, border radius, and more.
+- Angular Material represents design tokens as CSS variables
+
+## M2 vs M3 tokens
+- Angular Material can use tokens corresponding to either the [Material Design 2](https://m2.material.io/) or [Material Design 3](https://m3.material.io/) spec
+ - Token values for M2 can be obtained by:
+ 1. Generating them from an Angular Material theme object (e.g. one defined with `mat.define-light-theme`). To generate M2 tokens for a theme, pass it to the `mat.m2-tokens-from-theme` function.
+ - Token values for M3 are not yet available
+
+Example:
+```scss
+// Create an Angular Material theme.
+$my-theme: mat.define-light-theme(...);
+
+// Create tokens for M2 from the theme.
+$m2-tokens: mat.m2-tokens-from-theme($my-theme);
+```
+## Component theme configuration functions
+- These functions are used to specify which tokens should be applied by the theming mixins _and_ to customize the tokens used in that component to something other than the value from the token set
+- So far the following component theme configuration functions have been implements:
+ - `matx.checkbox` configures tokens for the mat-checkbox to be applied
+ - `matx.card` configures tokens for the mat-card to be applied
+- The returned configurations from these functions are passed to `matx.theme` or `matx.retheme`
+- If no arguments are passed, the configuration instructs the mixin to just output the default value for all of the tokens needed by that component
+- The functions can also accept a map of customizations as an argument.
+ - Each function has its own set of supported map keys that can be used to customize the value of the underlying tokens
+ - The map keys are a higher level API then the tokens, some of the keys may result in a single token being change, but some may change multiple tokens
+ - For supported map keys (TODO: have docs for these):
+ - See `$_customization-resolvers` [here](https://github.com/angular/components/blob/main/src/material-experimental/theming/_checkbox.scss) for `matx.checkbox`
+ - See `$_customization-resolvers` [here](https://github.com/angular/components/blob/main/src/material-experimental/theming/_card.scss) for `matx.card`
+
+## Theming mixins
+- There are 2 mixins used for theming apps
+ - `matx.theme` is intended to apply the full theme for some components, with all tokens they need to function.
+ - `matx.retheme` is intended to re-apply specific tokens to change the appearance for some components by overriding the tokens applied by `matx.theme`.
+- Both mixins emit *only* CSS variables representing design tokens
+- Both mixins emit their tokens directly under the user specified selector. This gives the user complete control over the selector specificity.
+- Using `matx.theme`
+ - Takes 2 arguments:
+ - `$tokens` The set of token defaults that will be used for any tokens not explicitly customized by the component theme config
+ - `$components` List of component theme configs indicating which components to emit tokens for, and optionally, customizations for some token values
+ - Outputs *all* tokens used by the configured components
+- Using `matx.retheme`
+ - Takes 1 argument:
+ - `$components` List of component theme configs to emit customized token values for
+ - Outputs *only* the explicitly customized tokens, not any of the other tokens used by the component
+
+## Recommended theming structure
+- Apply the base token values using `matx.theme` *once*
+- Choose selectors with minimal specificity when applying tokens
+- Prefer to rely on CSS inheritance to apply token overrides rather than specificity.
+ For example if checkbox tokens are set on the root element (`html`) they will be inherited down
+ the DOM and affect any `` within the document. If checkboxes in a specific section
+ need to appear differently, say within `.dark-sidebar`, set the token overrides on the
+ `.dark-sidebar` element and they will be inherited down to the checkboxes within, instead of the
+ values from the root element.
+- For a small example, see this [alternate partial theme](https://github.com/angular/components/blob/main/src/dev-app/theme-token-api.scss) for the dev-app
diff --git a/src/material-experimental/theming/_card.scss b/src/material-experimental/theming/_card.scss
new file mode 100644
index 000000000000..7ec6148f403d
--- /dev/null
+++ b/src/material-experimental/theming/_card.scss
@@ -0,0 +1,93 @@
+@use 'sass:color';
+@use 'sass:meta';
+@use '@angular/material' as mat;
+@use './token-resolution';
+
+// TODO(mmalerba): This should live under material/card when moving out of experimental.
+
+/// Gets tokens for setting the card's shape.
+/// @param {String} $shape The card's shape.
+/// @return {Map} A map of tokens for setting the card's shape.
+// Note: we use a function rather than simple rename, because we want to map a single shape value to
+// multiple tokens, rather than offer separate shape customizations for elevated and outlined cards.
+@function _get-tokens-for-card-shape($shape) {
+ @return (
+ (mdc, elevated-card): (container-shape: $shape),
+ (mdc, outline-card): (container-shape: $shape),
+ );
+}
+
+/// Gets tokens for setting the card's color.
+/// @param {String} $shape The card's shape.
+/// @return {Map} A map of tokens for setting the card's shape.
+@function _get-tokens-for-card-color($color) {
+ @return (
+ (mdc, elevated-card): (container-color: $color),
+ (mdc, outline-card): (container-color: $color),
+ );
+}
+
+/// Gets a map of card token values that are derived from the theme type.
+/// @param {'light' | 'dark'} $theme-type The type of theme.
+/// @return {Map} A map of card token values derived from the given theme type.
+@function _get-tokens-for-theme-type($theme-type) {
+ $is-dark: $theme-type == 'dark';
+ $foreground: if($is-dark, white, black);
+ $card-color: if($is-dark, mat.get-color-from-palette(mat.$gray-palette, 800), white);
+ $outline-color: color.change($foreground, $alpha: 0.12);
+ $subtitle-color: if($is-dark, rgba(white, 0.7), rgba(black, 0.54));
+
+ @return (
+ (mdc, elevated-card): (
+ container-color: $card-color,
+ ),
+ (mdc, outlined-card): (
+ container-color: $card-color,
+ outline-color: $outline-color,
+ ),
+ (mat, card): (
+ subtitle-text-color: $subtitle-color,
+ ),
+ );
+}
+
+/// Resolvers for mat-card customizations.
+$_customization-resolvers: mat.private-merge-all(
+ token-resolution.alias((
+ elevation: container-elevation,
+ shadow-color: container-shadow-color,
+ ), (mdc, elevated-card)),
+ token-resolution.forward((
+ outline-width,
+ outline-color
+ ), (mdc, outlined-card)),
+ token-resolution.alias((
+ title-font: title-text-font,
+ title-line-height: title-text-line-height,
+ title-font-size: title-text-size,
+ title-letter-spacing: title-text-tracking,
+ title-font-weight: title-text-weight,
+ subtitle-font: subtitle-text-font,
+ subtitle-line-height: subtitle-text-line-height,
+ subtitle-font-size: subtitle-text-size,
+ subtitle-letter-spacing: subtitle-text-tracking,
+ subtitle-font-weight: subtitle-text-weight,
+ subtitle-color: subtitle-text-color
+ ), (mat, card)),
+ (
+ background-color: meta.get-function(_get-tokens-for-card-color),
+ border-radius: meta.get-function(_get-tokens-for-card-shape),
+ theme-type: meta.get-function(_get-tokens-for-theme-type),
+ )
+);
+
+/// Configure the mat-card's theme.
+/// @param {Map} $customizations [()] A map of custom values to use when theming mat-card.
+@function card($customizations: ()) {
+ @return (
+ id: 'mat.card',
+ customizations: token-resolution.resolve-customized-tokens(
+ 'mat.card', $_customization-resolvers, $customizations),
+ deps: (),
+ );
+}
diff --git a/src/material-experimental/theming/_checkbox.scss b/src/material-experimental/theming/_checkbox.scss
new file mode 100644
index 000000000000..b89b2c8e9907
--- /dev/null
+++ b/src/material-experimental/theming/_checkbox.scss
@@ -0,0 +1,109 @@
+@use 'sass:color';
+@use 'sass:map';
+@use 'sass:meta';
+@use '@angular/material' as mat;
+@use '@material/theme/theme-color' as mdc-theme-color;
+@use './token-resolution';
+
+// TODO(mmalerba): This should live under material/checkbox when moving out of experimental.
+
+// Duplicated from core/tokens/m2/mdc/checkbox
+// TODO(mmalerba): Delete duplicated code when this is moved out of experimental.
+@function _contrast-tone($value, $light-color: '#fff', $dark-color: '#000') {
+ @if ($value == 'dark' or $value == 'light' or type-of($value) == 'color') {
+ @return if(mdc-theme-color.contrast-tone($value) == 'dark', $dark-color, $light-color);
+ }
+ @return false;
+}
+
+/// Gets a map of checkbox token values that are derived from the given palette.
+/// @param {Map} $palette An Angular Material palette object.
+/// @return {Map} A map of checkbox token values derived from the given palette.
+@function _get-tokens-for-color-palette($palette) {
+ $palette-default-color: mat.get-color-from-palette($palette);
+ $checkmark-color: _contrast-tone($palette-default-color);
+
+ @return (
+ (mdc, checkbox): (
+ selected-checkmark-color: $checkmark-color,
+ selected-focus-icon-color: $palette-default-color,
+ selected-hover-icon-color: $palette-default-color,
+ selected-icon-color: $palette-default-color,
+ selected-pressed-icon-color: $palette-default-color,
+ selected-focus-state-layer-color: $palette-default-color,
+ selected-hover-state-layer-color: $palette-default-color,
+ selected-pressed-state-layer-color: $palette-default-color,
+ )
+ );
+}
+
+/// Gets a map of checkbox token values that are derived from the theme type.
+/// @param {'light' | 'dark'} $theme-type The type of theme.
+/// @return {Map} A map of checkbox token values derived from the given theme type.
+@function _get-tokens-for-theme-type($theme-type) {
+ $is-dark: $theme-type == dark;
+ $foreground: if($is-dark, white, black);
+ $disabled-color: color.change($foreground, $alpha: 0.38);
+ $border-color: color.change($foreground, $alpha: 0.54);
+ $active-border-color: mat.get-color-from-palette(mat.$gray-palette, if($is-dark, 200, 900));
+
+ @return (
+ (mdc, checkbox): (
+ disabled-selected-icon-color: $disabled-color,
+ disabled-unselected-icon-color: $disabled-color,
+ unselected-focus-icon-color: $active-border-color,
+ unselected-hover-icon-color: $active-border-color,
+ unselected-icon-color: $border-color,
+ unselected-pressed-icon-color: $border-color,
+ unselected-focus-state-layer-color: $foreground,
+ unselected-hover-state-layer-color: $foreground,
+ unselected-pressed-state-layer-color: $foreground,
+ )
+ );
+}
+
+/// Resolvers for mat-checkbox customizations.
+$_customization-resolvers: map.merge(
+ token-resolution.alias((
+ checkmark-color: selected-checkmark-color,
+ disabled-checkmark-color: disabled-selected-checkmark-color,
+ selected-focus-ring-opacity: selected-focus-state-layer-opacity,
+ selected-hover-ring-opacity: selected-hover-state-layer-opacity,
+ selected-pressed-ring-opacity: selected-pressed-state-layer-opacity,
+ unselected-focus-ring-opacity: unselected-focus-state-layer-opacity,
+ unselected-hover-ring-opacity: unselected-hover-state-layer-opacity,
+ unselected-pressed-ring-opacity: unselected-pressed-state-layer-opacity,
+ disabled-selected-box-color: disabled-selected-icon-color,
+ disabled-unselected-box-color: disabled-unselected-icon-color,
+ selected-focus-box-color: selected-focus-icon-color,
+ selected-hover-box-color: selected-hover-icon-color,
+ selected-box-color: selected-icon-color,
+ selected-pressed-box-color: selected-pressed-icon-color,
+ unselected-focus-box-color: unselected-focus-icon-color,
+ unselected-hover-box-color: unselected-hover-icon-color,
+ unselected-box-color: unselected-icon-color,
+ unselected-pressed-box-color: unselected-pressed-icon-color,
+ selected-focus-ring-color: selected-focus-state-layer-color,
+ selected-hover-ring-color: selected-hover-state-layer-color,
+ selected-pressed-ring-color: selected-pressed-state-layer-color,
+ unselected-focus-ring-color: unselected-focus-state-layer-color,
+ unselected-hover-ring-color: unselected-hover-state-layer-color,
+ unselected-pressed-ring-color: unselected-pressed-state-layer-color,
+ ripple-size: state-layer-size,
+ ), (mdc, checkbox)),
+ (
+ color-palette: meta.get-function(_get-tokens-for-color-palette),
+ theme-type: meta.get-function(_get-tokens-for-theme-type),
+ )
+);
+
+/// Configure the mat-checkbox's theme.
+/// @param {Map} $customizations [()] A map of custom values to use when theming mat-checkbox.
+@function checkbox($customizations: ()) {
+ @return (
+ id: 'mat.checkbox',
+ customizations: token-resolution.resolve-customized-tokens(
+ 'mat.checkbox', $_customization-resolvers, $customizations),
+ deps: (),
+ );
+}
diff --git a/src/material-experimental/theming/_theming.scss b/src/material-experimental/theming/_theming.scss
index 9a6b181d8a8a..1c4007da0bb1 100644
--- a/src/material-experimental/theming/_theming.scss
+++ b/src/material-experimental/theming/_theming.scss
@@ -110,25 +110,5 @@ $_error-on-missing-dep: false;
// - override-theme
// - retheme
@mixin retheme($components) {
- @include _theme((), $components);
-}
-
-/// Configure the mat-card's theme.
-/// @param {Map} $customizations [()] A map of custom token values to use when theming mat-card.
-@function card($customizations: ()) {
- @return (
- id: 'mat.card',
- customizations: $customizations,
- deps: (),
- );
-}
-
-/// Configure the mat-checkbox's theme.
-/// @param {Map} $customizations [()] A map of custom token values to use when theming mat-checkbox.
-@function checkbox($customizations: ()) {
- @return (
- id: 'mat.checkbox',
- customizations: $customizations,
- deps: (),
- );
+ @include _theme((), mat.private-coerce-to-list($components));
}
diff --git a/src/material-experimental/theming/_token-resolution.scss b/src/material-experimental/theming/_token-resolution.scss
new file mode 100644
index 000000000000..d3edcce7d729
--- /dev/null
+++ b/src/material-experimental/theming/_token-resolution.scss
@@ -0,0 +1,88 @@
+@use 'sass:list';
+@use 'sass:map';
+@use 'sass:meta';
+
+/// Creates a map of short token names to fully qualified token name under the given namespace.
+/// @param {List} $tokens A list of tokens to forward under the given namespace.
+/// @param {List} $namespace The namespace to use for the forwarded tokens.
+/// @return {Map} A map of the short token name to pairs of (namespace, token-name) representing the
+/// fully-qualified name
+/// @example
+/// forward((token1, token2), (mat, my-comp))
+/// => (
+/// token1: ((mat, my-comp), token1),
+/// token2: ((mat, my-comp), token1)
+/// )
+@function forward($tokens, $namespace) {
+ $result: ();
+ @each $token in $tokens {
+ $result: map.set($result, $token, ($namespace, $token));
+ }
+ @return $result;
+}
+
+// Creates a map of token alias names to fully qualified canonical names under the given namespace.
+/// @param {Map} $tokens A map of aliases to canonical short names for tokens under the given
+/// namespace.
+/// @param {List} $namespace The namespace to use for the canonical tokens.
+/// @return A map of the token alias name to pairs of (namespace, token-name) representing the
+/// fully-qualified canonical name of the token.
+/// @example
+/// alias((alias1: canonical1, alias2: canonical2), (mat, my-comp))
+/// => (
+/// alias1: ((mat, my-comp), canonical1),
+/// alias2: ((mat, my-comp), canonical2)
+/// )
+@function alias($tokens, $namespace) {
+ $result: ();
+ @each $from, $to in $tokens {
+ $result: map.set($result, $from, ($namespace, $to));
+ }
+ @return $result;
+}
+
+/// Gets the full set of customized tokens from a component configuration's customization map.
+/// @param {String} $component-id The id of the component whose customizations are being resolved.
+/// Used for error logging purposes.
+/// @param {Map} $customization-resolvers A map of resolvers that map customization keys to
+/// fully-qualified token names or functions to generate fully-qualified token names.
+/// @param {Map} $customizations A map of values for customization keys
+/// @return {Map} A map of fully-qualified token values
+/// @example
+/// resolve-customized-tokens('mat.checkbox',
+/// forward(my-color, my-size, (mat, my-comp)),
+/// (my-color: red, my-size: 100px)
+/// )
+/// => (
+/// (mat, my-comp): (
+/// my-color: red,
+/// my-size: 100px
+/// )
+/// )
+@function resolve-customized-tokens($component-id, $customization-resolvers, $customizations) {
+ $result: ();
+
+ @each $customization, $value in $customizations {
+ $resolver: map.get($customization-resolvers, $customization);
+ @if not $resolver {
+ @error 'Unrecognized customization for #{$component-id}: #{$customization}';
+ }
+
+ $resolver-type: meta.type-of($resolver);
+ @if $resolver-type == 'list' {
+ // If the resolver is a list, it represents the token namespace and name.
+ $key-and-value: list.append($resolver, $value);
+ $result: map.deep-merge($result, map.set((), $key-and-value...));
+ }
+ @else if $resolver-type == 'function' {
+ // If the resolver is a function, it should take a value and return a token map.
+ $result: map.deep-merge($result, meta.call($resolver, $value));
+ }
+ @else {
+ // Anything else is unexpected.
+ @error 'Invalid customization resolver for `#{$customization}` on #{$component-id}';
+ }
+ }
+
+ @return $result;
+}
diff --git a/src/material/card/_card-theme.scss b/src/material/card/_card-theme.scss
index 2f3eb3cbb71a..bef100328208 100644
--- a/src/material/card/_card-theme.scss
+++ b/src/material/card/_card-theme.scss
@@ -79,11 +79,8 @@
}
@mixin theme-from-tokens($tokens) {
- // Add values for card tokens.
- .mat-mdc-card {
- @include mdc-elevated-card-theme.theme(map.get($tokens, tokens-mdc-elevated-card.$prefix));
- @include mdc-outlined-card-theme.theme(map.get($tokens, tokens-mdc-outlined-card.$prefix));
- @include token-utils.create-token-values(
- tokens-mat-card.$prefix, map.get($tokens, tokens-mat-card.$prefix));
- }
+ @include mdc-elevated-card-theme.theme(map.get($tokens, tokens-mdc-elevated-card.$prefix));
+ @include mdc-outlined-card-theme.theme(map.get($tokens, tokens-mdc-outlined-card.$prefix));
+ @include token-utils.create-token-values(
+ tokens-mat-card.$prefix, map.get($tokens, tokens-mat-card.$prefix));
}
diff --git a/src/material/checkbox/_checkbox-theme.scss b/src/material/checkbox/_checkbox-theme.scss
index b46761feeac5..b18b0b323dcc 100644
--- a/src/material/checkbox/_checkbox-theme.scss
+++ b/src/material/checkbox/_checkbox-theme.scss
@@ -92,8 +92,5 @@
@mixin theme-from-tokens($tokens) {
// TODO(mmalerba): Some of the theme styles above are not represented in terms of tokens,
// so this mixin is currently incomplete.
-
- .mat-mdc-checkbox {
- @include mdc-checkbox-theme.theme(map.get($tokens, tokens-mdc-checkbox.$prefix));
- }
+ @include mdc-checkbox-theme.theme(map.get($tokens, tokens-mdc-checkbox.$prefix));
}
diff --git a/src/material/core/style/_sass-utils.scss b/src/material/core/style/_sass-utils.scss
index af9fb2218cef..772cfbec7b35 100644
--- a/src/material/core/style/_sass-utils.scss
+++ b/src/material/core/style/_sass-utils.scss
@@ -14,6 +14,18 @@
}
}
+/// A version of the standard `map.merge` function that takes a variable number of arguments.
+/// Each argument is merged into the final result from left to right.
+/// @param {List} $maps The maps to combine with map.merge
+/// @return {Map} The combined result of successively calling map.merge with each parameter.
+@function merge-all($maps...) {
+ $result: ();
+ @each $map in $maps {
+ $result: map.merge($result, $map);
+ }
+ @return $result;
+}
+
/// A version of the standard `map.deep-merge` function that takes a variable number of arguments.
/// Each argument is deep-merged into the final result from left to right.
/// @param {List} $maps The maps to combine with map.deep-merge