Which @angular/* package(s) are the source of the bug?
compiler
Is this a regression?
No
Description
CSS Variables namespacing, introduced in v22.1.0, rewrites custom property references inside a component's styles unconditionally, while global stylesheets are left alone by design. For a library published with partial compilation that rewrite does not happen when the library is built. It happens during the consuming application's build, with the consumer's namespace.
As a result, calling provideCssVarNamespacing() breaks any third-party library that exposes theming through custom properties defined in a global stylesheet, which is the usual way libraries do it. Angular Material ends up completely unstyled.
With provideCssVarNamespacing():

Without:

With @angular/material and the prebuilt azure-blue theme, a plain <button matButton="filled">:
| computed |
with provideCssVarNamespacing() |
without |
background-color |
rgba(0, 0, 0, 0) |
rgb(0, 92, 187) |
color |
rgb(0, 0, 0) |
rgb(255, 255, 255) |
border-radius |
0px |
9999px |
The theme defines its tokens in the global stylesheet and the components reference them, so the two sides end up under different names:
$ grep -o -- "--mat-sys-[a-z-]*:" node_modules/@angular/material/prebuilt-themes/azure-blue.css | sort -u | wc -l
157
$ grep -o -- "var(--mat-" node_modules/@angular/material/fesm2022/button.mjs | wc -l
832
getComputedStyle(document.documentElement).getPropertyValue('--mat-sys-primary') // "#005cbb"
getComputedStyle(document.documentElement).getPropertyValue('--myapp_mat-sys-primary') // ""
This is not specific to Material. A minimal library component with color: var(--brand) is published as
// dist/ui/fesm2022/ui.mjs
styles: ["#lib{color:var(--brand);border:2px solid var(--brand);...}"]
and comes out of the consuming application's build as
#lib[_ngcontent-%COMP%]{color:var(--%NS%brand)}
because PartialComponentLinkerVersion1.linkPartialDeclaration re-runs compileComponentFromMetadata, which applies namespaceCssVariables to styles unconditionally (packages/compiler/src/render3/view/compiler.ts).
--global-- is the designed opt-out, but it only helps in source you control. A library would have to spell every one of its tokens --global--… to defend itself against a decision its consumers make, and libraries published before v22.1 obviously cannot. A consuming application has no way to apply the opt-out to a dependency.
Steps to reproduce:
ng new an application on 22.1
npm i @angular/material @angular/cdk and add node_modules/@angular/material/prebuilt-themes/azure-blue.css to the styles array in angular.json
- put
<button matButton="filled">Filled button</button> in the template
- add
provideCssVarNamespacing() to appConfig.providers
- build and open the page
Please provide a link to a minimal reproduction of the bug
https://github.com/lacolaco/angular-cssvar-namespacing-library-repro
Please provide the exception or error you saw
No errors. The declarations are silently dropped.
Please provide the environment you discovered this bug in (run ng version)
Angular CLI : 22.1.2
Angular : 22.1.0
Node.js : 22.22.3
Package Manager : npm 10.9.8
Operating System : darwin arm64
┌───────────────────────────┬───────────────────┬───────────────────┐
│ Package │ Installed Version │ Requested Version │
├───────────────────────────┼───────────────────┼───────────────────┤
│ @angular/build │ 22.1.2 │ ^22.1.2 │
│ @angular/cdk │ 22.1.0 │ ^22.1.0 │
│ @angular/cli │ 22.1.2 │ ^22.1.2 │
│ @angular/common │ 22.1.0 │ ^22.1.0 │
│ @angular/compiler │ 22.1.0 │ ^22.1.0 │
│ @angular/compiler-cli │ 22.1.0 │ ^22.1.0 │
│ @angular/core │ 22.1.0 │ ^22.1.0 │
│ @angular/forms │ 22.1.0 │ ^22.1.0 │
│ @angular/material │ 22.1.0 │ ^22.1.0 │
│ @angular/platform-browser │ 22.1.0 │ ^22.1.0 │
│ @angular/router │ 22.1.0 │ ^22.1.0 │
│ ng-packagr │ 22.1.0 │ ^22.1.0 │
│ rxjs │ 7.8.2 │ ~7.8.0 │
│ typescript │ 6.0.3 │ ~6.0.2 │
└───────────────────────────┴───────────────────┴───────────────────┘
Anything else?
For context on prior art, in case this reads as already known: the closest discussion I found is #67362, where the mismatch between a var() reference and a declaration living in a global stylesheet was raised, and --global-- was chosen as the answer.
What doesn't appear in either PR is the distribution dimension. Neither discussion mentions partial compilation, the linker, or npm packages, and the single sentence about libraries in the description is about using CssVarNamespacer for JS references rather than a library's own styles. --global-- only helps in source the author controls, which is what makes this case different from an application namespacing itself. I can't see the internal design doc linked from #67362, so this may well have been considered there.
If the answer here is that libraries need to adopt --global-- for their tokens, then adopting this feature is gated on an ecosystem-wide source change rather than on an application-level provider. Every library published before 22.1 is incompatible with any consumer that turns the flag on, there is no version constraint or diagnostic that surfaces it, and the failure mode is styles silently not applying. That seems like a larger coordination problem than a minor release can carry. --global- with a single hyphen is already slated to become an error in v23, so parts of this are on a major track anyway.
It also seems worth asking whether linked library styles should be namespaced at all. The namespace is application-scoped (it defaults to APP_ID), so rewriting a dependency's styles during the consuming application's link step is a choice rather than a consequence. Leaving them alone would still give the feature what it is after, an application's own variables no longer colliding with another application's on the same page, while keeping libraries working.
Which @angular/* package(s) are the source of the bug?
compiler
Is this a regression?
No
Description
CSS Variables namespacing, introduced in v22.1.0, rewrites custom property references inside a component's
stylesunconditionally, while global stylesheets are left alone by design. For a library published with partial compilation that rewrite does not happen when the library is built. It happens during the consuming application's build, with the consumer's namespace.As a result, calling
provideCssVarNamespacing()breaks any third-party library that exposes theming through custom properties defined in a global stylesheet, which is the usual way libraries do it. Angular Material ends up completely unstyled.With
provideCssVarNamespacing():Without:
With
@angular/materialand the prebuiltazure-bluetheme, a plain<button matButton="filled">:provideCssVarNamespacing()background-colorrgba(0, 0, 0, 0)rgb(0, 92, 187)colorrgb(0, 0, 0)rgb(255, 255, 255)border-radius0px9999pxThe theme defines its tokens in the global stylesheet and the components reference them, so the two sides end up under different names:
This is not specific to Material. A minimal library component with
color: var(--brand)is published asand comes out of the consuming application's build as
because
PartialComponentLinkerVersion1.linkPartialDeclarationre-runscompileComponentFromMetadata, which appliesnamespaceCssVariablestostylesunconditionally (packages/compiler/src/render3/view/compiler.ts).--global--is the designed opt-out, but it only helps in source you control. A library would have to spell every one of its tokens--global--…to defend itself against a decision its consumers make, and libraries published before v22.1 obviously cannot. A consuming application has no way to apply the opt-out to a dependency.Steps to reproduce:
ng newan application on 22.1npm i @angular/material @angular/cdkand addnode_modules/@angular/material/prebuilt-themes/azure-blue.cssto thestylesarray inangular.json<button matButton="filled">Filled button</button>in the templateprovideCssVarNamespacing()toappConfig.providersPlease provide a link to a minimal reproduction of the bug
https://github.com/lacolaco/angular-cssvar-namespacing-library-repro
Please provide the exception or error you saw
Please provide the environment you discovered this bug in (run
ng version)Anything else?
For context on prior art, in case this reads as already known: the closest discussion I found is #67362, where the mismatch between a
var()reference and a declaration living in a global stylesheet was raised, and--global--was chosen as the answer.What doesn't appear in either PR is the distribution dimension. Neither discussion mentions partial compilation, the linker, or npm packages, and the single sentence about libraries in the description is about using
CssVarNamespacerfor JS references rather than a library's ownstyles.--global--only helps in source the author controls, which is what makes this case different from an application namespacing itself. I can't see the internal design doc linked from #67362, so this may well have been considered there.If the answer here is that libraries need to adopt
--global--for their tokens, then adopting this feature is gated on an ecosystem-wide source change rather than on an application-level provider. Every library published before 22.1 is incompatible with any consumer that turns the flag on, there is no version constraint or diagnostic that surfaces it, and the failure mode is styles silently not applying. That seems like a larger coordination problem than a minor release can carry.--global-with a single hyphen is already slated to become an error in v23, so parts of this are on a major track anyway.It also seems worth asking whether linked library styles should be namespaced at all. The namespace is application-scoped (it defaults to
APP_ID), so rewriting a dependency'sstylesduring the consuming application's link step is a choice rather than a consequence. Leaving them alone would still give the feature what it is after, an application's own variables no longer colliding with another application's on the same page, while keeping libraries working.