@use '../../button-toggle/m3-button-toggle'; @use '../../button/m3-button'; @use '../../button/m3-fab'; @use '../../button/m3-icon-button'; @use '../../checkbox/m3-checkbox'; @use '../../chips/m3-chip'; @use '../../expansion/m3-expansion'; @use '../../form-field/m3-form-field'; @use '../../list/m3-list'; @use '../../paginator/m3-paginator'; @use '../../radio/m3-radio'; @use '../../select/m3-select'; @use '../../stepper/m3-stepper'; @use '../../table/m3-table'; @use '../../tabs/m3-tabs'; @use '../../toolbar/m3-toolbar'; @use '../../tree/m3-tree'; @use '../style/elevation'; @use '../theming/config-validation'; @use '../theming/definition'; @use '../theming/palettes'; @use './m3'; @use 'sass:map'; @use 'sass:meta'; /// Emits necessary CSS variables for Material's system level values for the values defined in the /// config map. The config map can have values color, typography, and/or density. /// /// If the config map's color value is an Angular Material color palette, it will be used as the /// primary and tertiary colors with a `color-scheme` theme type. Otherwise if the color value is a /// map, it must have a `primary` value containing an Angular Material color palette, and /// optionally a different `tertiary` palette (defaults to primary palette) and `theme-type` that /// is either `light`, `dark`, or 'color-scheme` (defaults to `color-scheme`). Color variable /// definitions will not be emitted if there are no color values in the config. /// /// If the config map's typography value is a font family string, it will be used as the /// plain and brand font family with default bold, medium, and regular weights of 700, 500, and 400, /// respectfully. Otherwise if the typography value is a map, it must have a `plain-family` font /// family value, and optionally a different `brand-family` font family (defaults to the plain /// value) and weights for `bold-weight` (default: 700), `bold-weight` (default: 500), and /// `bold-weight` (default: 400). Typography variable definitions will not be emitted if there are /// no typography values in the config. /// /// If the config map's density value is a number, it will be used as the density scale. Otherwise /// if the density value is a map, it must have a `scale` value. Density variable definitions will /// not be emitted if there are no density values in the config. /// /// The application variables emitted use the namespace prefix "--mat-sys". /// e.g. --mat-sys-surface: #E5E5E5 /// /// @param {Map} $config The color configuration with optional keys color, typography, or density. @mixin theme($config, $overrides: ()) { $color: map.get($config, color); $color-config: null; @if ($color) { // validate-palette returns null if it is a valid M3 palette $is-palette: config-validation.validate-palette($color) == null; // Default to "color-scheme" theme type if the config's color does not provide one. @if (not $is-palette and not map.has-key($color, theme-type)) { $color: map.set($color, theme-type, color-scheme); } $color-config: $color; @if ($is-palette) { $color: map.set($color, tertiary, $color); $color-config: ( definition.$internals: ( palettes: ( primary: map.remove($color, neutral, neutral-variant, secondary), secondary: map.get($color, secondary), tertiary: map.remove($color, neutral, neutral-variant, secondary, error), neutral: map.get($color, neutral), neutral-variant: map.get($color, neutral-variant), error: map.get($color, error), ), theme-type: color-scheme, ) ); } @else { $primary: map.get($color, primary) or palettes.$violet-palette; $tertiary: map.get($color, tertiary) or $primary; $color-config: ( definition.$internals: ( palettes: ( primary: map.remove($primary, neutral, neutral-variant, secondary), secondary: map.get($primary, secondary), tertiary: map.remove($tertiary, neutral, neutral-variant, secondary, error), neutral: map.get($primary, neutral), neutral-variant: map.get($primary, neutral-variant), error: map.get($primary, error), ), theme-type: map.get($color, theme-type), ) ); } @include system-level-colors($color-config, $overrides, definition.$system-fallback-prefix); @include system-level-elevation($color-config, $overrides, definition.$system-fallback-prefix); } $typography: map.get($config, typography); $typography-config: null; @if ($typography) { $plain: (Roboto, sans-serif); $brand: $plain; $bold: 700; $medium: 500; $regular: 400; @if (meta.type-of($typography) == map) { $plain: map.get($typography, plain-family); $brand: map.get($typography, brand-family) or $plain; $bold: map.get($typography, bold-weight) or $bold; $medium: map.get($typography, medium-weight) or $medium; $regular: map.get($typography, regular-weight) or $regular; } @else { $plain: $typography; } $typography-config: ( definition.$internals: ( font-definition: ( plain: $plain, brand: $brand, bold: $bold, medium: $medium, regular: $regular, ) ) ); @include system-level-typography( $typography-config, $overrides, definition.$system-fallback-prefix); } $density: map.get($config, density); $density-config: null; @if ($density) { $scale: 0; @if (meta.type-of($density) == map) { $scale: map.get($density, scale); } @else { $scale: $density; } @if ($scale != 0) { // Emit component-level density tokens if the scale is lower than 0. The density tokens // do not fallback to any system-level values and must be defined if the scale is different. $density-tokens: get-density-tokens($scale); @each $tokens in $density-tokens { @each $token-name, $token-value in $tokens { --mat-#{$token-name}: #{$token-value}; } } } } @include system-level-shape($overrides: $overrides, $prefix: definition.$system-fallback-prefix); @include system-level-state($overrides: $overrides, $prefix: definition.$system-fallback-prefix); } // Gets all density-related tokens from the components. @function get-density-tokens($scale) { @return ( m3-checkbox.get-density-tokens($scale), m3-chip.get-density-tokens($scale), m3-expansion.get-density-tokens($scale), m3-fab.get-density-tokens($scale), m3-button.get-density-tokens($scale), m3-form-field.get-density-tokens($scale), m3-icon-button.get-density-tokens($scale), m3-list.get-density-tokens($scale), m3-paginator.get-density-tokens($scale), m3-radio.get-density-tokens($scale), m3-tabs.get-density-tokens($scale), m3-select.get-density-tokens($scale), m3-button-toggle.get-density-tokens($scale), m3-stepper.get-density-tokens($scale), m3-table.get-density-tokens($scale), m3-toolbar.get-density-tokens($scale), m3-tree.get-density-tokens($scale), ); } /// Emits the system-level CSS variables for each of the provided override values. E.g. to /// change the primary color to red, use `mat.theme-overrides((primary: red));` @mixin theme-overrides($overrides, $prefix: definition.$system-fallback-prefix) { $sys-names: map-merge-all( m3.md-sys-color-values-light(palettes.$blue-palette), m3.md-sys-typescale-values(( brand: (Roboto), plain: (Roboto), bold: 700, medium: 500, regular: 400 )), m3.md-sys-elevation-values(), m3.md-sys-shape-values(), m3.md-sys-state-values()); & { @each $name, $value in $overrides { @if (map.has-key($sys-names, $name)) { --#{$prefix}-#{$name}: #{map.get($overrides, $name)}; } } } } @mixin system-level-colors($theme, $overrides: (), $prefix: null) { $palettes: map.get($theme, definition.$internals, palettes); $type: map.get($theme, definition.$internals, theme-type); @if (not $prefix) { $prefix: map.get($theme, definition.$internals, color-system-variables-prefix) or definition.$system-level-prefix; } $sys-colors: _generate-sys-colors($palettes, $type); // Manually insert a subset of palette values that are used directly by components // instead of system variables. $sys-colors: map.set($sys-colors, neutral-variant20, map.get($palettes, neutral-variant, 20)); $sys-colors: map.set($sys-colors, neutral10, map.get($palettes, neutral, 10)); & { @each $name, $value in $sys-colors { --#{$prefix}-#{$name}: #{map.get($overrides, $name) or $value}; } } } @function _generate-sys-colors($palettes, $type) { $light-sys-colors: m3.md-sys-color-values-light($palettes); @if ($type == light) { @return $light-sys-colors; } $dark-sys-colors: m3.md-sys-color-values-dark($palettes); @if ($type == dark) { @return $dark-sys-colors; } @if ($type == color-scheme) { $light-dark-sys-colors: (); @each $name, $light-value in $light-sys-colors { $dark-value: map.get($dark-sys-colors, $name); $light-dark-sys-colors: map.set($light-dark-sys-colors, $name, light-dark($light-value, $dark-value)); } @return $light-dark-sys-colors; } @error 'Unknown theme-type provided: #{$type}'; } @mixin system-level-typography($theme, $overrides: (), $prefix: null) { $font-definition: map.get($theme, definition.$internals, font-definition); @if (not $prefix) { $prefix: map.get($theme, definition.$internals, typography-system-variables-prefix) or definition.$system-level-prefix; } & { @each $name, $value in m3.md-sys-typescale-values($font-definition) { --#{$prefix}-#{$name}: #{map.get($overrides, $name) or $value}; } } } @mixin system-level-elevation($theme, $overrides: (), $prefix: definition.$system-level-prefix) { $shadow-color: map.get( $theme, definition.$internals, palettes, neutral, 0); @each $name, $value in m3.md-sys-elevation-values() { $level: map.get($overrides, $name) or $value; $value: elevation.get-box-shadow($level, $shadow-color); & { --#{$prefix}-#{$name}: #{$value}; } } } @mixin system-level-shape($theme: (), $overrides: (), $prefix: definition.$system-level-prefix) { & { @each $name, $value in m3.md-sys-shape-values() { --#{$prefix}-#{$name}: #{map.get($overrides, $name) or $value}; } } } @mixin system-level-state($theme: (), $overrides: (), $prefix: definition.$system-level-prefix) { & { @each $name, $value in m3.md-sys-state-values() { --#{$prefix}-#{$name}: #{map.get($overrides, $name) or $value}; } } } /// Creates a single merged map from the provided variable-length map arguments @function map-merge-all($maps...) { $result: (); @each $map in $maps { $result: map.merge($result, $map); } @return $result; }