diff --git a/src/material/card/_card-theme.scss b/src/material/card/_card-theme.scss index c667b81d881f..f0f648125923 100644 --- a/src/material/card/_card-theme.scss +++ b/src/material/card/_card-theme.scss @@ -1,70 +1,65 @@ @use '../core/theming/theming'; -@use '../core/mdc-helpers/mdc-helpers'; @use '../core/style/private'; @use '../core/typography/typography'; +@use '../core/tokens/token-utils'; +@use '../core/tokens/m2/mat/card' as tokens-mat-card; +@use '../core/tokens/m2/mdc/elevated-card' as tokens-mdc-elevated-card; +@use '../core/tokens/m2/mdc/outlined-card' as tokens-mdc-outlined-card; @use '@material/card/elevated-card-theme' as mdc-elevated-card-theme; @use '@material/card/outlined-card-theme' as mdc-outlined-card-theme; -@use '@material/typography' as mdc-typography; -@use '@material/theme/theme-color' as mdc-theme-color; -@use 'sass:color'; -@use 'sass:map'; -@use 'sass:meta'; @mixin color($config-or-theme) { $config: theming.get-color-config($config-or-theme); - $foreground: map.get($config, foreground); + $mdc-elevated-card-color-tokens: tokens-mdc-elevated-card.get-color-tokens($config); + $mdc-outlined-card-color-tokens: tokens-mdc-outlined-card.get-color-tokens($config); + $mat-card-color-tokens: tokens-mat-card.get-color-tokens($config); - @include mdc-helpers.using-mdc-theme($config) { - $on-surface: mdc-theme-color.prop-value(on-surface); - $surface: mdc-theme-color.prop-value(surface); + // Add values for card tokens. + .mat-mdc-card { + @include mdc-elevated-card-theme.theme($mdc-elevated-card-color-tokens); + @include mdc-outlined-card-theme.theme($mdc-outlined-card-color-tokens); + @include token-utils.create-token-values(tokens-mat-card.$prefix, $mat-card-color-tokens); - .mat-mdc-card { - // MDC's theme has `container-elevation` and `container-shadow-color` tokens, but we can't - // use them because they output under a `.mdc-card` selector whereas the rest of the theme - // isn't under any selector. Even if the mixin is pulled out of the selector, it throws a - // different error. - @include private.private-theme-elevation(1, $config); - @include mdc-elevated-card-theme.theme(( - container-color: $surface, - )); - } - - .mat-mdc-card-outlined { - @include private.private-theme-elevation(0, $config); - @include mdc-outlined-card-theme.theme(( - outline-color: if( - meta.type-of($on-surface) == color and meta.type-of($surface) == color, - color.mix($on-surface, $surface, 12%), - $on-surface - ) - )); - } + // TODO(mmalerba): This should be based on tokens, but the elevation tokens don't seem to be + // working currently. They wind up setting values like `box-shadow: var(--e); --e: 1` + @include private.private-theme-elevation(1, $config); + } - // Card subtitles are an Angular Material construct (not MDC), so we explicitly set their - // color to secondary text here. - .mat-mdc-card-subtitle { - color: theming.get-color-from-palette($foreground, secondary-text); - } + .mat-mdc-card-outlined { + // TODO(mmalerba): This should be based on tokens, but the elevation tokens don't seem to be + // working currently. They wind up setting values like `box-shadow: var(--e); --e: 1` + @include private.private-theme-elevation(0, $config); } } @mixin typography($config-or-theme) { $config: typography.private-typography-to-2018-config( - theming.get-typography-config($config-or-theme)); - @include mdc-helpers.using-mdc-typography($config) { - // Card subtitles and titles are an Angular Material construct (not MDC), so we explicitly - // set their typographic styles here. - .mat-mdc-card-title { - @include mdc-typography.typography(headline6); - } + theming.get-typography-config($config-or-theme)); + $mdc-elevated-card-typography-tokens: tokens-mdc-elevated-card.get-typography-tokens($config); + $mdc-outlined-card-typography-tokens: tokens-mdc-outlined-card.get-typography-tokens($config); + $mat-card-typography-tokens: tokens-mat-card.get-typography-tokens($config); - .mat-mdc-card-subtitle { - @include mdc-typography.typography(subtitle2); - } + // Add values for card tokens. + .mat-mdc-card { + @include mdc-elevated-card-theme.theme($mdc-elevated-card-typography-tokens); + @include mdc-outlined-card-theme.theme($mdc-outlined-card-typography-tokens); + @include token-utils.create-token-values(tokens-mat-card.$prefix, $mat-card-typography-tokens); } } -@mixin density($config-or-theme) {} +@mixin density($config-or-theme) { + $density-scale: theming.get-density-config($config-or-theme); + $mdc-elevated-card-density-tokens: tokens-mdc-elevated-card.get-density-tokens($density-scale); + $mdc-outlined-card-density-tokens: tokens-mdc-outlined-card.get-density-tokens($density-scale); + $mat-card-density-tokens: tokens-mat-card.get-density-tokens($density-scale); + + // Add values for card tokens. + .mat-mdc-card { + @include mdc-elevated-card-theme.theme($mdc-elevated-card-density-tokens); + @include mdc-outlined-card-theme.theme($mdc-outlined-card-density-tokens); + @include token-utils.create-token-values(tokens-mat-card.$prefix, $mat-card-density-tokens); + } +} @mixin theme($theme-or-color-config) { $theme: theming.private-legacy-get-theme($theme-or-color-config); diff --git a/src/material/card/card.scss b/src/material/card/card.scss index a3c71efd30e7..37fa2bbc727f 100644 --- a/src/material/card/card.scss +++ b/src/material/card/card.scss @@ -1,10 +1,56 @@ @use '@material/card' as mdc-card; -@use '@material/card/variables' as mdc-card-variables; @use '@material/card/elevated-card-theme' as mdc-elevated-card-theme; @use '@material/card/outlined-card-theme' as mdc-outlined-card-theme; -@use '../core/mdc-helpers/mdc-helpers'; +@use '@material/theme/custom-properties' as mdc-custom-properties; +@use '../core/tokens/token-utils'; +@use '../core/tokens/m2/mat/card' as tokens-mat-card; +@use '../core/tokens/m2/mdc/elevated-card' as tokens-mdc-elevated-card; +@use '../core/tokens/m2/mdc/outlined-card' as tokens-mdc-outlined-card; // TODO(jelbourn): move header and title-group styles to their own files. +@include mdc-custom-properties.configure($emit-fallback-values: false, $emit-fallback-vars: false) { + $mdc-elevated-card-token-slots: tokens-mdc-elevated-card.get-token-slots(); + $mdc-outlined-card-token-slots: tokens-mdc-outlined-card.get-token-slots(); + + // Add the MDC card static styles. + @include mdc-card.static-styles(); + + .mat-mdc-card { + // Add the official slots for the MDC elevated-card. + @include mdc-elevated-card-theme.theme-styles($mdc-elevated-card-token-slots); + + // Add default values for MDC card tokens that aren't outputted by the theming API. + @include mdc-elevated-card-theme.theme(tokens-mdc-elevated-card.get-unthemable-tokens()); + @include mdc-outlined-card-theme.theme(tokens-mdc-outlined-card.get-unthemable-tokens()); + @include token-utils.create-token-values( + tokens-mat-card.$prefix, tokens-mat-card.get-unthemable-tokens()); + } + + .mat-mdc-card-outlined { + // Add the official slots for the MDC outlined-card. + @include mdc-outlined-card-theme.theme-styles($mdc-outlined-card-token-slots); + } + + // Add slots for custom Angular Material card tokens. + @include token-utils.use-tokens(tokens-mat-card.$prefix, tokens-mat-card.get-token-slots()) { + .mat-mdc-card-title { + @include token-utils.create-token-slot(font-family, title-text-font); + @include token-utils.create-token-slot(line-height, title-text-line-height); + @include token-utils.create-token-slot(font-size, title-text-size); + @include token-utils.create-token-slot(letter-spacing, title-text-tracking); + @include token-utils.create-token-slot(font-weight, title-text-weight); + } + + .mat-mdc-card-subtitle { + @include token-utils.create-token-slot(color, subtitle-text-color); + @include token-utils.create-token-slot(font-family, subtitle-text-font); + @include token-utils.create-token-slot(line-height, subtitle-text-line-height); + @include token-utils.create-token-slot(font-size, subtitle-text-size); + @include token-utils.create-token-slot(letter-spacing, subtitle-text-tracking); + @include token-utils.create-token-slot(font-weight, subtitle-text-weight); + } + } +} // Size of the `mat-card-header` region custom to Angular Material. $mat-card-header-size: 40px !default; @@ -12,29 +58,8 @@ $mat-card-header-size: 40px !default; // Default padding for text content within a card. $mat-card-default-padding: 16px !default; -@include mdc-helpers.disable-mdc-fallback-declarations { - // Include all MDC card styles except for color and typography. - @include mdc-card.static-styles($query: mdc-helpers.$mdc-base-styles-query); -} - .mat-mdc-card { position: relative; - - @include mdc-helpers.disable-mdc-fallback-declarations { - @include mdc-elevated-card-theme.theme-styles(( - container-color: transparent, - container-shape: mdc-card-variables.$shape-radius, - )); - } -} - -.mat-mdc-card-outlined { - @include mdc-helpers.disable-mdc-fallback-declarations { - @include mdc-outlined-card-theme.theme-styles(( - outline-color: transparent, - outline-width: 1px, - )); - } } // Title text and subtitles text within a card. MDC doesn't have pre-made title sections for cards. diff --git a/src/material/core/tokens/_token-utils.scss b/src/material/core/tokens/_token-utils.scss index b922edbf2e63..94ba7605d37b 100644 --- a/src/material/core/tokens/_token-utils.scss +++ b/src/material/core/tokens/_token-utils.scss @@ -1,6 +1,7 @@ @use 'sass:map'; @use '@material/theme/custom-properties' as mdc-custom-properties; @use '@material/theme/theme' as mdc-theme; +@use '@material/theme/keys' as mdc-keys; @use '../mdc-helpers/mdc-helpers'; @use '../theming/palette'; @use '../theming/theming'; @@ -78,3 +79,18 @@ $_component-prefix: null; @include mdc-theme.property($property, $value); } } + +@mixin create-token-values($prefix, $tokens) { + @include _configure-token-prefix($prefix...) { + @include mdc-keys.declare-custom-properties($tokens, $_component-prefix); + } +} + +// Merges together a list of maps into a single map. +@function merge-all($maps...) { + $result: (); + @each $map in $maps { + $result: map.merge($result, $map); + } + @return $result; +} diff --git a/src/material/core/tokens/m2/mat/_card.scss b/src/material/core/tokens/m2/mat/_card.scss new file mode 100644 index 000000000000..7f19a99189a5 --- /dev/null +++ b/src/material/core/tokens/m2/mat/_card.scss @@ -0,0 +1,67 @@ +@use 'sass:map'; +@use '../../token-utils'; +@use '../../../theming/theming'; +@use '../../../typography/typography-utils'; + +// The prefix used to generate the fully qualified name for tokens in this file. +$prefix: (mat, card); + +// Tokens that can't be configured through Angular Material's current theming API, +// but may be in a future version of the theming API. +@function get-unthemable-tokens() { + @return (); +} + +// Tokens that can be configured through Angular Material's color theming API. +@function get-color-tokens($config) { + $foreground: map.get($config, foreground); + + @return ( + // Text color of the card's subtitle. + subtitle-text-color: theming.get-color-from-palette($foreground, secondary-text), + ); +} + +// Tokens that can be configured through Angular Material's typography theming API. +@function get-typography-tokens($config) { + @return ( + // Font family of the card's title. + title-text-font: typography-utils.font-family($config, headline-6) + or typography-utils.font-family($config), + // Line height of the card's title. + title-text-line-height: typography-utils.line-height($config, headline-6), + // Font size of the card's title. + title-text-size: typography-utils.font-size($config, headline-6), + // Letter spacing of the card's title. + title-text-tracking: typography-utils.letter-spacing($config, headline-6), + // Font weight of the card's title. + title-text-weight: typography-utils.font-weight($config, headline-6), + // Font family of the card's subtitle. + subtitle-text-font: typography-utils.font-family($config, subtitle-2) + or typography-utils.font-family($config), + // Line height of the card's subtitle. + subtitle-text-line-height: typography-utils.line-height($config, subtitle-2), + // Font size of the card's subtitle. + subtitle-text-size: typography-utils.font-size($config, subtitle-2), + // Letter spacing of the card's subtitle. + subtitle-text-tracking: typography-utils.letter-spacing($config, subtitle-2), + // Font weight of the card's subtitle. + subtitle-text-weight: typography-utils.font-weight($config, subtitle-2), + ); +} + +// Tokens that can be configured through Angular Material's density theming API. +@function get-density-tokens($config) { + @return (); +} + +// Combines the tokens generated by the above functions into a single map with placeholder values. +// This is used to create token slots. +@function get-token-slots() { + @return token-utils.merge-all( + get-unthemable-tokens(), + get-color-tokens(token-utils.$placeholder-color-config), + get-typography-tokens(token-utils.$placeholder-typography-config), + get-density-tokens(token-utils.$placeholder-density-config) + ); +} diff --git a/src/material/core/tokens/m2/mdc/_elevated-card.scss b/src/material/core/tokens/m2/mdc/_elevated-card.scss new file mode 100644 index 000000000000..20ea94736400 --- /dev/null +++ b/src/material/core/tokens/m2/mdc/_elevated-card.scss @@ -0,0 +1,78 @@ +@use 'sass:map'; +@use '../../../theming/theming'; +@use '../../token-utils'; + +// The prefix used to generate the fully qualified name for tokens in this file. +$prefix: (mdc, elevated-card); + +// Tokens that can't be configured through Angular Material's current theming API, +// but may be in a future version of the theming API. +// +// Tokens that are available in MDC, but not used in Angular Material should be mapped to `null`. +// `null` indicates that we are intentionally choosing not to emit a slot or value for the token in +// our CSS. +@function get-unthemable-tokens() { + @return ( + // The border-radius of the card. + container-shape: 4px, + // ============================================================================================= + // = TOKENS NOT USED IN ANGULAR MATERIAL = + // ============================================================================================= + // TODO(mmalerba): The elevation tokens don't seem to work currently. + // Should work with MDC to figure out what's wrong. + container-elevation: null, + container-shadow-color: null, + // Angular Material's card is not an interactive element, and therefore does not support states. + disabled-container-color: null, + disabled-container-elevation: null, + disabled-container-opacity: null, + dragged-container-elevation: null, + dragged-state-layer-color: null, + dragged-state-layer-opacity: null, + focus-container-elevation: null, + focus-state-layer-color:null, + focus-state-layer-opacity: null, + hover-container-elevation: null, + hover-state-layer-color: null, + hover-state-layer-opacity: null, + pressed-container-elevation: null, + pressed-state-layer-color: null, + pressed-state-layer-opacity: null, + // Angular Material does not currently support surface tint. + container-surface-tint-layer-color: null, + // MDC does not seem to use these tokens. + icon-color: null, + icon-size: null, + ); +} + +// Tokens that can be configured through Angular Material's color theming API. +@function get-color-tokens($config) { + $background: map.get($config, background); + + @return ( + // The background color of the card. + container-color: theming.get-color-from-palette($background, card), + ); +} + +// Tokens that can be configured through Angular Material's typography theming API. +@function get-typography-tokens($config) { + @return (); +} + +// Tokens that can be configured through Angular Material's density theming API. +@function get-density-tokens($config) { + @return (); +} + +// Combines the tokens generated by the above functions into a single map with placeholder values. +// This is used to create token slots. +@function get-token-slots() { + @return token-utils.merge-all( + get-unthemable-tokens(), + get-color-tokens(token-utils.$placeholder-color-config), + get-typography-tokens(token-utils.$placeholder-typography-config), + get-density-tokens(token-utils.$placeholder-density-config) + ); +} diff --git a/src/material/core/tokens/m2/mdc/_list.scss b/src/material/core/tokens/m2/mdc/_list.scss index 28610449dca2..79b2cccbad1c 100644 --- a/src/material/core/tokens/m2/mdc/_list.scss +++ b/src/material/core/tokens/m2/mdc/_list.scss @@ -207,14 +207,10 @@ $prefix: (mdc, list); // Combines the tokens generated by the above functions into a single map with placeholder values. // This is used to create token slots. @function get-token-slots() { - @return map.merge( + @return token-utils.merge-all( get-unthemable-tokens(), - map.merge( - get-color-tokens(token-utils.$placeholder-color-config), - map.merge( - get-typography-tokens(token-utils.$placeholder-typography-config), - get-density-tokens(token-utils.$placeholder-density-config) - ) - ) + get-color-tokens(token-utils.$placeholder-color-config), + get-typography-tokens(token-utils.$placeholder-typography-config), + get-density-tokens(token-utils.$placeholder-density-config) ); } diff --git a/src/material/core/tokens/m2/mdc/_outlined-card.scss b/src/material/core/tokens/m2/mdc/_outlined-card.scss new file mode 100644 index 000000000000..d6640833fe4f --- /dev/null +++ b/src/material/core/tokens/m2/mdc/_outlined-card.scss @@ -0,0 +1,87 @@ +@use 'sass:map'; +@use '../../../theming/theming'; +@use '../../token-utils'; + +// The prefix used to generate the fully qualified name for tokens in this file. +$prefix: (mdc, outlined-card); + +// Tokens that can't be configured through Angular Material's current theming API, +// but may be in a future version of the theming API. +// +// Tokens that are available in MDC, but not used in Angular Material should be mapped to `null`. +// `null` indicates that we are intentionally choosing not to emit a slot or value for the token in +// our CSS. +@function get-unthemable-tokens() { + @return ( + // The border-radius of the card. + container-shape: 4px, + // The thickness of the card's border. + outline-width: 1px, + // ============================================================================================= + // = TOKENS NOT USED IN ANGULAR MATERIAL = + // ============================================================================================= + // TODO(mmalerba): The elevation tokens don't seem to work currently. + // Should work with MDC to figure out what's wrong. + container-elevation: null, + container-shadow-color: null, + // Angular Material's card is not an interactive element, and therefore does not support states. + disabled-container-elevation: null, + disabled-outline-color: null, + disabled-outline-opacity: null, + dragged-container-elevation: null, + dragged-outline-color: null, + dragged-state-layer-color: null, + dragged-state-layer-opacity: null, + focus-container-elevation: null, + focus-outline-color: null, + focus-state-layer-color: null, + focus-state-layer-opacity: null, + hover-container-elevation: null, + hover-outline-color: null, + hover-state-layer-color: null, + hover-state-layer-opacity: null, + pressed-container-elevation: null, + pressed-outline-color: null, + pressed-state-layer-color: null, + pressed-state-layer-opacity: null, + // Angular Material does not currently support surface tint. + container-surface-tint-layer-color: null, + // MDC does not seem to use these tokens. + icon-color: null, + icon-size: null, + ); +} + +// Tokens that can be configured through Angular Material's color theming API. +@function get-color-tokens($config) { + $background: map.get($config, background); + $foreground: map.get($config, foreground); + + @return ( + // The background color of the card. + container-color: theming.get-color-from-palette($background, card), + // The border color of the card. + outline-color: theming.get-color-from-palette($foreground, base, 0.12), + ); +} + +// Tokens that can be configured through Angular Material's typography theming API. +@function get-typography-tokens($config) { + @return (); +} + +// Tokens that can be configured through Angular Material's density theming API. +@function get-density-tokens($config) { + @return (); +} + +// Combines the tokens generated by the above functions into a single map with placeholder values. +// This is used to create token slots. +@function get-token-slots() { + @return token-utils.merge-all( + get-unthemable-tokens(), + get-color-tokens(token-utils.$placeholder-color-config), + get-typography-tokens(token-utils.$placeholder-typography-config), + get-density-tokens(token-utils.$placeholder-density-config) + ); +} diff --git a/src/material/core/tokens/tests/test-validate-tokens.scss b/src/material/core/tokens/tests/test-validate-tokens.scss index 8bcd40124a16..e26402aa563e 100644 --- a/src/material/core/tokens/tests/test-validate-tokens.scss +++ b/src/material/core/tokens/tests/test-validate-tokens.scss @@ -1,8 +1,12 @@ @use 'sass:list'; @use 'sass:map'; -@use '@material/theme/validate' as mdc-validate; +@use '@material/card/elevated-card-theme' as mdc-elevated-card-theme; +@use '@material/card/outlined-card-theme' as mdc-outlined-card-theme; @use '@material/list/list-theme' as mdc-list-theme; +@use '@material/theme/validate' as mdc-validate; +@use '../m2/mdc/elevated-card' as tokens-mdc-elevated-card; @use '../m2/mdc/list' as tokens-mdc-list; +@use '../m2/mdc/outlined-card' as tokens-mdc-outlined-card; @mixin validate-slots( $component, @@ -25,3 +29,13 @@ $slots: tokens-mdc-list.get-token-slots(), $reference: mdc-list-theme.$light-theme ); +@include validate-slots( + $component: 'm2.mdc.elevated-card', + $slots: tokens-mdc-elevated-card.get-token-slots(), + $reference: mdc-elevated-card-theme.$light-theme +); +@include validate-slots( + $component: 'm2.mdc.outlined-card', + $slots: tokens-mdc-outlined-card.get-token-slots(), + $reference: mdc-outlined-card-theme.$light-theme +);