Skip to content

Consistent and permissive style encapsulation management #65989

@Gouvernathor

Description

@Gouvernathor

Which @angular/* package(s) are relevant/related to the feature request?

core

Description

It should be possible for a component to apply some style rules to its child contents.

An example of use is a reusable <hgroup> with consistent style but arbitrary contents. If you want to be able to pass <p> elements and one heading element (of arbitrary level), containing inline markup such as <br/>, <a>, <b>, <em>... you cannot use the component's inputs and have to pass the elements as child content. And then the trouble begins when the component needs to style that content.

With Angular's default view encapsulation mode, the style of that component's dedicated style sheet won't apply to the child content.
With the two ShadowDom view encapsulation modes, there are technical consequences which are warned against in the documentation that are probably not wanted as a general solution. Additionally, it could break the application of global style to the component's template and child contents, which is a big side-effect.
With no view encapsulation, the styles will apply globally which is definitely not a good solution for anything.

Proposed solution

Allow (documentation-wise) and support the use of bounded ::ng-deep to solve the issue.
What I mean by "bounded" means placing ng-deep in the middle of a CSS selector, and not at the start, for instance :host ::ng-deep b or a ::ng-deep b, such that the rule thus introduced cannot apply outside of the component's node.
Such a rule then applies to the child content of the component.

It is a simple solution, fits nicely in the style sheet of the component, and is also very versatile : as opposed to a new view encapsulation mode, you can have in the same component (even in the same style sheet), some rules applying only to the component's template, and others applying to all the component's contents (template and child).

To be perfectly clear, CSS selectors that would start with ::ng-deep, or which would in any way allow styling to apply outside of the component's element, would remain as frowned upon as ::ng-deep currently is.

Previous related discussions:
#25160
a thread on the discord server
#65718
#65944

Alternatives considered

Using "design tokens" or CSS variables - both kinda works the same.
CSS variables would be declared as (in the previous example) part of the hgroup component's API, the component's style sheet would then set values for those variables, and the component's user would have the responsibility to set all the related CSS properties, in the content passed to the component, to evaluating those variables.

I think that's several things:

  • a massive bug nest with a lot of moving parts
  • way more complex than the use of ::ng-deep
  • requires more action from the component user's part than usually necessary
  • exposing what should be private about the component : what CSS properties it affects is nobody's business, it's the component's job
  • if the component doesn't want to apply the same styling to all its child contents, it needs to use CSS selectors, which the users of the component would need to copy to apply them to the child content they are passing to the component. Which makes the proposed solution even more complex.

This is the point where the discussion was left off.


I think that, at the very least, proposed alternatives need to:

  • solve the above example
  • provide concrete examples as to how allowing the "bounded ::ng-deep" recipe would have worse consequences than the existing and supported ViewEncapsulation.None mode, in other words, why "bounded ::ng-deep" should remain discouraged while that mode is not.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions