[csswg-drafts] [css-cascade][css-scoping] Host layerable shadow roots (#9792)

knowler has just created a new issue for https://github.com/w3c/csswg-drafts:

== [css-cascade][css-scoping] Host layerable shadow roots ==
_I originally proposed this here: https://github.com/WICG/webcomponents/issues/909#issuecomment-1883480640. I talked with @mirisuzanne and she recommended we continue to flesh out this proposal here before bringing it back to WICG. What follows is more so geared towards one who has read/understands the CSS Cascade spec (see the original proposal for a more web-components-oriented perspective)._

This is a proposal to allow cascade layers within an outer context (e.g. a document or shadow root) to style elements within an inner context (i.e. elements within a shadow root).

In regard to the Cascade, this would effectively bump the corresponding shadow context down in [the sorting order](https://www.w3.org/TR/css-cascade-5/#cascade-sort) to be within the outer context’s layers (i.e. sorted after whatever layer it was marked for access).

For authors of the outer context (e.g. a consumer of a Web Component using the Shadow DOM), this would mean greater styling flexibility than what is available through existing shadow root styling APIs (e.g. CSS Parts, slots, custom properties).

For authors of the inner context (e.g. an author of a Web Component using the Shadow DOM), this would mean reduced style encapsulation—but style encapsulation nonetheless:

- Since the inner context’s styles would be cascaded _after_ the permitted outer context’s layers, the inner context’s styles would take precedence unless the those layers were using `!important`.
  - This might seem a bit strange, but I think that it forces the consumer to understand that what they are overriding is an _important default_ for the subsequent levels in the cascade. If the component author has set an element with “important defaults” as a CSS Part that might be a more appropriate API for the consumer to use to style said element where it’s actually used.
- The inner context can use `all: revert` to effectively ignore styles from the host’s permitted layers for an element.
  - Again, this could be circumvented by the author of the outer context using `!important`.
- The inner context would still remain isolated from subsequent layers (including unlayered styles) which would mean its own styling APIs using CSS Parts, slots, and custom properties would still be effective.
- Allowing a component consumer to set some default styles can be advantageous for a component author:
  - Slots can be burdensome for their consumers and lose the benefits of DOM encapsulation for the component author.
  - CSS Parts might be too limited for a consumer and awkward to work with, especially with an existing system.

## How this would work?

**TODO: add better examples here**

Here’s an idea for the CSS syntax (`shadow()` might not be great since `drop-shadow()` is a thing):

```css
@layer defaults shadow(my-element), components, layouts;

@layer defaults {
  /* Anything here will apply to elements in `my-element`’s shadow tree */

  button {
    background-color: DeepPink;
  }
}

@layer components {
  /* Subsequent layers will not apply to the shadow tree’s elements */
}

@layer layouts {
  /* Subsequent layers will not apply to the shadow tree’s elements */
}

/* Unlayered styles (i.e. a subsequent layer) will not apply to the shadow tree’s elements */
```

In this case, the simplified expected sorting order (for elements in the shadow root):

1. User agent styles
2. User styles
3. The host context’s `defaults` layer.
4. The shadow context
5. The rest of the host context’s styles starting with the `layouts` layer for the exposed elements (e.g. CSS Parts).

In cases where the shadow root was layered after the first layer, it would still receive the styles of the previous layer(s):

```css
@layer defaults, components shadow(my-element), layouts;

@layer defaults {
  button { /* These will apply to `button` elements in `my-element`’s shadow tree */ }
}

@layer components {
  @scope(my-button) {
    button { /* These will apply to `button` elements in `my-element`’s shadow tree */ }
  }
}

@layer layouts {
  .some-layout button {
    /* These will not apply */
  }
}
```

For this case, the simplified expected sorting order (for elements in the shadow root):

1. User agent styles
2. User styles
3. The host context’s `defaults` and `components` layer.
4. The shadow context
5. The rest of the host context’s styles starting with the `layouts` layer for the exposed elements (e.g. CSS Parts).

## Use cases

**TODO: add more use cases**

- It allows a component consumer greater styling control over a component than what is currently available through CSS Parts.
  - TODO: explain why CSS Parts is not a great API for this kind of styling (it’s good for other styling thouhg).
- It allows a component author a way to allow elements in their tree to receive “defaults” from the document while still preserving the component’s own styling API (whether that be using parts or custom properties).
- It can allow legacy CSS design systems to apply to new custom elements that are using the Shadow DOM for templating.

Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/9792 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Saturday, 13 January 2024 03:09:57 UTC