MatButton loses color styling after upgrading to Angular Material v17 theming API
0 reputation · 19 Apr 2021, 21:32 UTC
0 reputation · 19 Apr 2021, 21:32 UTC
After migrating an application to Angular Material v17, I want all mat-raised-button and mat-button instances with [color]="primary" or [color]="accent" to render with the correct palette colors again, both in the browser and in a server-side rendered (SSR) build.
As I understand the v17 theming changes, button colors are no longer applied implicitly; the theme configuration is expected to include a mat-button-theme (or equivalent component theme) block, and omitting it leaves buttons without palette styling. My project also defines multiple button themes in different scopes, and the color input now appears to merge with the global theme, so inheritance between nested themes is unclear to me.
In the SSR build, theme styles seem to be injected after component initialization, which I suspect could cause a flash of unstyled buttons, but I have not confirmed whether the new API changes that timing.
[color] on MatButton resolves against the intended palette when more than one button theme exists in the stylesheet?mat-button-theme definitions versus the global theme—does the nearest scope win, or do they merge?30775 reputation · 20 Apr 2021, 05:41 UTC
In Angular Material v17, [color] on MatButton only resolves to a palette color if the stylesheet actually emits the button theme tokens. The minimal fix is to include a button theme (or the all-components theme) once at the global level, built from a theme defining primary and accent palettes. Scoped themes override the global one for their subtree — the nearest ancestor scope wins for the tokens it defines; it is not a deep merge. For SSR, the theme must live in the global styles bundle (the styles array in angular.json), not in a component-level or lazy-loaded stylesheet, so tokens ship with the first HTML payload.
Assuming the v17 M3-style API (mat.define-theme; verify exact mixin names against your installed version), the pattern is:
@use '@angular/material' as mat;
$theme: mat.define-theme((
color: (theme-type: light, primary: mat.$azure-palette)
));
html {
@include mat.all-component-themes($theme);
// or, minimally for your case:
// @include mat.button-theme($theme);
}If you omit the button theme, the color="primary" attribute still renders a class like mat-primary, but the underlying CSS custom properties (e.g. --mdc-filled-button-container-color) are never defined, so the button falls back to default colors — matching your symptom.
Caveat: mixin names and palette maps differ between the M3 API and the legacy compatibility layer, and mixing M2 and M3 themes can cause silent token overrides. Confirm which API your @angular/material version documents before copying mixin names.
Scoped themes are plain CSS: wrapping a theme mixin in a selector emits custom properties for that subtree, and normal cascade rules apply — the nearest ancestor defining a token wins. The [color] input selects which palette role the button asks for; the scope determines what those roles resolve to. They do not merge two themes into one; tokens not emitted by the scoped block (density, typography) still come from the global theme.
There is no special SSR theming API; the requirement is that theme CSS is part of the critical, server-delivered CSS: put it in a global stylesheet in the styles array of angular.json, not in component styleUrls or a lazy bundle. Whether v17 changed injection timing is unconfirmed — verify empirically rather than assuming a regression.
--mdc-filled-button-container-color (or the token for your button variant) is defined. Undefined = missing theme mixin..mat-primary rules tied to your palette values.If tokens are defined but colors are still wrong, the detail that would change this advice is whether you are mixing legacy (M2) and M3 theme definitions — share your theme SCSS and that can be checked directly.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.