[csswg-drafts] [css-selectors] Proposal: Selector Boundary (#5057)

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

== [css-selectors] Proposal: Selector Boundary ==
An element can be marked as having a selector boundary:

```
<div boundary="my-element">
  <h1>My Element</h1>
</div>
```

This states that a css selector will not match the element unless the correct boundary is specified (please note use of pseudo class is very much tentative):

```
  // will not match:
  div h1 {
    color: red;
  }

  //will match:
  :boundary(my-element) h1 {
    color: green;
  }
```

If the tag name contains a hyphen, and a boundary attribute is present with no defined value:

```
<my-element boundary>
  <h1>My Element</h1>
</my-element>
```

Use of the tag name in a selector will be a valid match:

```
// won't match
h1 {
  color: red;
}

// will match
my-element h1 {
  color: green;
}
```

## Why?
Using Shadow DOM to encapsulate styles may not be the optimal solution for all use cases. For a component-based application where the author has full control, the concern is often limited to preventing styles defined for one component leaking into others. This has led to CSS-in-JS solutions to emulate scoping. While these solutions have other merits (modularisation and tree-shaking), downsides can be, depending on the solution and viewpoint: a tighter coupling between css and a component's template, or an inability to define styles for an element and its descendants in the standard, declaritive, way. This proposal offers a way for selectors defined in global stylesheets to not apply beyond defined boundaries, effectively enabling style scoping for components.

Another reason is to allow for theming of embedded library components. As discussed in https://github.com/w3c/webcomponents/issues/864, there is a desire to allow greater control of styling for users of component libraries. I share the opinion with others in that discussion that allowing the shadow root to be pierced will be detrimental to the maintainability of the component. Still, a component author may wish to distribute a component with the assurance that outside styles will not leak into it *by default*, while knowingly allowing the styles for the component to be overridden freely by the user of the library. This doesn't necessarily have to replace the need for greater theming of a shadow root, but it does give the component author more flexibility if they feel using Shadow DOM isn't an optimal solution for the situation.

Thirdly, and not much thought has gone into this so it may be a bit of a stretch... it could enable better SSR of elements that will eventually attach shadow roots on script execution. With selector boundaries superficially encapsulating styles only, it may be possible for the initial render to utilise them, before hydrating the elements with their shadow roots.

## Other details

### Nested boundaries

When a boundary is specified for an element that is a descendant of an element that has its own boundary:
```
<my-grandparent boundary>

  <my-parent boundary>

    <my-child boundary>
      <h1>My Child</h1>
    </my-child>

  </my-parent>

</my-grandparent>
```

A selector only has to match the single most-relevant boundary to be valid, not the full hierarchy:
```
// will match
my-child h1 {
  color: green;
}

// will not match
my-parent h1 {
  color: red;
}

// will match but is not necessary
my-grandparent my-parent my-child h1 {
  color: green;
}
```

### Inheritance
Allowing for inheritance to pass through the boundary would depend on if there are any use cases where it would be wanted, and may be a separate concern. Preventing it can already be handled quite easily with ```my-element { all: initial; }```

### Default boundary for custom elements
A custom element could be marked with a boundary quite easily:
```
class MyElement extends HTMLElement {
  connectedCallback() {
    this.setAttribute('boundary', ''); // perhaps a new "boundary/selectorBoundary" property instead
  }
}
```

Alternatively, the following could be supported:
```
customElements.define('my-element', MyElement, { selectorBoundary: true });
```

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

Received on Saturday, 9 May 2020 20:41:16 UTC