Skip to content

fix(material/core): elevation classes emit invalid shadows with system theme - #33661

Open
ManicardiFrancesco wants to merge 1 commit into
angular:mainfrom
ManicardiFrancesco:fix-elevation-classes-mat-theme
Open

fix(material/core): elevation classes emit invalid shadows with system theme#33661
ManicardiFrancesco wants to merge 1 commit into
angular:mainfrom
ManicardiFrancesco:fix-elevation-classes-mat-theme

Conversation

@ManicardiFrancesco

Copy link
Copy Markdown
Contributor

What is the current behavior?

mat.elevation-classes() emits no shadow at all for apps themed with mat.theme.

Each mat-elevation-z* class resolves to var(--mat-app-elevation-shadow-level-<n>, <fallback>), and the fallback is built from the shadow entry of the system theme. In the $fallbacks map that entry is the CSS variable name --mat-sys-shadow rather than a color, and _compute-color-opacity() in core/style/_elevation.scss only handled Sass color values — anything else was returned untouched and interpolated into the shadow verbatim:

.mat-elevation-z6, .mat-mdc-elevation-specific.mat-elevation-z6 {
  box-shadow: var(--mat-app-elevation-shadow-level-6, 0px 3px 5px -1px --mat-sys-shadow, 0px 6px 10px 0px --mat-sys-shadow, 0px 1px 18px 0px --mat-sys-shadow);
}

A bare --mat-sys-shadow is not a valid <color>, so the declaration is invalid at computed-value time and the browser drops it.

This only bites the mat.theme API. The --mat-app-elevation-shadow-level-* tokens are emitted exclusively by mat.core-theme (i.e. the all-component-themes / prebuilt theme path), so those apps never reach the fallback. mat.theme never defines them, which means the broken fallback is the only value they ever get. The dev-app themes with mat.m2-theme + all-component-themes, which is why this isn't visible there.

Reproduction (against main):

@use '@angular/material' as mat;

html {
  @include mat.theme((color: mat.$violet-palette, typography: Roboto, density: 0));
}

@include mat.elevation-classes();

What is the new behavior?

_compute-color-opacity() now recognizes a CSS variable name, wraps it in var() and applies the umbra/penumbra/ambient opacities with color-mix, reusing the m3-utils.color-with-opacity helper that already handles this exact case for other tokens:

.mat-elevation-z6, .mat-mdc-elevation-specific.mat-elevation-z6 {
  box-shadow: var(--mat-app-elevation-shadow-level-6, 0px 3px 5px -1px color-mix(in srgb, var(--mat-sys-shadow) 20%, transparent), 0px 6px 10px 0px color-mix(in srgb, var(--mat-sys-shadow) 14%, transparent), 0px 1px 18px 0px color-mix(in srgb, var(--mat-sys-shadow) 12%, transparent));
}

I compiled a stylesheet exercising all three theming paths (mat.m2-theme, mat.define-theme + all-component-themes, and mat.theme) plus all four prebuilt themes before and after the change. The only difference is the 25 mat-elevation-z* rules; every other declaration in the bundle is byte-identical. Prebuilt and M2 themes are unaffected because they define the app elevation tokens, so the fallback is never used there.

The added test in m3-theme.spec.ts asserts that no box-shadow emitted by mat.elevation-classes() contains a bare token name. It reports 25 failures before this change and none after.

Does this PR introduce a breaking change?

  • Yes
  • No

…m theme

`mat.elevation-classes()` resolves each `mat-elevation-z*` class to
`var(--mat-app-elevation-shadow-level-<n>, <fallback>)`, where the
fallback is built from `--mat-sys-shadow`. Since that shadow color is
a CSS variable name rather than a color, it was interpolated into the
shadow verbatim, producing `0px 3px 5px -1px --mat-sys-shadow, ...`.

That value is invalid CSS, so the browser drops the entire declaration.
Apps that theme with `mat.theme` never define the
`--mat-app-elevation-shadow-level-*` tokens (only the older
`mat.core-theme`/prebuilt theming path does), which means they always
hit the broken fallback and get no shadow at all from any
`mat-elevation-z*` class.

Wrap the variable in `var()` and apply the shadow opacities through
`color-mix`, mirroring what `m3-utils.color-with-opacity` already does
elsewhere. Themes that define the app elevation tokens are unaffected,
since the fallback is unused there.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant