Skip to content

Commit

Permalink
refactor(material/button): handle disabled state of icon buttons thro…
Browse files Browse the repository at this point in the history
…ugh tokens

* Fixes that icon buttons weren't using the right token for disabled state.
* Fixes that the specificity of the icon button disabled styles was really high.
* Moves the logic for generating the palette tokens into the icon button tokens file.
* Fixes one place where we were referencing a token directly instead of using the API to generate the name.
  • Loading branch information
crisbeto committed Oct 24, 2023
1 parent e2d9cfa commit 13b8ad1
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 48 deletions.
19 changes: 19 additions & 0 deletions src/dev-app/button/button-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,25 @@ <h4 class="demo-section-header"> Icon Buttons [mat-icon-button]</h4>
</button>
</section>

<h4 class="demo-section-header"> Icon Button Anchors [mat-icon-button]</h4>
<section>
<a href="#" mat-icon-button>
<mat-icon>cached</mat-icon>
</a>
<a href="#" mat-icon-button color="primary">
<mat-icon>cached</mat-icon>
</a>
<a href="#" mat-icon-button color="accent">
<mat-icon>backup</mat-icon>
</a>
<a href="#" mat-icon-button color="warn">
<mat-icon>trending_up</mat-icon>
</a>
<a href="#" mat-icon-button disabled>
<mat-icon>visibility</mat-icon>
</a>
</section>

<h4 class="demo-section-header">Fab Buttons [mat-fab]</h4>
<section>
<button mat-fab>
Expand Down
43 changes: 12 additions & 31 deletions src/material/button/_icon-button-theme.scss
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
@use 'sass:math';
@use '@material/density/functions' as mdc-density-functions;
@use '@material/icon-button/icon-button-theme' as mdc-icon-button-theme;
@use '@material/theme/theme-color' as mdc-theme-color;
@use '../core/style/sass-utils';
@use '../core/tokens/m2/mdc/icon-button' as tokens-mdc-icon-button;

Expand All @@ -11,19 +10,12 @@

$_icon-size: 24px;

// TODO(crisbeto): move these into tokens
@mixin _ripple-color($color) {
--mat-mdc-button-persistent-ripple-color: #{$color};
--mat-mdc-button-ripple-color: #{rgba($color, 0.1)};
}

@function _variable-safe-contrast-tone($value, $is-dark) {
@if ($value == 'dark' or $value == 'light' or type-of($value) == 'color') {
@return mdc-theme-color.contrast-tone($value);
}

@return if($is-dark, 'light', 'dark');
}

