components: bug(button): Palette contrast values are not applied

Is this a regression?

  • Yes, this behavior used to work in the previous version

The previous version in which this bug was not present was

13.x

Description

When using a custom theme the contrast values from a custom palette are not applied

Reproduction

steps

  • define a custom palette with a “slightly light” color and a white contrast color
$md-secondary: (
  ...
  500 : #f86072,
  ...
contrast: (
  ...
    500 : #ffffff
  ...
  )
);
  • Apply the theme
$theme-primary: mat.define-palette($md-primary,500);
$theme-accent: mat.define-palette($md-secondary,500);
$theme-warn: mat.define-palette(mat.$red-palette);
$theme: mat.define-light-theme((
  color: (
    primary: $theme-primary,
    accent: $theme-accent,
    warn: $theme-warn,
  ),
  typography: mat.define-typography-config(),
));
@include mat.all-component-themes($theme);

example:

https://stackblitz.com/edit/angular-5b8rw5?file=src/theme.scss

Expected Behavior

Text in accent button should be white (contrast color) as specified in the palette

Actual Behavior

Text in accent button is black

Environment

  • Angular:15.0.0
  • CDK/Material:15.0.0
  • Browser(s):chrome, safari
  • Operating System (e.g. Windows, macOS, Ubuntu):Ubuntu

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 80
  • Comments: 62 (3 by maintainers)

Commits related to this issue

Most upvoted comments

It will be in the release next week.

Most of the Angular Material Components don’t work properly for the color. I don’t know how this as not been fix already. Should have been fix first week after the release of v15, yet here we are at v16 and the problem persist. I’m deceived by this.

First ever comment in GitHub… I don’t really know what I’m doing, but hopefully can be some help.

Been following this since angular 15 hoping it got fixed as it affects our palettes. Last week bit the bullet at work and overwrote the styles as didn’t want to miss out on the nice features of angular 17.

Anyhow, to save others the hassle:

  • Have made a repo with the style overrides in a library and a demo with the usage.
  • Have published the mixins into npm too if people would rather install the styles from there than copy paste them from here/the repo.
  • If anything else crops up in this chain to do with this I’ll add it in too.

Repo: https://github.com/Plabbee/angular-material-contrast-fix NPM: https://www.npmjs.com/package/angular-material-contrast-fix

Same issue, and breaking all my product styles and need to be overwritten, forcing colors hardcoding rules

This is a major issue especially when running multiple themes where most of the proposed workarounds don’t work and overwrites get even messier… How come this does work with the preshipped themes on the demo page? 🤔

@noel-invonto I was about to say “No there is no hope” as it’s been ignored for over a year. And I get an email at least once a week from the various tickets on this. BUT I see that crisbeto has started work on this today, so yes… there is definitely hope!

Same issue. Just posting here as a vote to fix this.

Edit: please 😄

Angular 16 is already live, and this is not fixed. Any news on this?

Same issue for me, my workaround:

.mat-mdc-unelevated-button.mat-primary {
 --mdc-filled-button-label-text-color: var(--player-primary-contrast-500);
}

.mat-mdc-unelevated-button.mat-accent {
  --mdc-filled-button-label-text-color: var(--player-accent-contrast-500);
}

.mat-mdc-raised-button.mat-primary {
  --mdc-protected-button-label-text-color: var(--player-primary-contrast-500);
}

.mat-mdc-raised-button.mat-accent {
  --mdc-protected-button-label-text-color: var(--player-accent-contrast-500);
}

Notify me if resolved.

The problem is still there. The Angular Material v15 is not usable for more than 2 months.

Same issue using angular material 16.2.1 and angular 16.0.4

Same issue. As @beckerjohannes mentioned above, the issue is that for buttons the contrast color is not used. Instead the on-primary color from material/theme is applied.

The on-primary color is used for the button text color.

https://github.com/angular/components/blob/96aadaa411a3af93626af9aa73fb4abd6600d0b3/src/material/button/_button-theme.scss#L50

Now on-primary is defined here:

https://github.com/material-components/material-components-web/blob/cedffb44c0c5e2b6aeca591b4e5d19346a52983c/packages/mdc-theme/_theme-color.scss#L137


@function tone($color) {
  $minimumContrast: 3.1;

  $lightContrast: contrast($color, white);
  $darkContrast: contrast($color, rgba(black, 0.87));

  @if ($lightContrast < $minimumContrast) and ($darkContrast > $lightContrast) {
    @return 'light';
  } @else {
    @return 'dark';
  }
}

@function contrast-tone($color) {
  @return if(tone($color) == 'dark', 'light', 'dark');
}


