Skip to content

Commit

Permalink
refactor(material/button): clean up fab tokens
Browse files Browse the repository at this point in the history
Reworks the tokens setup for the FAB by:
* Introducing a custom tokens map. It was necessary both for these changes and will come in handy later for the ripples.
* Using tokens specifically for the disabled state instead of passing in the non-disabled ones. This aligns the FAB with the rest of the library and allows us to reduce the specificity.
* Introducing a tokens specifically for the icon/text color. Currently we have an ad-hoc variable declared directly in the component styles.
* Using our own mixins for the elevation instead of MDC's. Our mixin calls into MDC eventually, but it has some safeguards as well.
* Generating the tokens that we pass into MDC's theme using our APIs, instead of creating the map directly.
  • Loading branch information
crisbeto committed Oct 25, 2023
1 parent d5dd878 commit 290b109
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 69 deletions.
64 changes: 22 additions & 42 deletions src/material/button/_fab-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
@use '../core/theming/inspection';
@use '../core/tokens/m2/mdc/fab' as tokens-mdc-fab;
@use '../core/tokens/m2/mdc/extended-fab' as tokens-mdc-extended-fab;
@use '../core/tokens/m2/mat/fab' as tokens-mat-fab;
@use '../core/tokens/token-utils';
@use '../core/typography/typography';

@mixin base($theme) {
Expand All @@ -21,37 +23,25 @@
}
}

