// // Copyright 2023 Google LLC // SPDX-License-Identifier: Apache-2.0 // // go/keep-sorted start @use 'sass:list'; @use 'sass:map'; // go/keep-sorted end // go/keep-sorted start @use '../tokens'; // go/keep-sorted end /// `typescale.theme()` emits `--md-sys-typescale-*` custom properties for given /// typescale tokens. /// /// Use `typeface.theme()` to change font family and weight for all typescales, /// rather than individually. /// /// @example scss /// @use '@material/web/typography/typescale'; /// /// :root { /// @include typescale.theme(( /// 'body-medium-size': 1rem, /// 'body-medium-line-height': 1.5rem, /// /* ... */ /// )); /// } /// /// /* Generated CSS */ /// :root { /// --md-sys-typescale-body-medium-size: 1rem; /// --md-sys-typescale-body-medium-line-height: 1.5rem; /// /* ... */ /// } /// /// @param {Map} $tokens - A Map with `md-sys-typescale` token name keys and /// their corresponding `font` shorthand values. /// @output Emits `--md-sys-typescale-*` custom properties for given typescales. @mixin theme($tokens) { @each $token, $value in $tokens { @if list.index(tokens.$md-sys-typescale-supported-tokens, $token) == null { @error 'md-sys-typescale `#{$token}` is not a supported token.'; } @if $value { --md-sys-typescale-#{$token}: #{$value}; } } } /// Emits `.md-typescale-*` classes with font styles for each typescale in the /// provided `$tokens`. /// /// @example scss /// @include typescale.styles(tokens.md-sys-typescale-values()); /// // Generates the following CSS: /// .md-typescale-display-small { font: ...; } /// .md-typescale-display-medium { font: ...; } /// .md-typescale-display-large { font: ...; } /// .md-typescale-body-small { font: ...; } /// .md-typescale-body-medium { font: ...; } /// .md-typescale-body-large { font: ...; } /// // etc... /// /// @param {Map} $tokens - A Map with `md-sys-typescale` token values. /// @output Emits `.md-typescale-*` classes for each typescale size. @mixin styles($tokens) { $typescale-properties: _tokens-to-typescale-properties-map($tokens); // Use the default layer for lowered specificity. @layer { @each $typescale, $properties in $typescale-properties { // $typescale is a scale and size (ex. 'body-medium'). // $properties is a Map with 'font', 'size', 'line-height', 'weight', and // an optional 'weight-prominent'. .md-typescale-#{$typescale} { font: map.get($properties, 'weight') map.get($properties, 'size') / map.get($properties, 'line-height') map.get($properties, 'font'); } .md-typescale-#{$typescale}-prominent { // Inherit the font styles from the non-prominent selector. This adds // another class selector to the regular styles, instead of re-emitting // them. // ``` // .md-typescale-label-medium, .md-typescale-label-medium-prominent { // font: ...; // } // .md-typescale-label-medium-prominent { // font-weight: ...; // } // ``` @extend .md-typescale-#{$typescale}; // Note: the prominent selector is not emitted by Sass when a // typescale's prominent values are null. font-weight: map.get($properties, 'weight-prominent'); } } } } /// Takes a md-sys-typescale token values Map and returns a Map whose keys are /// typescale names ('body-medium', 'label-large', etc) and whose values are a /// Map of properties for that Typescale ('font', 'size', etc). @function _tokens-to-typescale-properties-map($tokens) { $typescale-properties: (); // The keys of $typescale-properties. Each typescale is joined with each size // ('display-small', 'display-medium', 'display-large', 'headline-small'...). $typescales: ('display', 'headline', 'title', 'body', 'label'); $sizes: ('small', 'medium', 'large'); // The keys to the Map for each scale in $typescale-properties. These // properties are required... $required-properties: ('font', 'line-height', 'size', 'weight'); // ...while not all typescales have these properties. $optional-properties: ('weight-prominent'); $properties: list.join($required-properties, $optional-properties); @each $typescale in $typescales { @each $size in $sizes { $typescale-and-size: #{$typescale}-#{$size}; @each $property in $properties { $token: '#{$typescale-and-size}-#{$property}'; $value: map.get($tokens, $token); @if $value == null and list.index($required-properties, $property) != null { @error 'Missing required typescale token `#{$token}`'; } // Remove token to check if we used them all at the end of the function. $tokens: map.remove($tokens, $token); $typescale-properties: map.set( $typescale-properties, $typescale-and-size, $property, $value ); } } } $unused-tokens: map.keys($tokens); $unused-token-count: list.length($unused-tokens); @if $unused-token-count > 0 { @error 'Missing styles for #{$unused-token-count} typescale tokens (#{$unused-tokens})'; } @return $typescale-properties; }