$on-primary: if(contrast-tone($primary) == 'dark', #000, #fff) !default;

As you can see, the minimal contrast is hardcoded. Depending on the contrast the tone is set to light or dark and depending on the tone the text color is set to white or black.

Would love to know as well. I’ve been looking for workarounds for days and none really seem to work for all components that the primary color doesn’t get used at anymore. Super frustrating 😦… Angular 17 will get released soon, I’d hate not to be able to use that version either.

Only workaround I know of so far is manually fixing it e.g. by overwriting the affected classes in global styles.scss. Keep in mind to not only change the text-color, you also need to target the ripple effect color.

// *** Contrast fix example of custom accent color ***
.mat-mdc-raised-button.mat-accent,
.mat-mdc-unelevated-button.mat-accent,
.mat-mdc-fab.mat-accent,
.mat-mdc-mini-fab.mat-accent {
  color: mat.get-color-from-palette(
    $custom-theme-accent,
    default-contrast
  ) !important;
  --mat-mdc-button-persistent-ripple-color: mat.get-color-from-palette(
    $custom-theme-accent,
    default-contrast
  ) !important;
  --mat-mdc-button-ripple-color: rgba(255, 255, 255, 0.1); //Insert custom rgba contrast color
}

.mat-mdc-fab.mat-accent,
.mat-mdc-mini-fab.mat-accent {
  --mdc-fab-icon-color: mat.get-color-from-palette(
    $custom-theme-accent,
    default-contrast
  );
  --mat-mdc-fab-color: mat.get-color-from-palette(
    $custom-theme-accent,
    default-contrast
  );
}

So far I found that checkboxes are also affected, but I was able to fix it by overwriting like this:

* {
  --mdc-checkbox-selected-checkmark-color: #fff !important; //Using mat.get-color-from-palette() doesn't work for me so I hardcoded the color
}

I hope this helps as it did for me.

Bugged: image

Fixed: image

Same thing in 16.1.5 with chips

Yeah - it doesn’t appear to be getting fixed (I think they might view it as a feature rather than a bug; because it DOES enforce a certain level of contrast, which helps with accessibility). I just targeted the elements I cared about with CSS in my global styles and moved on.

Also ran into this issue. In my case with a slightly light primary color

  1. Raised Buttons shows primary with black text
  2. Primary Tool Bar shows primary with white text

The issue seems to be, that the button uses the on-primary calculated by the mdc-theme See: https://github.com/angular/components/blob/57676e467a1ed9875def9a825746799c3427c942/src/material/button/_button-theme.scss#L50 Whereas the toolbar uses the contrast color defined by the custom theme. See: https://github.com/angular/components/blob/57676e467a1ed9875def9a825746799c3427c942/src/material/toolbar/_toolbar-theme.scss#L20

It’s pretty widespread:

  • Button
  • Checkbox
  • Slide toggle
  • Datepicker apparently

Differs here and there between light & dark mode.

Pls fix this. There is actually a forked repo with a fix. This bug is super painful, because even if you got a workaround, it breaks on every major update eventually and one has to find a new workaround. This is the 3rd time i’m stumbling over this.

how come this issue has not resolved for almost a year, I thought about upgrading to v16 but it seems unreliable

Found an other place where the contrast color does not get applied this time it affect the datepicker:

.mat-calendar-body-cell-content {
    color: var(--mat-datepicker-calendar-date-text-color); // also affected
}

Would love to know as well. I’ve been looking for workarounds for days and none really seem to work for all components that the primary color doesn’t get used at anymore. Super frustrating 😦… Angular 17 will get released soon, I’d hate not to be able to use that version either.

I set up a fresh angular in the newest version in combination with material. While trying to setup a custom theme, I noticed that the issue still persists 😦

I am using angular 16.1.0 and angular material 16.1.7

Same problem in Angular Material 17.1.0. I’m using the package from @Plabbee for workaround, it works perfectly!

Same here with v17.0.4

The contrast for 600 is white.

image

But the raised-button applys black

image

@Davidihl Will have a look at updating later on today - Can see you’ve raised it in this thread before and I’ve overlooked it - Will use your fix for reference 👍

Sadly 😢 mat-flat-button as well as mat-raised-button still have this, fairly easy to fix imho, issue. Please get back to us with an explanation and/or fix. Greatly appreciated 👍🏼

It seems to work with the preshipped themes in 16.2.6?

image

Same thing in 16.1.5 with chips

Same here. I’ve been hitting myself in the head for the past two hours… thank god I found this issue. Thanks for the fix @Plabbee

@Plabbee I am using your npm package in our Angular 17 project. It integrates easily and works fine so far. Good work!

I tested @Plabbee npm package in a fresh angular 17 project. easily to implement and works like a charm. I hope the Angular team picks it up

Same issue. Similar to https://github.com/angular/components/issues/26056#issuecomment-1412183516 but I am reading the contrast value from the specific palette of the theme.

@mixin theme($theme) {
  button,
  a {
    @include -colors($theme);
  }
}
@mixin -colors($theme) {
  $color-config: mat.get-color-config($theme);
  $primary-palette: map.get($color-config, primary);
  $accent-palette: map.get($color-config, accent);
  $nominal-palette: map.get($color-config, nominal);
  
  $colors: (
    primary: $primary-palette,
    accent: $accent-palette,
    nominal: $nominal-palette
  );
  
  &[mat-flat-button],
  &[mat-raised-button],
  &[mat-fab],
  &[mat-mini-fab] {
    @each $color, $palette in $colors {
      &.mat-#{$color} {
        --mdc-protected-button-label-text-color: #{mat.get-color-from-palette($palette, '500-contrast')};
        --mdc-filled-button-label-text-color: #{mat.get-color-from-palette($palette, '500-contrast')};
        --mat-mdc-fab-color: #{mat.get-color-from-palette($palette, '500-contrast')};
      }
    }
  }
}

I’m having the same issue using angular 16.1.0 and angular material 16.2.1. This only an issue when defining your own palette, the contrast colors work correctly if you use one of the predefined palettes from angular material.

Same with checkboxes

Same issue here. Hope it’ll get fixed soon! 😃

Thank you @Frankitch for the workaround!

Just as an information for others facing an issue like this: There seems to be a change affecting “unthemed” buttons. A button in my application changed text-color after upgrading to angular material 15 from white to black. It is placed on a dark background. I found this issue and assumed it was caused by this. Just give it a try and give your button a color attribute if it has none and select primary, accent or warn depending on your background. You can use mat-flat-button and then the correct contrast color is selected. Definitely not a solution for everybody but worked for me.