@mixin base($theme) {
// Add default values for tokens not related to color, typography, or density.
@include sass-utils.current-selector-or-root() {
Expand All @@ -33,45 +25,34 @@ $_icon-size: 24px;

@mixin color($theme) {
$color-tokens: tokens-mdc-icon-button.get-color-tokens($theme);
$surface: inspection.get-theme-color($theme, background, card);
$is-dark: inspection.get-theme-type($theme) == dark;
$on-surface: if(_variable-safe-contrast-tone($surface, $is-dark) == 'dark', #000, #fff);

.mat-mdc-icon-button {
@include button-theme-private.ripple-theme-styles($theme, false);
@include mdc-icon-button-theme.theme($color-tokens);
@include _ripple-color($on-surface);
@include _ripple-color(if($is-dark, #fff, #000));

&.mat-primary {
$color: inspection.get-theme-color($theme, primary);
@include mdc-icon-button-theme.theme((icon-color: $color));
@include _ripple-color($color);
@include _ripple-color(inspection.get-theme-color($theme, primary));
@include mdc-icon-button-theme.theme(
tokens-mdc-icon-button.private-get-color-palette-color-tokens($theme, primary));
}

&.mat-accent {
$color: inspection.get-theme-color($theme, accent);
@include mdc-icon-button-theme.theme((icon-color: $color));
@include _ripple-color($color);
@include _ripple-color(inspection.get-theme-color($theme, accent));
@include mdc-icon-button-theme.theme(
tokens-mdc-icon-button.private-get-color-palette-color-tokens($theme, accent));
}

&.mat-warn {
$color: inspection.get-theme-color($theme, warn);
@include mdc-icon-button-theme.theme((icon-color: $color));
@include _ripple-color($color);
}

@include button-theme-private.apply-disabled-style() {
$disabled-color: rgba($on-surface, if($is-dark, 0.5, 0.38));
@include mdc-icon-button-theme.theme((
icon-color: $disabled-color,
disabled-icon-color: $disabled-color,
));
@include _ripple-color(inspection.get-theme-color($theme, warn));
@include mdc-icon-button-theme.theme(
tokens-mdc-icon-button.private-get-color-palette-color-tokens($theme, warn));
}
}
}

@mixin typography($theme) {
}
@mixin typography($theme) {}

@mixin density($theme) {
$density-scale: inspection.get-theme-density($theme);
Expand Down
26 changes: 15 additions & 11 deletions src/material/button/icon-button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
@use '@material/icon-button/icon-button-theme' as mdc-icon-button-theme;
@use '@material/theme/custom-properties' as mdc-custom-properties;

@use '../core/tokens/m2/mdc/icon-button' as m2-mdc-icon-button;
@use '../core/tokens/m2/mdc/icon-button' as tokens-mdc-icon-button;

@use './button-base';
@use '../core/style/private';
@use '../core/tokens/token-utils';

// The slots for tokens that will be configured in the theme can be emitted with no fallback.
@include mdc-custom-properties.configure($emit-fallback-values: false, $emit-fallback-vars: false) {
$token-slots: m2-mdc-icon-button.get-token-slots();
$token-slots: tokens-mdc-icon-button.get-token-slots();

// Add the MDC component static styles.
@include mdc-icon-button.static-styles();
Expand All @@ -25,10 +26,6 @@
// TODO: Determine how to enforce theming exists, otherwise padding will be unset.
padding: 12px;

// Icon size used to be placed on the host element. Now, in `theme-styles` it is placed on
// the unused `.mdc-button__icon` class. Explicitly set the font-size here.
font-size: var(--mdc-icon-button-icon-size);

// Border radius is inherited by ripple to know its shape. Set to 50% so the ripple is round.
border-radius: 50%;

Expand All @@ -42,11 +39,18 @@
vertical-align: baseline;
}

@include button-base.mat-private-button-disabled() {
// The color is already dimmed when the button is disabled. Restore the opacity both to
// help with the color contrast and to align with what we had before switching to the new API.
opacity: 1;
};
@include token-utils.use-tokens(
tokens-mdc-icon-button.$prefix, tokens-mdc-icon-button.get-token-slots()) {
// Icon size used to be placed on the host element. Now, in `theme-styles` it is placed on
// the unused `.mdc-button__icon` class. Explicitly set the font-size here.
@include token-utils.create-token-slot(font-size, icon-size);

@include button-base.mat-private-button-disabled {
// MDC's disabled styles target the `:disabled` selector which doesn't work on links.
// We re-apply the disabled icon color here since we support Material buttons on links too.
@include token-utils.create-token-slot(color, disabled-icon-color);
};
}

@include button-base.mat-private-button-interactive();
@include button-base.mat-private-button-touch-target(true);
Expand Down
25 changes: 19 additions & 6 deletions src/material/core/tokens/m2/mdc/_icon-button.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
@use 'sass:map';
@use '../../../style/sass-utils';
@use '../../../theming/inspection';
@use '../../token-utils';

// The prefix used to generate the fully qualified name for tokens in this file.
Expand All @@ -20,12 +22,7 @@ $prefix: (mdc, icon-button);
state-layer-size: 48px,
// MDC's icon size applied to svg and img elements inside the component
icon-size: 24px,
// Only applies to :disabled icons, but Angular Components uses [disabled] since :disabled
// wouldn't work on <a> tags.
disabled-icon-color: black,
// Angular version applies an opacity 1 with a color change, and this only applies with
// :disabled anyways.
disabled-icon-opacity: 0.38,

// =============================================================================================
// = TOKENS NOT USED IN ANGULAR MATERIAL =
// =============================================================================================
Expand All @@ -41,13 +38,29 @@ $prefix: (mdc, icon-button);
pressed-state-layer-opacity: null,
focus-ring-color: null,
focus-ring-offset: null,

// We use a color with an opacity to show the disabled state,
// instead of applying it to the entire button.
disabled-icon-opacity: null,
);
}

// 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;

@return (
icon-color: inherit,
disabled-icon-color: if($is-dark, rgba(#fff, 0.5), rgba(#000, 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 (
icon-color: inspection.get-theme-color($theme, $palette-name)
);
}

Expand Down

0 comments on commit 13b8ad1

Please sign in to comment.