Re: [w3ctag/design-reviews] Early design review of light-DOM CSS Scope proposal (#593)

I'm very happy to see a proposal that aims to address the problems that cause patterns like BEM or CSS Modules to proliferate.
Also, nice, thorough explainer. 💯 

So, to decouple the different parts of this:

- Namespacing is addressed via essentially nesting + proximity prioritization through a new `@scope` rule
- Subtrees can optionally be excluded from matching via lower boundaries. This seems useful beyond `@scope`, so I wonder if it would be better expressed as a selector extension.

Some lower level comments below.

> I think this should work inside and outside of the shadow DOM without any special concern for shadow boundaries.

This is a bit ambiguous. I can see two possible meanings:
1. `@scope` should match on the whole tree ignoring shadow boundaries.
2. It should be possible to use `@scope` both in CSS that is applied to light DOM, as well as CSS that is applied to Shadow DOM.

If you meant 2, that seems pretty uncontroversial. But in case you meant 1:

I think this would break author assumptions about shadow DOM. Also, it was decided early on that we don't want explicit access to styling shadow DOM structure that has not been explicitly exported. Furthermore, my understanding of implementations is that this kind of selector matching across Shadow DOM boundaries would be very difficult to implement.

> Given a syntax of `@scope (<selector>)`, are we placing any restrictions on the `<selector>`?

It is unclear whether the parentheses are optional or mandatory. In the examples that follow the part I quoted, there are no parentheses. Is that a typo or does it intentionally demonstrate that no parentheses are required in certain cases? Parentheses should definitely be required to disambiguate complex selectors, otherwise you don't know if `@scope foo to bar` is scoping `foo` until `bar` or just scoping under selector `foo to bar` (`to` could be a type selector targeting `<to>` elements).

> Getting even more complex, is there reason to allow selector lists -- defining multiple roots for a single scope block?

I definitely see use cases for selector lists as lower boundaries, e.g. blocks with multiple different areas to exclude.

> [Can scoped selectors reference external context?](https://github.com/oddbird/css-sandbox/blob/main/src/scope/explainer.md#can-scoped-selectors-reference-external-context)

It is definitely useful to be able to vary a component's style based on its context. However, simply allowing a part of the selector to match outside the scope could lead to unintentional effects, when the author has not anticipated this. Consider this:

```html
<div class="b">
 <div class="a">
  <div class="c"></div>
 </div>
</div>
```

```css
@scope (.b) {
 .a .c { /* matches! */ }
}
```

Now consider this HTML, with the same CSS:

```html
<div class="a">
 <div class="b">
  <div class="c"></div>
 </div>
</div>
```

The rule still matches, but the author may have not intended this, which breaks the isolation.
To enable the use cases without unintended leaking I would suggest an explicit opt-in of some sort. Perhaps selectors containing `:scope`?

> For normal declarations the inner scope's declarations override, but for ''!important'' rules outer scope's override.

Could you elaborate on this? I don't understand it as written.

> Another idea that I considered was to combine the specificity of the scope selector to the specificity of nested rule-blocks:

Until this point, I was under the impression that part of the reason for having an at-rule instead of a "donut" selector was the different cascade mechanism. If it just resolves to the same specificity as [nesting](https://drafts.csswg.org/css-nesting), then it could have scope leaks. Consider this:

```css
#main a { color: red }

@scope (.my-component) {
 a { color: purple; }
}
```

```html
<main id="main">
 <div class="my-component">
  <a>This is red?!</a>
 </div>
</main>
```

-------

It may also be interesting to explore what a JS API for fetching "donut scope" elements could look like. I've often needed this, and ended up querying and filtering using `element.matches()`, which is rather expensive.
On the other hand, if the lower boundary is expressed via a selector, and `@scope` merely enforces the different scoping, regular qSA would suffice.

--------

@kenchris 

> I think that having multiple CSS scoping methods that aren't build on the same principles or underlying primitives is problematic, and we should attempt to avoid that.

This gives me pause as well. It would be great if these issues can be solved with existing primitives. Though if there are widespread problems the existing primitives cannot reasonably address, solving the problems is more important than keeping the set of primitives constant.

> There are real problems with shadow DOM scoping today, but solving those outside of shadow DOM would not be ideal. For instance, I have heard the need for scoped light DOM styling, ie. a more powerful ::slotted(), which seems related to this.

My understanding is that this solves different problems than Shadow DOM. There are many use cases for partial style encapsulation where that is the only kind of encapsulation needed, and Shadow DOM would be too heavyweight a solution. Also, this being entirely a CSS side solution means it can address use cases where existing HTML is being styled.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/w3ctag/design-reviews/issues/593#issuecomment-761295677

Received on Saturday, 16 January 2021 02:19:07 UTC