Which @angular/* package(s) are the source of the bug?
compiler
Is this a regression?
Yes
Description
Since d0d7f57 (#69885, "fix(compiler): scope nested CSS rules"), selectors nested with & inside a :host rule are scoped with the content attribute. & there refers to the host element, which only has the _nghost-* attribute, so the rule never matches.
@Component({
selector: 'app-avatar',
host: { class: 'md' },
template: '',
styles: `
:host {
display: block;
&.md { width: 70px; }
}
`,
})
export class Avatar {}
Output of the emulated encapsulation:
| Version |
Output |
| 22.1.8 |
[_nghost-c1] { display: block; &.md { width: 70px; } } |
| 22.2.0 |
[_nghost-c1] { display: block; &.md[_ngcontent-c1] { width: 70px; } } |
In 22.2.0 the rule resolves to [_nghost-c1].md[_ngcontent-c1]. The host element carries _nghost-c1 and its parent's _ngcontent-*, so &.md no longer applies.
Minimal repro with the compiler directly:
import { encapsulateStyle } from '@angular/compiler';
encapsulateStyle(':host { &.md { width: 70px } }', 'c1');
// 22.1.8: [_nghost-c1] { &.md { width: 70px } }
// 22.2.0: [_nghost-c1] { &.md[_ngcontent-c1] { width: 70px } }
Expected: a compound selector that starts with & directly under :host (e.g. &.md, &:hover, &[attr]) is left unscoped, or scoped with the host attribute. Descendant selectors (& .child, .child) should still get the content attribute.
packages/compiler/test/shadow_css/nesting_spec.ts covers :host(.foo) { .child {} } and .parent { &.modifier {} }, but not :host { &.modifier {} }.
Please provide a link to a minimal reproduction of the bug
https://stackblitz.com/edit/bhxywn3c?file=src%2Fmain.ts
Please provide the exception or error you saw
None. The styles silently stop applying.
Please provide the environment you discovered this bug in (run ng version)
Angular CLI : 22.2.0
Angular : 22.2.0
Node.js : 24.16.0
Package Manager : npm 11.13.0
Operating System : darwin arm64
┌───────────────────────────────────┬───────────────────┬───────────────────┐
│ Package │ Installed Version │ Requested Version │
├───────────────────────────────────┼───────────────────┼───────────────────┤
│ @angular/animations │ 22.2.0 │ ~22.2.0 │
│ @angular/build │ 22.2.0 │ ~22.2.0 │
│ @angular/cdk │ 22.2.0 │ ~22.2.0 │
│ @angular/cli │ 22.2.0 │ ~22.2.0 │
│ @angular/common │ 22.2.0 │ ~22.2.0 │
│ @angular/compiler │ 22.2.0 │ ~22.2.0 │
│ @angular/compiler-cli │ 22.2.0 │ ~22.2.0 │
│ @angular/core │ 22.2.0 │ ~22.2.0 │
│ @angular/forms │ 22.2.0 │ ~22.2.0 │
│ @angular/platform-browser │ 22.2.0 │ ~22.2.0 │
│ @angular/platform-browser-dynamic │ 22.2.0 │ ~22.2.0 │
│ @angular/router │ 22.2.0 │ ~22.2.0 │
│ ng-packagr │ 22.2.2 │ ^22.2.2 │
│ rxjs │ 7.8.2 │ ^7.8.2 │
│ typescript │ 6.0.3 │ ~6.0.3 │
│ vitest │ 5.0.2 │ ^5.0.2 │
│ zone.js │ 0.16.3 │ ~0.16.0 │
└───────────────────────────────────┴───────────────────┴───────────────────┘
Anything else?
We JIT-compile third-party components at runtime, so their authors hit this without being able to change the build.
The repro uses aot: false. In our AOT CLI build, the CSS pipeline flattened :host { &.on {} } to :host.on {} before ShadowCss ran, which hides the bug. It shows up whenever unflattened nested CSS reaches the compiler, e.g. JIT.
Which @angular/* package(s) are the source of the bug?
compiler
Is this a regression?
Yes
Description
Since d0d7f57 (#69885, "fix(compiler): scope nested CSS rules"), selectors nested with
&inside a:hostrule are scoped with the content attribute.&there refers to the host element, which only has the_nghost-*attribute, so the rule never matches.Output of the emulated encapsulation:
[_nghost-c1] { display: block; &.md { width: 70px; } }[_nghost-c1] { display: block; &.md[_ngcontent-c1] { width: 70px; } }In 22.2.0 the rule resolves to
[_nghost-c1].md[_ngcontent-c1]. The host element carries_nghost-c1and its parent's_ngcontent-*, so&.mdno longer applies.Minimal repro with the compiler directly:
Expected: a compound selector that starts with
&directly under:host(e.g.&.md,&:hover,&[attr]) is left unscoped, or scoped with the host attribute. Descendant selectors (& .child,.child) should still get the content attribute.packages/compiler/test/shadow_css/nesting_spec.tscovers:host(.foo) { .child {} }and.parent { &.modifier {} }, but not:host { &.modifier {} }.Please provide a link to a minimal reproduction of the bug
https://stackblitz.com/edit/bhxywn3c?file=src%2Fmain.ts
Please provide the exception or error you saw
Please provide the environment you discovered this bug in (run
ng version)Anything else?
We JIT-compile third-party components at runtime, so their authors hit this without being able to change the build.
The repro uses
aot: false. In our AOT CLI build, the CSS pipeline flattened:host { &.on {} }to:host.on {}beforeShadowCssran, which hides the bug. It shows up whenever unflattened nested CSS reaches the compiler, e.g. JIT.