@use 'sass:map'; @use 'sass:math'; @use 'sass:meta'; @use 'typography-utils'; @use '../theming/theming'; @use '@material/typography' as mdc-typography; /// Defines a typography level from the Material Design spec. /// @param {String} $font-size The font-size for this level. /// @param {String | Number} $line-height The line-height for this level. /// @param {String | Number} $font-weight The font-weight for this level. /// @param {String} $font-family The font-family for this level. /// @param {String} $letter-spacing The letter-spacing for this level. /// @returns {Map} A map representing the definition of this typographic level. @function define-typography-level( $font-size, $line-height: $font-size, $font-weight: 400, $font-family: null, $letter-spacing: normal) { @return ( font-size: $font-size, line-height: $line-height, font-weight: $font-weight, font-family: $font-family, letter-spacing: $letter-spacing ); } /// Defines a collection of typography levels to configure typography for an application. /// Any level not specified defaults to the values defined in the Material Design specification: /// https://material.io/guidelines/style/typography.html. /// /// Note that the Material Design specification does not describe explicit letter-spacing values. /// The values here come from reverse engineering the Material Design examples. /// @param {String} $font-family Default font-family for levels that don't specify font-family. /// @param {Map} $display-4 Configuration for the "display-4" typographic level. /// @param {Map} $display-3 Configuration for the "display-3" typographic level. /// @param {Map} $display-2 Configuration for the "display-2" typographic level. /// @param {Map} $display-1 Configuration for the "display-1" typographic level. /// @param {Map} $headline Configuration for the "headline" typographic level. /// @param {Map} $title Configuration for the "title" typographic level. /// @param {Map} $subheading-2 Configuration for the "subheading-2" typographic level. /// @param {Map} $subheading-1 Configuration for the "subheading-1" typographic level. /// @param {Map} $body-2 Configuration for the "body-2" typographic level. /// @param {Map} $body-1 Configuration for the "body-1" typographic level. /// @param {Map} $caption Configuration for the "caption" typographic level. /// @param {Map} $button Configuration for the "button" typographic level. /// @param {Map} $input Configuration for the "input" typographic level. /// @returns {Map} A typography config for the application. /// /// @deprecated Use `mat.define-typography-config` instead. See https://material.angular.io/guide/mdc-migration for information about migrating. /// @breaking-change 17.0.0 @function define-legacy-typography-config( $font-family: 'Roboto, "Helvetica Neue", sans-serif', $display-4: define-typography-level(112px, 112px, 300, $letter-spacing: -0.05em), $display-3: define-typography-level(56px, 56px, 400, $letter-spacing: -0.02em), $display-2: define-typography-level(45px, 48px, 400, $letter-spacing: -0.005em), $display-1: define-typography-level(34px, 40px, 400), $headline: define-typography-level(24px, 32px, 400), $title: define-typography-level(20px, 32px, 500), $subheading-2: define-typography-level(16px, 28px, 400), $subheading-1: define-typography-level(15px, 24px, 400), $body-2: define-typography-level(14px, 24px, 500), $body-1: define-typography-level(14px, 20px, 400), $caption: define-typography-level(12px, 20px, 400), $button: define-typography-level(14px, 14px, 500), // Line-height must be unit-less fraction of the font-size. $input: define-typography-level(inherit, 1.125, 400) ) { // Declare an initial map with all of the levels. $config: ( display-4: $display-4, display-3: $display-3, display-2: $display-2, display-1: $display-1, headline: $headline, title: $title, subheading-2: $subheading-2, subheading-1: $subheading-1, body-2: $body-2, body-1: $body-1, caption: $caption, button: $button, input: $input, ); // Loop through the levels and set the `font-family` of the ones that don't have one to the base. // Note that Sass can't modify maps in place, which means that we need to merge and re-assign. @each $key, $level in $config { @if map.get($level, font-family) == null { $new-level: map.merge($level, (font-family: $font-family)); $config: map.merge($config, ($key: $new-level)); } } // Add the base font family to the config. @return map.merge($config, (font-family: $font-family)); } // Converts a map containing rem values to a map containing px values. @function _rem-to-px($x, $px-per-rem: 16px) { @if meta.type-of($x) == 'map' { @each $key, $val in $x { $x: map.merge($x, ($key: _rem-to-px($val))); } @return $x; } @if meta.type-of($x) == 'number' and math.unit($x) == 'rem' { @return math.div($x, 1rem) * $px-per-rem; } @else { @return $x; } } // Applies the default font family to all levels in a typography config. @function _apply-font-family($font-family, $initial-config) { $config: $initial-config; @each $key, $level in $config { @if map.get($level, 'font-family') == null { // Sass maps are immutable so we have to re-assign the variable each time. $config: map.set($config, $key, map.set($level, 'font-family', $font-family)); } } @return map.set($config, 'font-family', $font-family); } // Converts an MDC typography level config to an Angular Material one. @function typography-config-level-from-mdc($mdc-level, $font-family: null) { $mdc-level-config: map.get(mdc-typography.$styles, $mdc-level); // Explicitly default the font family to null since we'll apply it globally // through the `define-typgraphy-config`/`define-legacy-typography-config`. @return define-typography-level( $font-family: $font-family, $font-size: map.get($mdc-level-config, font-size), $line-height: map.get($mdc-level-config, line-height), $font-weight: map.get($mdc-level-config, font-weight), $letter-spacing: map.get($mdc-level-config, letter-spacing) ); } /// Generates an Angular Material typography config based on values from the official Material /// Design spec implementation (MDC Web). All arguments are optional, but may be passed to override /// the default values. The `mat-typography-level` function can be used to generate a custom /// typography level map which can be passed to this function to override one of the default levels. /// All default typography sizing generated by this function is in `px` units. /// /// @param {String} $font-family The font family to use for levels where it is not explicitly /// specified. /// @param {Map} $headline-1 The font settings for the headline-1 font level. /// @param {Map} $headline-2 The font settings for the headline-2 font level. /// @param {Map} $headline-3 The font settings for the headline-3 font level. /// @param {Map} $headline-4 The font settings for the headline-4 font level. /// @param {Map} $headline-5 The font settings for the headline-5 font level. /// @param {Map} $headline-6 The font settings for the headline-6 font level. /// @param {Map} $subtitle-1 The font settings for the subtitle-1 font level. /// @param {Map} $subtitle-2 The font settings for the subtitle-2 font level. /// @param {Map} $body-1 The font settings for the body-1 font level. /// @param {Map} $body-2 The font settings for the body-2 font level. /// @param {Map} $caption The font settings for the caption font level. /// @param {Map} $button The font settings for the button font level. /// @param {Map} $overline The font settings for the overline font level. /// @return {Map} A map containing font settings for each of the levels in the Material Design spec. @function define-typography-config( // TODO(mmalerba): rename this function to define-typography-config, // and create a predefined px based config for people that need it. $font-family: mdc-typography.$font-family, $headline-1: null, $headline-2: null, $headline-3: null, $headline-4: null, $headline-5: null, $headline-6: null, $subtitle-1: null, $subtitle-2: null, $body-1: null, $body-2: null, $caption: null, $button: null, $overline: null, ) { @return _apply-font-family($font-family, ( headline-1: $headline-1 or _rem-to-px(typography-config-level-from-mdc(headline1)), headline-2: $headline-2 or _rem-to-px(typography-config-level-from-mdc(headline2)), headline-3: $headline-3 or _rem-to-px(typography-config-level-from-mdc(headline3)), headline-4: $headline-4 or _rem-to-px(typography-config-level-from-mdc(headline4)), headline-5: $headline-5 or _rem-to-px(typography-config-level-from-mdc(headline5)), headline-6: $headline-6 or _rem-to-px(typography-config-level-from-mdc(headline6)), subtitle-1: $subtitle-1 or _rem-to-px(typography-config-level-from-mdc(subtitle1)), subtitle-2: $subtitle-2 or _rem-to-px(typography-config-level-from-mdc(subtitle2)), body-1: $body-1 or _rem-to-px(typography-config-level-from-mdc(body1)), body-2: $body-2 or _rem-to-px(typography-config-level-from-mdc(body2)), caption: $caption or _rem-to-px(typography-config-level-from-mdc(caption)), button: $button or _rem-to-px(typography-config-level-from-mdc(button)), overline: $overline or _rem-to-px(typography-config-level-from-mdc(overline)), )); } /// Generates an Angular Material typography config based on values from the official Material /// Design spec implementation (MDC Web). All arguments are optional, but may be passed to override /// the default values. The `mat-typography-level` function can be used to generate a custom /// typography level map which can be passed to this function to override one of the default levels. /// All default typography sizing generated by this function is in `rem` units. /// /// @param {String} $font-family The font family to use for levels where it is not explicitly /// specified. /// @param {Map} $headline-1 The font settings for the headline-1 font level. /// @param {Map} $headline-2 The font settings for the headline-2 font level. /// @param {Map} $headline-3 The font settings for the headline-3 font level. /// @param {Map} $headline-4 The font settings for the headline-4 font level. /// @param {Map} $headline-5 The font settings for the headline-5 font level. /// @param {Map} $headline-6 The font settings for the headline-6 font level. /// @param {Map} $subtitle-1 The font settings for the subtitle-1 font level. /// @param {Map} $subtitle-2 The font settings for the subtitle-2 font level. /// @param {Map} $body-1 The font settings for the body-1 font level. /// @param {Map} $body-2 The font settings for the body-2 font level. /// @param {Map} $caption The font settings for the caption font level. /// @param {Map} $button The font settings for the button font level. /// @param {Map} $overline The font settings for the overline font level. /// @return {Map} A map containing font settings for each of the levels in the Material Design spec. @function define-rem-typography-config( // TODO(mmalerba): rename this function to define-typography-config, // and create a predefined px based config for people that need it. $font-family: mdc-typography.$font-family, $headline-1: null, $headline-2: null, $headline-3: null, $headline-4: null, $headline-5: null, $headline-6: null, $subtitle-1: null, $subtitle-2: null, $body-1: null, $body-2: null, $caption: null, $button: null, $overline: null, ) { @return _apply-font-family($font-family, ( headline-1: $headline-1 or typography-config-level-from-mdc(headline1), headline-2: $headline-2 or typography-config-level-from-mdc(headline2), headline-3: $headline-3 or typography-config-level-from-mdc(headline3), headline-4: $headline-4 or typography-config-level-from-mdc(headline4), headline-5: $headline-5 or typography-config-level-from-mdc(headline5), headline-6: $headline-6 or typography-config-level-from-mdc(headline6), subtitle-1: $subtitle-1 or typography-config-level-from-mdc(subtitle1), subtitle-2: $subtitle-2 or typography-config-level-from-mdc(subtitle2), body-1: $body-1 or typography-config-level-from-mdc(body1), body-2: $body-2 or typography-config-level-from-mdc(body2), caption: $caption or typography-config-level-from-mdc(caption), button: $button or typography-config-level-from-mdc(button), overline: $overline or typography-config-level-from-mdc(overline), )); } // Whether a config is for the Material Design 2018 typography system. @function private-typography-is-2018-config($config) { @return map.get($config, headline-1) != null; } // Whether a config is for the Material Design 2014 typography system. @function private-typography-is-2014-config($config) { @return map.get($config, headline) != null; } // Given a config for either the 2014 or 2018 Material Design typography system, // produces a normalized typography config for the 2014 Material Design typography system. // 2014 - https://material.io/archive/guidelines/style/typography.html#typography-styles // 2018 - https://material.io/design/typography/the-type-system.html#type-scale // // Components using this function should be migrated to normalize to the 2018 style config instead. // New components should not use this function. @function private-typography-to-2014-config($config) { @if $config == null { @return null; } @if not private-typography-is-2014-config($config) { $args: ( display-4: map.get($config, headline-1), display-3: map.get($config, headline-2), display-2: map.get($config, headline-3), display-1: map.get($config, headline-4), headline: map.get($config, headline-5), title: map.get($config, headline-6), subheading-2: map.get($config, subtitle-1), subheading-1: map.get($config, subtitle-2), body-2: map.get($config, body-1), body-1: map.get($config, body-2), button: map.get($config, button), caption: map.get($config, caption), font-family: map.get($config, font-family), ); $non-null-args: (); @each $key, $value in $args { @if $value != null { $non-null-args: map.merge($non-null-args, ($key: $value)); } } @return define-legacy-typography-config($non-null-args...); } @return $config; } // Given a config for either the 2014 or 2018 Material Design typography system, // produces a normalized typography config for the 2018 Material Design typography system. // 2014 - https://material.io/archive/guidelines/style/typography.html#typography-styles // 2018 - https://material.io/design/typography/the-type-system.html#type-scale @function private-typography-to-2018-config($config) { @if $config == null { @return null; } @if not private-typography-is-2018-config($config) { @return ( headline-1: map.get($config, display-4), headline-2: map.get($config, display-3), headline-3: map.get($config, display-2), headline-4: map.get($config, display-1), headline-5: map.get($config, headline), headline-6: map.get($config, title), subtitle-1: map.get($config, subheading-2), font-famiy: map.get($config, font-family), // These mappings are odd, but body-2 in the 2014 system actually looks closer to subtitle-2 // in the 2018 system, and subeading-1 in the 2014 system looks more like body-1 in the 2018 // system. subtitle-2: map.get($config, body-2), body-1: map.get($config, subheading-1), body-2: map.get($config, body-1), button: map.get($config, button), caption: map.get($config, caption), overline: if(map.get($config, overline), map.get($config, overline), define-typography-level(12px, 32px, 500) ) ); } @return $config; } /// Emits baseline typographic styles based on a given config. /// @param {Map} $config-or-theme A typography config for an entire theme. /// @param {String} $selector Ancestor selector under which native elements, such as h1, will /// be styled. @mixin typography-hierarchy($config-or-theme, $selector: '.mat-typography') { $config: private-typography-to-2018-config(theming.get-typography-config($config-or-theme)); // Note that it seems redundant to prefix the class rules with the `$selector`, however it's // necessary if we want to allow people to overwrite the tag selectors. This is due to // selectors like `#{$selector} h1` being more specific than ones like `.mat-title`. .mat-h1, .mat-headline-5, #{$selector} .mat-h1, #{$selector} .mat-headline-5, #{$selector} h1 { @include typography-utils.typography-level($config, headline-5); margin: 0 0 16px; } .mat-h2, .mat-headline-6, #{$selector} .mat-h2, #{$selector} .mat-headline-6, #{$selector} h2 { @include typography-utils.typography-level($config, headline-6); margin: 0 0 16px; } .mat-h3, .mat-subtitle-1, #{$selector} .mat-h3, #{$selector} .mat-subtitle-1, #{$selector} h3 { @include typography-utils.typography-level($config, subtitle-1); margin: 0 0 16px; } .mat-h4, .mat-body-1, #{$selector} .mat-h4, #{$selector} .mat-body-1, #{$selector} h4 { @include typography-utils.typography-level($config, body-1); margin: 0 0 16px; } // Note: the spec doesn't have anything that would correspond to h5 and h6, but we add these for // consistency. The font sizes come from the Chrome user agent styles which have h5 at 0.83em // and h6 at 0.67em. .mat-h5, #{$selector} .mat-h5, #{$selector} h5 { @include typography-utils.font-shorthand( // calc is used here to support css variables calc(#{typography-utils.font-size($config, body-2)} * 0.83), typography-utils.font-weight($config, body-2), typography-utils.line-height($config, body-2), typography-utils.font-family($config, body-2) ); margin: 0 0 12px; } .mat-h6, #{$selector} .mat-h6, #{$selector} h6 { @include typography-utils.font-shorthand( // calc is used here to support css variables calc(#{typography-utils.font-size($config, body-2)} * 0.67), typography-utils.font-weight($config, body-2), typography-utils.line-height($config, body-2), typography-utils.font-family($config, body-2) ); margin: 0 0 12px; } .mat-body-strong, .mat-subtitle-2, #{$selector} .mat-body-strong, #{$selector} .mat-subtitle-2 { @include typography-utils.typography-level($config, subtitle-2); } .mat-body, .mat-body-2, #{$selector} .mat-body, #{$selector} .mat-body-2, #{$selector} { @include typography-utils.typography-level($config, body-2); p { margin: 0 0 12px; } } .mat-small, .mat-caption, #{$selector} .mat-small, #{$selector} .mat-caption { @include typography-utils.typography-level($config, caption); } .mat-headline-1, #{$selector} .mat-headline-1 { @include typography-utils.typography-level($config, headline-1); margin: 0 0 56px; } .mat-headline-2, #{$selector} .mat-headline-2 { @include typography-utils.typography-level($config, headline-2); margin: 0 0 64px; } .mat-headline-3, #{$selector} .mat-headline-3 { @include typography-utils.typography-level($config, headline-3); margin: 0 0 64px; } .mat-headline-4, #{$selector} .mat-headline-4 { @include typography-utils.typography-level($config, headline-4); margin: 0 0 64px; } } /// Emits baseline typographic styles based on a given config. /// @param {Map} $config-or-theme A typography config for an entire theme. /// @param {String} $selector Ancestor selector under which native elements, such as h1, will /// be styled. /// @deprecated Use `mat.typography-hierarchy` instead. See https://material.angular.io/guide/mdc-migration for information about migrating. /// @breaking-change 17.0.0 @mixin legacy-typography-hierarchy($config-or-theme, $selector: '.mat-typography') { $config: private-typography-to-2014-config(theming.get-typography-config($config-or-theme)); // Note that it seems redundant to prefix the class rules with the `$selector`, however it's // necessary if we want to allow people to overwrite the tag selectors. This is due to // selectors like `#{$selector} h1` being more specific than ones like `.mat-title`. .mat-h1, .mat-headline, #{$selector} .mat-h1, #{$selector} .mat-headline, #{$selector} h1 { @include typography-utils.typography-level($config, headline); margin: 0 0 16px; } .mat-h2, .mat-title, #{$selector} .mat-h2, #{$selector} .mat-title, #{$selector} h2 { @include typography-utils.typography-level($config, title); margin: 0 0 16px; } .mat-h3, .mat-subheading-2, #{$selector} .mat-h3, #{$selector} .mat-subheading-2, #{$selector} h3 { @include typography-utils.typography-level($config, subheading-2); margin: 0 0 16px; } .mat-h4, .mat-subheading-1, #{$selector} .mat-h4, #{$selector} .mat-subheading-1, #{$selector} h4 { @include typography-utils.typography-level($config, subheading-1); margin: 0 0 16px; } // Note: the spec doesn't have anything that would correspond to h5 and h6, but we add these for // consistency. The font sizes come from the Chrome user agent styles which have h5 at 0.83em // and h6 at 0.67em. .mat-h5, #{$selector} .mat-h5, #{$selector} h5 { @include typography-utils.font-shorthand( // calc is used here to support css variables calc(#{typography-utils.font-size($config, body-1)} * 0.83), typography-utils.font-weight($config, body-1), typography-utils.line-height($config, body-1), typography-utils.font-family($config, body-1) ); margin: 0 0 12px; } .mat-h6, #{$selector} .mat-h6, #{$selector} h6 { @include typography-utils.font-shorthand( // calc is used here to support css variables calc(#{typography-utils.font-size($config, body-1)} * 0.67), typography-utils.font-weight($config, body-1), typography-utils.line-height($config, body-1), typography-utils.font-family($config, body-1) ); margin: 0 0 12px; } .mat-body-strong, .mat-body-2, #{$selector} .mat-body-strong, #{$selector} .mat-body-2 { @include typography-utils.typography-level($config, body-2); } .mat-body, .mat-body-1, #{$selector} .mat-body, #{$selector} .mat-body-1, #{$selector} { @include typography-utils.typography-level($config, body-1); p { margin: 0 0 12px; } } .mat-small, .mat-caption, #{$selector} .mat-small, #{$selector} .mat-caption { @include typography-utils.typography-level($config, caption); } .mat-display-4, #{$selector} .mat-display-4 { @include typography-utils.typography-level($config, display-4); margin: 0 0 56px; } .mat-display-3, #{$selector} .mat-display-3 { @include typography-utils.typography-level($config, display-3); margin: 0 0 64px; } .mat-display-2, #{$selector} .mat-display-2 { @include typography-utils.typography-level($config, display-2); margin: 0 0 64px; } .mat-display-1, #{$selector} .mat-display-1 { @include typography-utils.typography-level($config, display-1); margin: 0 0 64px; } }