From cdc7ae8d8949aec891618e5efd0e4f3ee56afb53 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Thu, 12 Oct 2023 13:03:51 +0200 Subject: [PATCH] refactor(material/button): switch text button to tokens for color and density Switches the color and density styles for the text button to tokens. --- src/material/button/_button-theme.scss | 65 +++++------- src/material/button/button.scss | 46 +++++---- .../core/tokens/m2/mdc/_text-button.scss | 99 +++++++++++++++++++ .../tokens/tests/test-validate-tokens.scss | 7 ++ 4 files changed, 160 insertions(+), 57 deletions(-) create mode 100644 src/material/core/tokens/m2/mdc/_text-button.scss diff --git a/src/material/button/_button-theme.scss b/src/material/button/_button-theme.scss index d4d248161890..2302833db372 100644 --- a/src/material/button/_button-theme.scss +++ b/src/material/button/_button-theme.scss @@ -14,18 +14,13 @@ @use '../core/typography/typography'; @use '../core/tokens/m2/mdc/button-filled' as tokens-mdc-button-filled; @use '../core/tokens/m2/mdc/button-protected' as tokens-mdc-button-protected; +@use '../core/tokens/m2/mdc/text-button' as tokens-mdc-text-button; @function _on-color($theme, $palette) { $is-dark: inspection.get-theme-type($theme) == dark; @return if(mdc-helpers.variable-safe-contrast-tone($palette, $is-dark) == 'dark', #000, #fff); } -@mixin _button-variant($color) { - @include mdc-button-text-theme.theme(( - label-text-color: $color, - )); -} - @mixin _outlined-button-variant($color) { @include mdc-button-outlined-theme.theme(( label-text-color: $color, @@ -40,42 +35,10 @@ @include mdc-helpers.using-mdc-theme($theme) { $is-dark: inspection.get-theme-type($theme) == dark; $on-surface: mdc-theme-color.prop-value(on-surface); - $surface: mdc-theme-color.prop-value(surface); $disabled-ink-color: rgba($on-surface, if($is-dark, 0.5, 0.38)); - $disabled-container-color: rgba($on-surface, 0.12); $primary: mdc-theme-color.prop-value(primary); - $on-primary: mdc-theme-color.prop-value(on-primary); $secondary: mdc-theme-color.prop-value(secondary); - $on-secondary: mdc-theme-color.prop-value(on-secondary); $error: mdc-theme-color.prop-value(error); - $on-error: mdc-theme-color.prop-value(on-error); - - .mat-mdc-button { - &.mat-unthemed { - @include _button-variant($on-surface); - } - - &.mat-primary { - @include _button-variant($primary); - } - - &.mat-accent { - @include _button-variant($secondary); - } - - &.mat-warn { - @include _button-variant($error); - } - - @include button-theme-private.apply-disabled-style() { - @include mdc-button-text-theme.theme(( - // We need to pass both the disabled and enabled values, because the enabled - // ones apply to anchors while the disabled ones are for buttons. - disabled-label-text-color: $disabled-ink-color, - label-text-color: $disabled-ink-color - )); - } - } .mat-mdc-outlined-button { @include mdc-button-outlined-theme.theme(( @@ -130,6 +93,25 @@ $on-accent: _on-color($theme, $accent); $on-error: _on-color($theme, $error); + .mat-mdc-button { + @include mdc-button-text-theme.theme(tokens-mdc-text-button.get-color-tokens($theme)); + + &.mat-primary { + @include mdc-button-text-theme.theme( + tokens-mdc-text-button.private-get-color-palette-color-tokens($theme, primary)); + } + + &.mat-accent { + @include mdc-button-text-theme.theme( + tokens-mdc-text-button.private-get-color-palette-color-tokens($theme, accent)); + } + + &.mat-warn { + @include mdc-button-text-theme.theme( + tokens-mdc-text-button.private-get-color-palette-color-tokens($theme, warn)); + } + } + .mat-mdc-unelevated-button { $default-color-tokens: tokens-mdc-button-filled.get-color-tokens($theme, $surface, $on-surface); $primary-color-tokens: tokens-mdc-button-filled.get-color-tokens($theme, $primary, $on-primary); @@ -234,6 +216,12 @@ @mixin density($theme) { $density-scale: theming.clamp-density(inspection.get-theme-density($theme), -3); + .mat-mdc-button { + $density-tokens: tokens-mdc-text-button.get-density-tokens($theme); + @include mdc-button-text-theme.theme($density-tokens); + @include button-theme-private.touch-target-density($density-scale); + } + .mat-mdc-raised-button { $density-tokens: tokens-mdc-button-protected.get-density-tokens($theme); @include mdc-button-protected-theme.theme($density-tokens); @@ -246,7 +234,6 @@ @include button-theme-private.touch-target-density($density-scale); } - .mat-mdc-button, .mat-mdc-outlined-button { // Use `mat-mdc-button-base` to increase the specificity over the button's structural styles. &.mat-mdc-button-base { diff --git a/src/material/button/button.scss b/src/material/button/button.scss index f86050e9d34d..3c5f10c2fd43 100644 --- a/src/material/button/button.scss +++ b/src/material/button/button.scss @@ -10,39 +10,49 @@ @use './button-base'; @use '../core/mdc-helpers/mdc-helpers'; +@use '../core/tokens/token-utils'; @use '../core/style/private' as style-private; @use '../core/focus-indicators/private' as focus-indicators-private; @use '../core/tokens/m2/mdc/button-filled' as tokens-mdc-button-filled; @use '../core/tokens/m2/mdc/button-protected' as tokens-mdc-button-protected; +@use '../core/tokens/m2/mdc/text-button' as tokens-mdc-text-button; @include mdc-helpers.disable-mdc-fallback-declarations { @include mdc-button.static-styles-without-ripple($query: mdc-helpers.$mdc-base-styles-query); - // Keys to exclude from the MDC theme config, allowing us to drop styles we don't need. - $override-keys: button-base.mat-private-button-remove-ripple(( - label-text-font: null, - label-text-size: null, - label-text-tracking: null, - label-text-transform: null, - label-text-weight: null, - with-icon-icon-size: null, - label-text-color: inherit, - )); - - // Note that we don't include a feature query, because this mixins declare - // all the "slots" for CSS variables that will be defined in the theme. - .mat-mdc-button { - @include mdc-button-text-theme.theme-styles( - map.merge(mdc-button-text-theme.$light-theme, $override-keys)); - } - .mat-mdc-outlined-button { + // Keys to exclude from the MDC theme config, allowing us to drop styles we don't need. + $override-keys: button-base.mat-private-button-remove-ripple(( + label-text-font: null, + label-text-size: null, + label-text-tracking: null, + label-text-transform: null, + label-text-weight: null, + with-icon-icon-size: null, + label-text-color: inherit, + )); + @include mdc-button-outlined-theme.theme-styles( map.merge(mdc-button-outlined-theme.$light-theme, $override-keys)); } } @include mdc-custom-properties.configure($emit-fallback-values: false, $emit-fallback-vars: false) { + $text-button-slot: tokens-mdc-text-button.get-token-slots(); + + .mat-mdc-button { + @include mdc-button-text-theme.theme-styles($text-button-slot); + @include mdc-button-text-theme.theme(tokens-mdc-text-button.get-unthemable-tokens()); + } + + // MDC uses `:disabled` for disabled styling which doesn't work on anchors. + // Override the color to point to the correct disabled color token. + a.mat-mdc-button[disabled] { + @include token-utils.use-tokens(tokens-mdc-text-button.$prefix, $text-button-slot) { + color: var(#{token-utils.get-token-variable(disabled-label-text-color)}); + } + } + // Note that we don't include a feature query, because this mixins declare // all the "slots" for CSS variables that will be defined in the theme. .mat-mdc-unelevated-button { diff --git a/src/material/core/tokens/m2/mdc/_text-button.scss b/src/material/core/tokens/m2/mdc/_text-button.scss new file mode 100644 index 000000000000..0768315d7657 --- /dev/null +++ b/src/material/core/tokens/m2/mdc/_text-button.scss @@ -0,0 +1,99 @@ +@use 'sass:map'; +@use '../../token-utils'; +@use '../../../style/sass-utils'; +@use '../../../theming/inspection'; +@use '../../../theming/theming'; + +// The prefix used to generate the fully qualified name for tokens in this file. +$prefix: (mdc, text-button); + +// 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 ( + container-shape: 4px, + + // TODO: handle these through the typography styles eventually. + label-text-font: null, + label-text-size: null, + label-text-tracking: null, + label-text-transform: null, + label-text-weight: null, + + // ============================================================================================= + // = TOKENS NOT USED IN ANGULAR MATERIAL = + // ============================================================================================= + hover-label-text-color: null, + focus-label-text-color: null, + pressed-label-text-color: null, + focus-state-layer-color: null, + focus-state-layer-opacity: null, + hover-state-layer-color: null, + hover-state-layer-opacity: null, + pressed-state-layer-color: null, + pressed-state-layer-opacity: null, + with-icon-disabled-icon-color: null, + with-icon-focus-icon-color: null, + with-icon-hover-icon-color: null, + with-icon-icon-color: null, + with-icon-icon-size: null, + with-icon-pressed-icon-color: null, + focus-ring-color: null, + focus-ring-offset: null, + keep-touch-target: false, + ); +} + +// Tokens that can be configured through Angular Material's color theming API. +@function get-color-tokens($theme) { + $is-dark: inspection.get-theme-type($theme) == dark; + $on-surface: if($is-dark, #fff, #000); + + @return ( + label-text-color: $on-surface, + disabled-label-text-color: rgba($on-surface, if($is-dark, 0.5, 0.38)), + ); +} + +// Generates the mapping for the properties that change based on the button palette color. +@function private-get-color-palette-color-tokens($theme, $palette-name) { + $palette: map.get($theme, $palette-name); + + @return ( + label-text-color: inspection.get-theme-color($theme, $palette-name) + ); +} + +// Tokens that can be configured through Angular Material's typography theming API. +@function get-typography-tokens($theme) { + @return (); +} + +// Tokens that can be configured through Angular Material's density theming API. +@function get-density-tokens($theme) { + $scale: theming.clamp-density(inspection.get-theme-density($theme), -3); + + @return ( + container-height: map.get(( + 0: 36px, + -1: 32px, + -2: 28px, + -3: 24px, + ), $scale), + ); +} + +// 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 sass-utils.deep-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 9e630d2ad40e..561eca41b75c 100644 --- a/src/material/core/tokens/tests/test-validate-tokens.scss +++ b/src/material/core/tokens/tests/test-validate-tokens.scss @@ -3,6 +3,7 @@ @use '@material/button/button-protected-theme' as mdc-button-protected-theme; @use '@material/button/button-filled-theme' as mdc-button-filled-theme; +@use '@material/button/button-text-theme' as mdc-button-text-theme; @use '@material/card/elevated-card-theme' as mdc-elevated-card-theme; @use '@material/card/outlined-card-theme' as mdc-outlined-card-theme; @use '@material/checkbox/checkbox-theme' as mdc-checkbox-theme; @@ -27,6 +28,7 @@ @use '../m2/mdc/button-protected' as tokens-mdc-button-protected; @use '../m2/mdc/button-filled' as tokens-mdc-button-filled; +@use '../m2/mdc/text-button' as tokens-mdc-text-button; @use '../m2/mdc/circular-progress' as tokens-mdc-circular-progress; @use '../m2/mdc/linear-progress' as tokens-mdc-linear-progress; @use '../m2/mdc/elevated-card' as tokens-mdc-elevated-card; @@ -170,3 +172,8 @@ $slots: tokens-mdc-button-protected.get-token-slots(), $reference: mdc-button-protected-theme.$light-theme ); +@include validate-slots( + $component: 'm2.mdc.text-button', + $slots: tokens-mdc-text-button.get-token-slots(), + $reference: mdc-button-text-theme.$light-theme +);