@mixin _fab-variant($foreground, $background) {
$color-tokens: (
container-color: $background,
icon-color: $foreground
@mixin _fab-variant($theme, $palette) {
$mdc-tokens: if($palette,
tokens-mdc-fab.private-get-color-palette-color-tokens($theme, $palette),
tokens-mdc-fab.get-color-tokens($theme)
);
@include mdc-fab-theme.theme($color-tokens);

--mat-mdc-fab-color: #{$foreground};
}
$mat-tokens: if($palette,
tokens-mat-fab.private-get-color-palette-color-tokens($theme, $palette),
tokens-mat-fab.get-color-tokens($theme)
);

@function white-or-black($color, $is-dark) {
@return if(mdc-helpers.variable-safe-contrast-tone($color, $is-dark) == 'dark', #000, #fff);
@include mdc-fab-theme.theme($mdc-tokens);
@include token-utils.create-token-values(tokens-mat-fab.$prefix, $mat-tokens);
}

@mixin color($theme) {
$is-dark: inspection.get-theme-type($theme) == dark;

$surface: inspection.get-theme-color($theme, background, card);
$primary: inspection.get-theme-color($theme, primary);
$accent: inspection.get-theme-color($theme, accent);
$warn: inspection.get-theme-color($theme, warn);

$on-surface: white-or-black($surface, $is-dark);
$on-primary: white-or-black($primary, $is-dark);
$on-accent: white-or-black($accent, $is-dark);
$on-warn: white-or-black($warn, $is-dark);

$disabled: rgba($on-surface, 0.12);
$on-disabled: rgba($on-surface, if($is-dark, 0.5, 0.38));

@include sass-utils.current-selector-or-root() {
@include _fab-variant($theme, null);

// TODO(wagnermaciel): The ripple-theme-styles mixin depends heavily on
// being wrapped by using-mdc-theme. This workaround needs to be
// revisited w/ a more holistic solution.
Expand All @@ -60,28 +50,18 @@
@include mdc-helpers.using-mdc-theme($theme) {
@include button-theme-private.ripple-theme-styles($theme, true);
}
}

:disabled, a[disabled='true'] {
@include button-theme-private.apply-disabled-style() {
@include _fab-variant($on-disabled, $disabled);
&.mat-primary {
@include _fab-variant($theme, primary);
}
}

.mat-unthemed {
@include _fab-variant($on-surface, $surface);
}

.mat-primary {
@include _fab-variant($on-primary, $primary);
}

.mat-accent {
@include _fab-variant($on-accent, $accent);
}
&.mat-accent {
@include _fab-variant($theme, accent);
}

.mat-warn {
@include _fab-variant($on-warn, $warn);
&.mat-warn {
@include _fab-variant($theme, warn);
}
}
}
}
Expand Down
48 changes: 24 additions & 24 deletions src/material/button/fab.scss
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
@use '@material/fab' as mdc-fab;
@use '@material/fab/extended-fab-theme' as mdc-extended-fab-theme;
@use '@material/fab/fab-theme' as mdc-fab-theme;
@use '@material/elevation/elevation-theme' as mdc-elevation-theme;
@use '@material/theme/custom-properties' as mdc-custom-properties;

@use './button-base';
@use '../core/style/elevation';
@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/extended-fab' as m2-mdc-extended-fab;
@use '../core/tokens/m2/mdc/fab' as m2-mdc-fab;
@use '../core/tokens/m2/mdc/extended-fab' as tokens-mdc-extended-fab;
@use '../core/tokens/m2/mdc/fab' as tokens-mdc-fab;
@use '../core/tokens/m2/mat/fab' as tokens-mat-fab;

@include mdc-custom-properties.configure($emit-fallback-values: false, $emit-fallback-vars: false) {
$mdc-fab-token-slots: m2-mdc-fab.get-token-slots();
$mdc-extended-fab-token-slots: m2-mdc-extended-fab.get-token-slots();
$mdc-fab-token-slots: tokens-mdc-fab.get-token-slots();
$mdc-extended-fab-token-slots: tokens-mdc-extended-fab.get-token-slots();

// Add the MDC fab static styles.
@include mdc-fab.static-styles();
Expand All @@ -34,33 +36,31 @@
@include button-base.mat-private-button-interactive();
@include button-base.mat-private-button-touch-target(true);
@include style-private.private-animation-noop();
@include elevation.elevation(6);
flex-shrink: 0; // Prevent the button from shrinking since it's always supposed to be a circle.

@include mdc-helpers.disable-mdc-fallback-declarations {
// TODO(crisbeto): the elevation can be controlled using `container-elevation` in `theme-styles`
// however when it is passed in, MDC throws an error that `container-shadow-color` isn't
// passed in. When `container-shadow-color` is passed in, MDC throws another error, because
// the produced value isn't valid CSS. Eventually we should switch to it once it's fixed.
@include mdc-elevation-theme.elevation(6);

&:hover, &:focus {
@include mdc-elevation-theme.elevation(8);
@include token-utils.use-tokens(tokens-mat-fab.$prefix, tokens-mat-fab.get-token-slots()) {
@include token-utils.create-token-slot(color, foreground-color, inherit);
}
}

&:active, &:focus:active {
@include mdc-elevation-theme.elevation(12);
}
&:hover, &:focus {
@include elevation.elevation(8);
}

@include button-base.mat-private-button-disabled {
@include mdc-elevation-theme.elevation(0);
}
&:active, &:focus:active {
@include elevation.elevation(12);
}

// TODO(crisbeto): `theme-styles` doesn't allow for the color to be controlled. Define a custom
// variable for now so we have something to target.
color: var(--mat-mdc-fab-color, inherit);
@include button-base.mat-private-button-disabled {
@include elevation.elevation(0);

// Prevent the button from shrinking since it's always supposed to be a circle.
flex-shrink: 0;
@include token-utils.use-tokens(tokens-mat-fab.$prefix, tokens-mat-fab.get-token-slots()) {
@include token-utils.create-token-slot(color, disabled-state-foreground-color);
@include token-utils.create-token-slot(background-color, disabled-state-container-color);
}
}

// MDC adds some styles to fab and mini-fab that conflict with some of our focus indicator
// styles and don't actually do anything. This undoes those conflicting styles.
Expand Down
57 changes: 57 additions & 0 deletions src/material/core/tokens/m2/mat/_fab.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
@use '../../token-utils';
@use '../../../theming/inspection';
@use '../../../style/sass-utils';

// The prefix used to generate the fully qualified name for tokens in this file.
$prefix: (mat, fab);

// 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($theme) {
$is-dark: inspection.get-theme-type($theme) == dark;
$surface: if($is-dark, #fff, #000);

@return (
// Color of icons and text projected into a FAB.
foreground-color: inspection.get-theme-color($theme, foreground, base),

// MDC doesn't have tokens for disabled FABs so we need to implemented them ourselves.
// Background color of the container when the FAB is disabled.
disabled-state-container-color: rgba($surface, 0.12),
// Color of the icons and projected text when the FAB is disabled.
disabled-state-foreground-color: rgba($surface, if($is-dark, 0.5, 0.38)),
);
}

// Generates the mapping for the properties that change based on the FAB palette color.
@function private-get-color-palette-color-tokens($theme, $palette-name) {
@return (
foreground-color: inspection.get-theme-color($theme, $palette-name, default-contrast),
);
}

// 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) {
@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 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)
);
}
17 changes: 14 additions & 3 deletions src/material/core/tokens/m2/mdc/_fab.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ $ripple-target: '.mdc-fab__ripple';
container-shape: 50%,
icon-size: 24px,

// We don't use this token, because it doesn't set the color of any text inside the FAB.
// We create a custom token for it instead.
icon-color: null,

container-elevation: null,
container-height: null,
container-shadow-color: null,
Expand Down Expand Up @@ -56,10 +60,17 @@ $ripple-target: '.mdc-fab__ripple';

// Tokens that can be configured through Angular Material's color theming API.
@function get-color-tokens($theme) {
$surface: inspection.get-theme-color($theme, primary, default);
$on-surface: inspection.get-theme-color($theme, primary, default-contrast);
@return (
// Background color of the FAB.
container-color: inspection.get-theme-color($theme, background, card),
);
}

@return (container-color: $surface, icon-color: $on-surface);
// Generates the mapping for the properties that change based on the FAB palette color.
@function private-get-color-palette-color-tokens($theme, $palette-name) {
@return (
container-color: inspection.get-theme-color($theme, $palette-name, default),
);
}

// Tokens that can be configured through Angular Material's typography theming API.
Expand Down

0 comments on commit 290b109

Please sign in to comment.