Skip to content

[css-cascade] Shadow DOM style ordering and cascade penetration #11547

@dead-claudia

Description

@dead-claudia

First filed in whatwg/dom#1348, but that was the wrong repo

What problem are you trying to solve?

I want to be able to use shadow DOM for display encapsulation, but while still being able to use global styles and participate in its inheritance.

What solutions exist today?

Currently, I could use shadowRoot.adoptedStyleSheets = [...globalSheets]. But doing this comes with a cost: these style sheets take precedence over local <style> elements (this can be easily worked around by removing the element and adopting its parsed style). Furthermore, it doesn't work with declarative shadow DOM at all.

Alternatively, I could inject all the global styles with duplicate <link rel="stylesheet">s. This comes with serious performance concerns: imagine a hundred posts on a TweetDeck-style UI for a microblogging site, each with <format-time> to format the time, <user-link> to format the username, and <format-text> to format the text. If you have 5 stylesheet tags on the site (say, normalize.css, your site's styles, Bootstrap, and a couple Bootstrap plugins) and want to use them all in that custom element's shadow DOM, that's 500 <link rel="stylesheet"> elements in that one page. The browser can cache style parsing per-URL to avoid the memory blow-up, but the browser can't avoid having to compute the entire stylesheet for every page, leading to high style compute times.

And in both cases, the cascade can't go through the shadow DOM.

How would you solve it?

Add a styleOrder ordered token list option for shadow root initializers:

  • outer - Set to apply parent styles.
    • If a shadow host is in a div in a parent and the corresponding shadow root's children has a span, the CSS selector div span would match the inner child.
    • If a shadow host is a foo-bar in a parent and the corresponding shadow root's children has a top-level span, the CSS selector foo-bar > span would match the inner child.
  • adopted - Set to apply adopted stylesheets. If not included, shadowRoot.adoptedStyleSheets has no effect.
  • inner - Set to apply inner stylesheets. If not included, inner <style> and <link rel="stylesheet"> elements have no effect.

The default, to match today's behavior, is styleOrder: "inner adopted". My problem would be addressed via styleOrder: "outer adopted inner".

Yes, this is intentionally immutable. It needs to be for perf reasons.

Yes, this allows disabling CSS customization entirely. But that's not the point, and I'm intentionally not proposing a way to disable scripting here.

For declarative shadow DOM, styleOrder can be specified via shadowrootstyleorder="...".

Anything else?

This addresses one of the big impediments to prerendering web components in my experience.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions