From 2963b7aaca3d91fe62f592707ca764f818a5877e Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Tue, 21 Nov 2023 17:00:49 +0000 Subject: [PATCH] feat(material-experimental/theming): add M3 icon-button & fab support (#28157) * feat(material-experimental/theming): add M3 icon-button support * feat(material-experimental/theming): add M3 fab support --- src/dev-app/theme-m3.scss | 6 + .../theming/_custom-tokens.scss | 39 +++++ .../theming/_m3-density.scss | 7 + .../theming/_m3-tokens.scss | 25 +++ src/material/button/_fab-theme.scss | 97 ++++++++---- src/material/button/_icon-button-theme.scss | 145 +++++++++++------- src/material/core/tokens/m2/_index.scss | 8 + 7 files changed, 239 insertions(+), 88 deletions(-) diff --git a/src/dev-app/theme-m3.scss b/src/dev-app/theme-m3.scss index db5f48009ccd..86417c0a4b23 100644 --- a/src/dev-app/theme-m3.scss +++ b/src/dev-app/theme-m3.scss @@ -39,8 +39,10 @@ html { @include mat.card-theme($light-theme); @include mat.checkbox-theme($light-theme); @include mat.divider-theme($light-theme); + @include mat.fab-theme($light-theme); @include mat.form-field-theme($light-theme); @include mat.grid-list-theme($light-theme); + @include mat.icon-button-theme($light-theme); @include mat.icon-theme($light-theme); @include mat.input-theme($light-theme); @include mat.list-theme($light-theme); @@ -74,8 +76,10 @@ html { @include mat.card-color($dark-theme); @include mat.checkbox-color($dark-theme); @include mat.divider-color($dark-theme); + @include mat.fab-color($dark-theme); @include mat.form-field-color($dark-theme); @include mat.grid-list-color($dark-theme); + @include mat.icon-button-color($dark-theme); @include mat.icon-color($dark-theme); @include mat.input-color($dark-theme); @include mat.list-color($dark-theme); @@ -108,8 +112,10 @@ html { @include mat.card-density($scale-theme); @include mat.checkbox-density($scale-theme); @include mat.divider-density($scale-theme); + @include mat.fab-density($scale-theme); @include mat.form-field-density($scale-theme); @include mat.grid-list-density($scale-theme); + @include mat.icon-button-density($scale-theme); @include mat.icon-density($scale-theme); @include mat.input-density($scale-theme); @include mat.list-density($scale-theme); diff --git a/src/material-experimental/theming/_custom-tokens.scss b/src/material-experimental/theming/_custom-tokens.scss index 247552c5c37c..5d681e980f65 100644 --- a/src/material-experimental/theming/_custom-tokens.scss +++ b/src/material-experimental/theming/_custom-tokens.scss @@ -46,6 +46,28 @@ ); } +/// Generates custom tokens for the mat-fab. +/// @param {Map} $systems The MDC system tokens +/// @param {Boolean} $exclude-hardcoded Whether to exclude hardcoded token values +/// @return {Map} A set of custom tokens for the mat-fab +@function fab($systems, $exclude-hardcoded) { + @return ( + foreground-color: map.get($systems, md-sys-color, on-primary-container), + state-layer-color: map.get($systems, md-sys-color, primary-container), + ripple-color: mat.private-safe-color-change( + map.get($systems, md-sys-color, on-primary-container), + $alpha: map.get($systems, md-sys-state, pressed-state-layer-opacity) + ), + hover-state-layer-opacity: map.get($systems, md-sys-state, hover-state-layer-opacity), + focus-state-layer-opacity: map.get($systems, md-sys-state, focus-state-layer-opacity), + pressed-state-layer-opacity: map.get($systems, md-sys-state, pressed-state-layer-opacity), + disabled-state-container-color: mat.private-safe-color-change( + map.get($systems, md-sys-color, on-surface), $alpha: 0.12), + disabled-state-foreground-color: mat.private-safe-color-change( + map.get($systems, md-sys-color, on-surface), $alpha: 0.38), + ); +} + /// Generates custom tokens for the mat-form-field. /// @param {Map} $systems The MDC system tokens /// @param {Boolean} $exclude-hardcoded Whether to exclude hardcoded token values @@ -96,6 +118,23 @@ ); } +/// Generates custom tokens for the mat-icon-button. +/// @param {Map} $systems The MDC system tokens +/// @param {Boolean} $exclude-hardcoded Whether to exclude hardcoded token values +/// @return {Map} A set of custom tokens for the mat-icon-button +@function icon-button($systems, $exclude-hardcoded) { + @return ( + state-layer-color: map.get($systems, md-sys-color, on-surface-variant), + ripple-color: mat.private-safe-color-change( + map.get($systems, md-sys-color, on-surface-variant), + $alpha: map.get($systems, md-sys-state, pressed-state-layer-opacity) + ), + hover-state-layer-opacity: map.get($systems, md-sys-state, hover-state-layer-opacity), + focus-state-layer-opacity: map.get($systems, md-sys-state, focus-state-layer-opacity), + pressed-state-layer-opacity: map.get($systems, md-sys-state, pressed-state-layer-opacity), + ); +} + /// Generates custom tokens for the mat-menu. /// @param {Map} $systems The MDC system tokens /// @param {Boolean} $exclude-hardcoded Whether to exclude hardcoded token values diff --git a/src/material-experimental/theming/_m3-density.scss b/src/material-experimental/theming/_m3-density.scss index 5ba148483a38..b6532be1d063 100644 --- a/src/material-experimental/theming/_m3-density.scss +++ b/src/material-experimental/theming/_m3-density.scss @@ -21,7 +21,12 @@ $_density-tokens: ( ), (mdc, circular-progress): (), (mdc, elevated-card): (), + (mdc, extended-fab): (), + (mdc, fab): (), (mdc, filled-text-field): (), + (mdc, icon-button): ( + state-layer-size: (48px, 44px, 40px, 36px, 32px, 28px), + ), (mdc, linear-progress): (), (mdc, list): ( list-item-one-line-container-height: (48px, 44px, 40px, 36px, 32px, 24px), @@ -45,9 +50,11 @@ $_density-tokens: ( // Custom Angular Material tokens (mat, card): (), (mat, divider): (), + (mat, fab): (), (mat, form-fild): (), (mat, grid-list): (), (mat, icon): (), + (mat, icon-button): (), (mat, menu): (), (mat, optgroup): (), (mat, option): (), diff --git a/src/material-experimental/theming/_m3-tokens.scss b/src/material-experimental/theming/_m3-tokens.scss index 114293f88529..1ecffc4f65aa 100644 --- a/src/material-experimental/theming/_m3-tokens.scss +++ b/src/material-experimental/theming/_m3-tokens.scss @@ -187,11 +187,26 @@ mdc-tokens.md-comp-elevated-card-values($systems, $exclude-hardcoded), $token-slots ), + _namespace-tokens( + (mdc, fab), + mdc-tokens.md-comp-fab-primary-values($systems, $exclude-hardcoded), + $token-slots + ), + _namespace-tokens( + (mdc, extended-fab), + mdc-tokens.md-comp-extended-fab-primary-values($systems, $exclude-hardcoded), + $token-slots + ), _namespace-tokens( (mdc, filled-text-field), mdc-tokens.md-comp-filled-text-field-values($systems, $exclude-hardcoded), $token-slots ), + _namespace-tokens( + (mdc, icon-button), + mdc-tokens.md-comp-icon-button-values($systems, $exclude-hardcoded), + $token-slots + ), _namespace-tokens( (mdc, linear-progress), mdc-tokens.md-comp-linear-progress-indicator-values($systems, $exclude-hardcoded), @@ -260,6 +275,11 @@ custom-tokens.divider($systems, $exclude-hardcoded), $token-slots ), + _namespace-tokens( + (mat, fab), + custom-tokens.fab($systems, $exclude-hardcoded), + $token-slots + ), _namespace-tokens( (mat, form-field), custom-tokens.form-field($systems, $exclude-hardcoded), @@ -275,6 +295,11 @@ custom-tokens.icon($systems, $exclude-hardcoded), $token-slots ), + _namespace-tokens( + (mat, icon-button), + custom-tokens.icon-button($systems, $exclude-hardcoded), + $token-slots + ), _namespace-tokens( (mat, menu), custom-tokens.menu($systems, $exclude-hardcoded), diff --git a/src/material/button/_fab-theme.scss b/src/material/button/_fab-theme.scss index 4841b30f2142..49d8b109fc36 100644 --- a/src/material/button/_fab-theme.scss +++ b/src/material/button/_fab-theme.scss @@ -1,7 +1,7 @@ +@use 'sass:map'; @use '@material/fab/fab' as mdc-fab; @use '@material/fab/fab-theme' as mdc-fab-theme; @use '@material/fab/extended-fab-theme' as mdc-extended-fab-theme; - @use '../core/mdc-helpers/mdc-helpers'; @use '../core/style/sass-utils'; @use '../core/theming/theming'; @@ -13,12 +13,17 @@ @use '../core/typography/typography'; @mixin base($theme) { - // Add default values for tokens not related to color, typography, or density. - @include sass-utils.current-selector-or-root() { - @include mdc-fab-theme.theme(tokens-mdc-fab.get-unthemable-tokens()); - @include mdc-extended-fab-theme.theme( - tokens-mdc-extended-fab.get-unthemable-tokens() - ); + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme, base)); + } + @else { + // Add default values for tokens not related to color, typography, or density. + @include sass-utils.current-selector-or-root() { + @include mdc-fab-theme.theme(tokens-mdc-fab.get-unthemable-tokens()); + @include mdc-extended-fab-theme.theme( + tokens-mdc-extended-fab.get-unthemable-tokens() + ); + } } } @@ -38,51 +43,79 @@ } @mixin color($theme) { - @include sass-utils.current-selector-or-root() { - @include _fab-variant($theme, null); + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme, color)); + } + @else { + @include sass-utils.current-selector-or-root() { + @include _fab-variant($theme, null); - .mat-mdc-fab, - .mat-mdc-mini-fab { - &.mat-primary { - @include _fab-variant($theme, primary); - } + .mat-mdc-fab, + .mat-mdc-mini-fab { + &.mat-primary { + @include _fab-variant($theme, primary); + } - &.mat-accent { - @include _fab-variant($theme, accent); - } + &.mat-accent { + @include _fab-variant($theme, accent); + } - &.mat-warn { - @include _fab-variant($theme, warn); + &.mat-warn { + @include _fab-variant($theme, warn); + } } } } } @mixin typography($theme) { - @include mdc-helpers.using-mdc-typography($theme) { - @include mdc-fab.without-ripple($query: mdc-helpers.$mdc-typography-styles-query); + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme, typography)); } + @else { + @include mdc-helpers.using-mdc-typography($theme) { + @include mdc-fab.without-ripple($query: mdc-helpers.$mdc-typography-styles-query); + } - $typography-tokens: tokens-mdc-extended-fab.get-typography-tokens($theme); - @include sass-utils.current-selector-or-root() { - @include mdc-extended-fab-theme.theme($typography-tokens); + $typography-tokens: tokens-mdc-extended-fab.get-typography-tokens($theme); + @include sass-utils.current-selector-or-root() { + @include mdc-extended-fab-theme.theme($typography-tokens); + } } } @mixin density($theme) { + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme, density)); + } + @else {} } @mixin theme($theme) { @include theming.private-check-duplicate-theme-styles($theme, 'mat-fab') { - @include base($theme); - @if inspection.theme-has($theme, color) { - @include color($theme); - } - @if inspection.theme-has($theme, density) { - @include density($theme); + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme)); } - @if inspection.theme-has($theme, typography) { - @include typography($theme); + @else { + @include base($theme); + @if inspection.theme-has($theme, color) { + @include color($theme); + } + @if inspection.theme-has($theme, density) { + @include density($theme); + } + @if inspection.theme-has($theme, typography) { + @include typography($theme); + } } } } + +@mixin _theme-from-tokens($tokens) { + @if ($tokens != ()) { + @include mdc-extended-fab-theme.theme(map.get($tokens, tokens-mdc-extended-fab.$prefix)); + @include mdc-fab-theme.theme(map.get($tokens, tokens-mdc-fab.$prefix)); + @include token-utils.create-token-values( + tokens-mat-fab.$prefix, map.get($tokens, tokens-mat-fab.$prefix)); + } +} diff --git a/src/material/button/_icon-button-theme.scss b/src/material/button/_icon-button-theme.scss index 511e9ac55731..67833f41b768 100644 --- a/src/material/button/_icon-button-theme.scss +++ b/src/material/button/_icon-button-theme.scss @@ -1,9 +1,9 @@ +@use 'sass:map'; @use 'sass:math'; @use '@material/density/functions' as mdc-density-functions; @use '@material/icon-button/icon-button-theme' as mdc-icon-button-theme; @use '../core/tokens/m2/mdc/icon-button' as tokens-mdc-icon-button; @use '../core/tokens/m2/mat/icon-button' as tokens-mat-icon-button; - @use '../core/style/sass-utils'; @use './button-base'; @use '../core/tokens/token-utils'; @@ -12,9 +12,14 @@ @mixin base($theme) { - // Add default values for tokens not related to color, typography, or density. - @include sass-utils.current-selector-or-root() { - @include mdc-icon-button-theme.theme(tokens-mdc-icon-button.get-unthemable-tokens()); + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme, base)); + } + @else { + // Add default values for tokens not related to color, typography, or density. + @include sass-utils.current-selector-or-root() { + @include mdc-icon-button-theme.theme(tokens-mdc-icon-button.get-unthemable-tokens()); + } } } @@ -34,74 +39,102 @@ } @mixin color($theme) { - .mat-mdc-icon-button { - @include _icon-button-variant($theme, null); + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme, color)); + } + @else { + .mat-mdc-icon-button { + @include _icon-button-variant($theme, null); - &.mat-primary { - @include _icon-button-variant($theme, primary); - } + &.mat-primary { + @include _icon-button-variant($theme, primary); + } - &.mat-accent { - @include _icon-button-variant($theme, accent); - } + &.mat-accent { + @include _icon-button-variant($theme, accent); + } - &.mat-warn { - @include _icon-button-variant($theme, warn); + &.mat-warn { + @include _icon-button-variant($theme, warn); + } } } } -@mixin typography($theme) {} +@mixin typography($theme) { + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme, typography)); + } + @else {} +} @mixin density($theme) { - $icon-size: 24px; - $density-scale: inspection.get-theme-density($theme); - // Manually apply the expected density theming, and include the padding - // as it was applied before - $calculated-size: mdc-density-functions.prop-value( - $density-config: ( - size: ( - default: 48px, - maximum: 48px, - minimum: 28px, + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme, density)); + } + @else { + $icon-size: 24px; + $density-scale: inspection.get-theme-density($theme); + // Manually apply the expected density theming, and include the padding + // as it was applied before + $calculated-size: mdc-density-functions.prop-value( + $density-config: ( + size: ( + default: 48px, + maximum: 48px, + minimum: 28px, + ), ), - ), - $density-scale: $density-scale, - $property-name: size, - ); - - // Use `mat-mdc-button-base` to increase the specificity over the button's structural styles. - .mat-mdc-icon-button.mat-mdc-button-base { - // Match the styles that used to be present. This is necessary for backwards - // compat to match the previous implementations selector count (two classes). - @include mdc-icon-button-theme.theme(( - state-layer-size: $calculated-size, - )); - - // TODO: Switch calculated-size to "var(--mdc-icon-button-state-layer-size)" - // Currently fails validation because the variable is "undefined" - // in the sass stack. - // TODO: Switch icon-size to "var(--mdc-icon-button-icon-size)". Currently - // fails validation because the variable is "undefined" in the sass stack. - width: var(--mdc-icon-button-state-layer-size); - height: var(--mdc-icon-button-state-layer-size); - padding: math.div($calculated-size - $icon-size, 2); - - @include button-base.mat-private-button-touch-target-density($density-scale); + $density-scale: $density-scale, + $property-name: size, + ); + + // Use `mat-mdc-button-base` to increase the specificity over the button's structural styles. + .mat-mdc-icon-button.mat-mdc-button-base { + // Match the styles that used to be present. This is necessary for backwards + // compat to match the previous implementations selector count (two classes). + @include mdc-icon-button-theme.theme(( + state-layer-size: $calculated-size, + )); + + // TODO: Switch calculated-size to "var(--mdc-icon-button-state-layer-size)" + // Currently fails validation because the variable is "undefined" + // in the sass stack. + // TODO: Switch icon-size to "var(--mdc-icon-button-icon-size)". Currently + // fails validation because the variable is "undefined" in the sass stack. + width: var(--mdc-icon-button-state-layer-size); + height: var(--mdc-icon-button-state-layer-size); + padding: math.div($calculated-size - $icon-size, 2); + + @include button-base.mat-private-button-touch-target-density($density-scale); + } } } @mixin theme($theme) { @include theming.private-check-duplicate-theme-styles($theme, 'mat-icon-button') { - @include base($theme); - @if inspection.theme-has($theme, color) { - @include color($theme); + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme)); } - @if inspection.theme-has($theme, density) { - @include density($theme); - } - @if inspection.theme-has($theme, typography) { - @include typography($theme); + @else { + @include base($theme); + @if inspection.theme-has($theme, color) { + @include color($theme); + } + @if inspection.theme-has($theme, density) { + @include density($theme); + } + @if inspection.theme-has($theme, typography) { + @include typography($theme); + } } } } + +@mixin _theme-from-tokens($tokens) { + @if ($tokens != ()) { + @include mdc-icon-button-theme.theme(map.get($tokens, tokens-mdc-icon-button.$prefix)); + @include token-utils.create-token-values( + tokens-mat-icon-button.$prefix, map.get($tokens, tokens-mat-icon-button.$prefix)); + } +} diff --git a/src/material/core/tokens/m2/_index.scss b/src/material/core/tokens/m2/_index.scss index bab478ea17ee..9a8ee24f871b 100644 --- a/src/material/core/tokens/m2/_index.scss +++ b/src/material/core/tokens/m2/_index.scss @@ -3,9 +3,11 @@ @use '../../style/sass-utils'; @use './mat/card' as tokens-mat-card; @use './mat/divider' as tokens-mat-divider; +@use './mat/fab' as tokens-mat-fab; @use './mat/form-field' as tokens-mat-form-field; @use './mat/grid-list' as tokens-mat-grid-list; @use './mat/icon' as tokens-mat-icon; +@use './mat/icon-button' as tokens-mat-icon-button; @use './mat/menu' as tokens-mat-menu; @use './mat/option' as tokens-mat-option; @use './mat/optgroup' as tokens-mat-optgroup; @@ -27,6 +29,8 @@ @use './mdc/circular-progress' as tokens-mdc-circular-progress; @use './mdc/dialog' as tokens-mdc-dialog; @use './mdc/elevated-card' as tokens-mdc-elevated-card; +@use './mdc/extended-fab' as tokens-mdc-extended-fab; +@use './mdc/fab' as tokens-mdc-fab; @use './mdc/filled-text-field' as tokens-mdc-filled-text-field; @use './mdc/icon-button' as tokens-mdc-icon-button; @use './mdc/linear-progress' as tokens-mdc-linear-progress; @@ -86,9 +90,11 @@ @return sass-utils.deep-merge-all( _get-tokens-for-module($theme, tokens-mat-card), _get-tokens-for-module($theme, tokens-mat-divider), + _get-tokens-for-module($theme, tokens-mat-fab), _get-tokens-for-module($theme, tokens-mat-form-field), _get-tokens-for-module($theme, tokens-mat-grid-list), _get-tokens-for-module($theme, tokens-mat-icon), + _get-tokens-for-module($theme, tokens-mat-icon-button), _get-tokens-for-module($theme, tokens-mat-menu), _get-tokens-for-module($theme, tokens-mat-optgroup), _get-tokens-for-module($theme, tokens-mat-option), @@ -110,6 +116,8 @@ _get-tokens-for-module($theme, tokens-mdc-circular-progress), _get-tokens-for-module($theme, tokens-mdc-dialog), _get-tokens-for-module($theme, tokens-mdc-elevated-card), + _get-tokens-for-module($theme, tokens-mdc-extended-fab), + _get-tokens-for-module($theme, tokens-mdc-fab), _get-tokens-for-module($theme, tokens-mdc-filled-text-field), _get-tokens-for-module($theme, tokens-mdc-icon-button), _get-tokens-for-module($theme, tokens-mdc-linear-progress),