79118690

Date: 2024-10-23 15:52:06
Score: 2
Natty:
Report link

Angular material mat-menu items use the foreground and background colours of the theme. This is why when you toggle the theme on the angular material website it does not use primary or accent colours, but toggles between a white and grey colour depending on if it's a light or dark theme selected:

When looking at the select when choosing the purple theme on their site you can see each option is either a grey colour in light mode or a black colour in dark mode:

enter image description here Turning our attention back to the menu-items this can be corroborated by looking at the source in @node_modules/@angular/material/core/tokens/m2/mat_menu.scss (Angular 18 if using Material 2)

We see that the colours are generally dependent on the foreground and background colours:

enter image description here

These colours can be altered application-wide via the theme's contrast of your $primary palette. Not recommended due to the number of component's dependent on it. This is mentioned in one of the answers here: Angular 2 Material Foreground

There is also a github issue on how to make a sweeping change to this: https://github.com/angular/components/issues/6244

The above might help for you in some circumstances where you want a very sweeping change.

But lets look at modifying a only say a mat-menu-item.

For now lets also assume you want to make the change on a very particular component, and not application wide, such as a navbar that uses the mat-menu-item

Many solutions give up on being clean, if it can't be done using the palettes, and just try to override the background-color directly. However, you now have a dependency on angular material's classes in your CSS. So if angular material changes their classnames in version updates, your code will break. Additionally you would need to use the deprecated ng-deep.

To solve this, you might add a class, and override the background-color or color on the class. This is better. But you could end up still needing to write very long CSS to make sure your override has higher specificity than angular material's CSS to take effect.

The trick is to use your own class and override the CSS variables that are at the html level and so do not have specificity you need to compete with.

So if you inspect the css of your mat-menu-item you'll see the a focussed state colour, for example is set like this:

enter image description here

If we trawl further down our CSS on the mat-menu-item we find the below section on the HTML level:

enter image description here

So lets say we want to alter the background color. We're probably interested in changing --mat-menu-item-focus-state-layer-color and --mat-menu-item-hover-state-layer-color

And so the final solution to this problem is as follows:

<button class="custom-menu-list-item"
                 mat-menu-item
My item
</button>

And in our *component.theme.scss:

.custom-menu-list-item{
   --mat-menu-item-focus-state-layer-color: red;
   --mat-menu-item-hover-state-layer-color: red;
}

Or preferably, from a palette an example would be:

 --mat-menu-item-hover-state-layer-color: #{mat.m2-get-color-from-palette($my-blue-palette, 500)}

And that's all there is to it.

If you want to make the change system-wide on all mat-menu then you can put it on the body element in one of your application's root theme files, to override the CSS variables on html element.

If you've defined a particular theme class due to having multiple themes, which is quite common, overriding the variable there won't work right away.

This is because your theme class will be defined on a div such as the themeClass$ observable below

<div class="mat-app-background" [class]="themeClass$ | async">
  <app-nav-menu></app-nav-menu>
  <div class="scroll-container">
    <router-outlet></router-outlet>
  </div>
</div>
.my-light-theme{
  --mat-menu-item-hover-state-layer-color: red;
}

This is because components that use the CDK overlay like the mat-options in a select select, modals and menu-items render outside of the angular application.

So you will have to use one of the solutions that put your theme class on the CDKOverlay as well. There are other stackoverflow solutions out there that explain how to do this. It involves injecting private overlayContainer: OverlayContainer into a high level component and adding classes to the overlayContainers classList

Reasons:
  • Blacklisted phrase (1.5): any solution
  • Blacklisted phrase (1): stackoverflow
  • Long answer (-1):
  • Has code block (-0.5):
  • Self-answer (0.5):
  • Low reputation (0.5):
Posted by: MatthewH