Re: [w3c/webcomponents] Custom pseudo-classes for host elements via shadow roots (:state) (#738)

I really like the idea of "attaching internal" APIs to expose internal features.

I think that 

```
#internals = this.attachInternals()
//...
this.#internals.pseudoClasses.add('hover')
```

would work perfectly for the scenario in the OP (which uses zero shadow roots).

I have a some questions and thoughts:

### 1) Why another new property on all elements as opposed to separate classes (or something)?

One concern I have is that this adds yet one more property all elements now have. I like the separate-class idea (`new ElementInternals(this)`), because it doesn't add yet more properties to the already giant list that all elements inherit. The example using a separate class would be:

```js
#internals = new ElementInternals(this)
```

I hope `#internals = this.attachInternals();` does not instantiate all possible internal APIs, and that `this.#internals.pseudoClasses` is a getter that instantiates that particular `pseudoClasses` API only on first read. Is optimization like this supposed to be expressed in the spec? Or do we just leave it to browsers to implement such optimizations?


With separate classes, it could be:

```js
#cssSates = new ElementCSSStates(this) // Note the "CSS" to make the feature name clear, bikesheddable
#pseudoClasses = new ElementPseudoClasses(this)

//...

this.#cssStates.add("foo")
this.#pseudoClasses.add("hover")
```

Is there a reason why all elements having an `attachInternals` property is clearly better than the separate-class idea?

The only one thing I can really think of is that `this.attachInternals().` in VS Code intellisense will show possible completions for available APIs. But good documentation will do too.

### 2) If we have custom pseudo classes, what would happen when users try to add things like `:empty`?

What happens if a CE author tries to do

```js
#internals = this.attachInternals();
//...
this.#internals.pseudoClasses.add('empty')
this.#internals.pseudoClasses.add('last-child')
```

or similar with others?

Maybe we need to somehow make a clear distinction between interaction states and the others (which merely describe the DOM)

If we limit the new surface to only custom `:state()`, then it will be a little strange to see `:hover` in some parts of styles for built-in elements, and `:state(hover)` for other elements like in the OP that render purely with WebGL where the CSS engine can not possibly know when to apply `:hover` (there's only a single canvas element that the mouse is interacting with).

### 3) keeping state only as custom element internals

@justinfagnani, you mentioned

> A custom element may very well want to make public a part that itself has state, and the part may not be a custom element itself.

If this is the case, then classes would work perfectly here because the public surface area of class names on the internal part is still private to the custom element.

The custom element only needs to expose the part, and set classes on it (which the end user will not touch), and the end user will style it like this:

```css
some-element::part(foo).expanded
```

This works great, because the custom element is using the public API internally on the internal shadow DOM, just as classes were designed for, then making that element stylable as a part.

Therefore I hope this convinces people that we do not need state as a public-side feature, because classes cover it.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/w3c/webcomponents/issues/738#issuecomment-620230506

Received on Monday, 27 April 2020 20:58:53 UTC