Skip to content

Commit 966bc88

Browse files
ryan-bendelthePunderWoman
authored andcommitted
feat(platform-browser): Add legacyshadowdom encapsulation
Fixes style leakage in Shadowdom encapsulation by removing sharedstyleshost from dom-renderer Shadowdom class. Create a deprecated LegacyShadowDOM renderer with the old behaviour, and provide migration. This allows a non-breaking change
1 parent 6d01168 commit 966bc88

File tree

31 files changed

+558
-25
lines changed

31 files changed

+558
-25
lines changed

adev/src/app/sub-navigation-data.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1353,6 +1353,11 @@ const REFERENCE_SUB_NAVIGATION_DATA: NavigationItem[] = [
13531353
path: 'reference/migrations/self-closing-tags',
13541354
contentPath: 'reference/migrations/self-closing-tags',
13551355
},
1356+
{
1357+
label: 'Shadowdom',
1358+
path: 'reference/migrations/shadowdom',
1359+
contentPath: 'reference/migrations/shadowdom',
1360+
},
13561361
],
13571362
},
13581363
];

adev/src/content/cli/help/generate.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,8 @@
171171
"enum": [
172172
"Emulated",
173173
"None",
174-
"ShadowDom"
174+
"ShadowDom",
175+
"LegacyShadowDom"
175176
],
176177
"description": "Sets the view encapsulation mode for the application's components. This determines how component styles are scoped and applied."
177178
},
@@ -372,7 +373,8 @@
372373
"enum": [
373374
"Emulated",
374375
"None",
375-
"ShadowDom"
376+
"ShadowDom",
377+
"LegacyShadowDom"
376378
],
377379
"description": "Sets the view encapsulation mode for the component. This determines how the component's styles are scoped and applied."
378380
}
@@ -998,4 +1000,4 @@
9981000
"deprecated": false
9991001
}
10001002
]
1001-
}
1003+
}

adev/src/content/cli/help/new.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,8 @@
184184
"enum": [
185185
"Emulated",
186186
"None",
187-
"ShadowDom"
187+
"ShadowDom",
188+
"LegacyShadowDom"
188189
],
189190
"description": "Sets the view encapsulation mode for components in the initial project. This determines how component styles are scoped and applied. Options include: `Emulated` (default, styles are scoped to the component), `None` (styles are global), and `ShadowDom` (styles are encapsulated using Shadow DOM)."
190191
},
@@ -195,4 +196,4 @@
195196
"description": "Create an initial application that does not utilize `zone.js`."
196197
}
197198
]
198-
}
199+
}

adev/src/content/reference/migrations/overview.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,7 @@ Learn about how you can migrate your existing angular project to the latest feat
3030
<docs-card title="Self-closing tags" link="Migrate now" href="reference/migrations/self-closing-tags">
3131
Convert component templates to use self-closing tags where possible.
3232
</docs-card>
33-
</docs-card-container>
33+
<docs-card title="Shadowdom" link="Migrate now" href="reference/migrations/shadowdom">
34+
Convert existing Shadowdom encapsulation to use the deprecated LegacyShadowDom.
35+
</docs-card>
36+
</docs-card-container>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Migration to LegacyShadowDom
2+
3+
Previously, Shadow DOM encapsulation allowed styles to leak into components, and did not follow the spec.
4+
5+
The `LegacyShadowDom` encapsulation mode was introduced so that you can preserve this behaviour while migrating to the new Shadow DOM encapsulation which only applies styles inside the shadowhost that are imported/defined by your component.
6+
7+
The updated `ShadowDom` encapsulation mode follows the Shadow DOM spec, and styles not declared by the component will not be pulled in . As this would have been a breaking change, the `LegacyShadowDom` encapsulation mode was introduced to allow you to migrate your application without breaking existing styling.
8+
9+
This schematic migrates any current `ShadowDom` encapsulation to `LegacyShadowDom` encapsulation. You can then choose to use the updated `ShadowDom` encapsulation mode if you do not rely on the external styles it was injecting, or
10+
if you have fixed any ui issues by moving styles into the style declarations directly imported by the component.
11+
12+
`LegacyShadowDom` encapsulation is not recommended for new applications, and should only be used if you are migrating an existing application that relies on the old behaviour, it is deprecated and will
13+
be removed in a future release.
14+
15+
Run the schematic using the following command:
16+
17+
<docs-code language="shell">
18+
19+
ng generate @angular/core:shadowdom-migration
20+
21+
</docs-code>
22+
23+
24+
#### Before
25+
26+
<docs-code language="typescript">
27+
28+
<!-- Before -->
29+
encapsulation: ViewEncapsulation.ShadowDom
30+
31+
<!-- After -->
32+
encapsulation: ViewEncapsulation.LegacyShadowDom
33+
34+
</docs-code>

devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/component-metadata.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export class ComponentMetadataComponent {
3636

3737
private _nestedProps = inject(ElementPropertyResolver);
3838

39-
angularViewEncapsulationModes = ['Emulated', 'Native', 'None', 'ShadowDom'];
39+
angularViewEncapsulationModes = ['Emulated', 'Native', 'None', 'ShadowDom', 'LegacyShadowDom'];
4040
acxViewEncapsulationModes = ['Emulated', 'None'];
4141

4242
readonly controller = computed(() => {

goldens/public-api/core/index.api.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2015,6 +2015,8 @@ export abstract class ViewContainerRef {
20152015
// @public
20162016
export enum ViewEncapsulation {
20172017
Emulated = 0,
2018+
// @deprecated (undocumented)
2019+
LegacyShadowDom = 4,
20182020
None = 2,
20192021
ShadowDom = 3
20202022
}

packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -862,7 +862,11 @@ export class ComponentDecoratorHandler
862862
}
863863
}
864864

865-
if (encapsulation === ViewEncapsulation.ShadowDom && metadata.selector !== null) {
865+
if (
866+
(encapsulation === ViewEncapsulation.ShadowDom ||
867+
encapsulation === ViewEncapsulation.LegacyShadowDom) &&
868+
metadata.selector !== null
869+
) {
866870
const selectorError = checkCustomElementSelectorForErrors(metadata.selector);
867871
if (selectorError !== null) {
868872
if (diagnostics === undefined) {

packages/compiler/src/compiler_facade_interface.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,7 @@ export enum ViewEncapsulation {
348348
// Historically the 1 value was for `Native` encapsulation which has been removed as of v11.
349349
None = 2,
350350
ShadowDom = 3,
351+
LegacyShadowDom = 4,
351352
}
352353

353354
export type ChangeDetectionStrategy = number;

packages/compiler/src/core.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export enum ViewEncapsulation {
2323
// Historically the 1 value was for `Native` encapsulation which has been removed as of v11.
2424
None = 2,
2525
ShadowDom = 3,
26+
LegacyShadowDom = 4,
2627
}
2728

2829
export enum ChangeDetectionStrategy {

0 commit comments

Comments
 (0)