[whatwg/dom] Composed shadow DOM (#531)

This is similar in nature to #510, but comes at it from a slightly different approach, very much similar to that of `<style scoped>` but using existing shadow DOM primitives.

I've written up my proposal [here](https://gist.github.com/treshugart/dafb15f613bb7664d451f582da512f63) but will copy it below for convenience.

# Declarative / composed Shadow DOM

This is a light proposal for Shadow DOM (no pun intended). The purpose of this proposal is to flesh out how we can take the current state of Shadow DOM and allow certain aspects of it to be used separately and composed together. The goals are:

1. Being able to use CSS encapsulation separately from DOM encapsulation (SSR is one beneficiary of this)
2. Being able to enable DOM encapsulation on a previously CSS-only encapsulated node (rehydration)
3. Maintaining backward compatibility

## CSS-only encapsulation

The idea of CSS only encapsulation has been previously tried and aborted with `<style scoped />`. I've been told that it was abandoned because it was slow. I'm only speculating, but the only difference between the encapsulation model is that `<style scoped />` worked for the parent and *entire* tree below it. It also had to factor in descendant `<style scoped />` elements.

The way I'm proposing that CSS encapsulation works is the same way it does now, it can simply be used *without* the DOM encapsulation aspect. For this to work, you need to know the outer boundary (host) and the inner boundary (slot). Given that there's already ways to flag the inner aspect (via <slot />), we only need a way to signify the outer boundary. I propose using a `composed` attribute on the host.

```html
<div composed>
  <style>
    p { border: 1px solid blue; }
  </style>
  <p>
    <slot></slot>
  </p>
</div>
```

There's probably similar ways to do this, but the important point is that you can declaratively enable encapsulation for CSS.

### Server-side rendering

If you could enable CSS-only encapsulation, it's pretty trivial to serialise a DOM / shadow tree on the server. This has two benefits.

#### Deferred / selective upgrades

You might be using custom elements / shadow DOM to template out your layout, but there may be no need to actually *upgrade* it if it's static and all it does is render once. This means that you don't need to deliver the custom element definitions, the template engine, and your templates for a subset of your components because they're display-only.

If you're upgrading components, you may want to defer their upgrades, or optimise them. CSS-only encapsulation would enable you to deliver HTML that looks like it would on initial upgrade so there's no jank.

#### Bots

Many bots that don't execute JavaScript, or that may parse content differently, can still have access to the content because it's all accessible via the HTML string.

Currently if you have an `<x-app />` component that renders your page, and you want it scraped, bots other than GoogleBot won't read the content.

```html
<x-app>
  #shadow-root
    can't see this
</x-app>
```

To me, this is unacceptable because it breaks the web. Sure, *some* bots might catch up, but not all, and should they? This also makes shadow DOM not viable until they do. Do we want to hamstring web components in such a way?

This is what it'd look like with CSS-only encapsulation.

```html
<x-app composed>
  can see this
</x-app>
```

Once your custom element is delivered to the page, it can be upgraded. The next section describes how this occurs.

## Enabling DOM encapsulation

Given a CSS-only encapsulated element, we can quite easily apply DOM encapsulation. Let's take the following example.

```html
<div composed>
  <style></style>
  <p><slot>slotted content</slot></p>
</div>
```

To enable DOM encapsulation, we could follow the current model and use `attachShadow()`. When this is called, the following steps take place to perform what we're calling: rehydration.

1. Remove content.
2. Attach shadow root.
3. Add previous light DOM as the shadow root content.
4. For each slot, append its content as light DOM to the host if it doesn't have a `default` attribute.

The `<slot default />` attribute is a way to tell the rehydration algorithm that it should not re-parent its content because it's representing the default content of the slot.

The above tree would end up looking something like:

```html
<div composed>
  #shadow-root
    <style></style>
    <p><slot></slot></p>
  slotted content
</div>
```

### Caveats

1. Unslotted content isn't taken into account yet.

## Backward compatibility

Since `attachShadow()` already exists, and couples both DOM and CSS encapsulation, nothing changes here. This is also why there's no separate way to do DOM only (without CSS) encapsulation. While it makes sense to have CSS-only encapsulation, I don't think it makes sense to have DOM-only because it would be confusing to have something hidden (in the shadow) in a node tree, that is affected by global CSS.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/whatwg/dom/issues/531

Received on Thursday, 2 November 2017 22:52:41 UTC