Re: [csswg-drafts] [css-shadow-parts] Unifying ::part() and ::--foo (#4900)

So I gave some more serious thought to what it would look like to let parts expose pseudo-classes as well, and yes, I'm pretty certain it introduces unresolveable problems.

So here's my sketch of how this all would work (forgive me for being somewhat stream-of-consciousness with it, tldr at the end):

First, a component can, itself, expose custom pseudo-classes declaring its state to the consumer of the component. This uses the API we're already talking about, no elaboration needed here.

Then, the component can expose parts of itself as pseudo-elements. Since our current concept of "part names" are class-like, but the pseudo-element syntax is restricted to not allow that semantic, we change things up a bit - custom elements can just expose one or more `::part` pseudo-elements, and can add pseudo-classes to give more detail. So a calendar widget's days would all be `::part:--day`, and could be further selected with `::part:--day:--weekend:--holiday` for specific styling.

(If we allow the author to denote one of the keywords as the "name" of the pseudo, so the user could instead write `::--day:--weekend:--holiday`, this doesn't materially change my analysis, so without loss of generality, let's continue to assume this doesn't happen and you only get `::part`.)

But the part could *itself* be a custom element, which can expose both (a) its own pseudo-classes, and (b) its own parts.

If the inner component's pseudo-classes are automatically exposed to users of the outer components, we have an obvious potential for clashes, causing upgrade hazards we've tried hard to avoid; the `<x-day>` component might expose a `:--holiday` pseudo-class of its own, in a way that clashes with the calendar's desired usage. (For example, it might assume US holidays, while the calendar is internationalized and can do different countries.)

If the inner component's pseudo-classes are *not* automatically exposed, but rather have to be forwarded by the outer component, this is avoided, but it means more work for the component author: you can't just use a `<cool-checkbox>` component as a part and be done; instead you have to manually forward *all* the form-relevant pseudo-classes the element might expose.  We don't want to allow wildcard forwarding, for the same reason we don't allow that for part names without a guarding prefix: it reintroduces the possibility of clashes.

But that's not all, the inner component can also have parts to expose, which, per our existing resolutions, need to be explicitly forwarded as well. So the outer component has to have a *separate* mechanism to filter/forward the parts. But now we have an interesting question: how? By the pseudo-classes it exposes, presumably? Or does it distinguish between the (external) part-defined pseudo-classes and the (internal) component-defined ones, and only filter on the former?

And then how do you decide which of the *forwarded* part's pseudo-classes should be exposed? Filtering is necessary at every level, after all; there's no good reason you should be able to filter your own parts but not any forwarded parts.  Do we need a *third* forwarding mechanism to handle those?  How, exactly, do we identify which forwarded parts are having their pseudoclasses filtered? The only way to refer to them is by their pseudo-classes, after all; this isn't like our own parts, where the element being filtered is the one we are adding the attributes to. So instead we're now dealing with filtering *sets* of forwarded parts, and have to deal with single parts showing up in multiple such sets and having clashing forwarding advice, too.

(Luckily we don't recurse further; any components nested further within the inner component have already been forwarded and filtered by the inner component, and we just have to handle its results.)


So yeah, ultimately the issue is that we're collapsing levels, something which is notoriously fraught. Part names and component pseudo-classes are distinct namespaces because they're controlled by different authors, but if we push them into the same namespace, avoiding clashes becomes a *lot* more complicated, to the point that I don't think authors can reasonably navigate it.

So I'm pretty strongly still of the opinion that part names and custom pseudo-classes, despite their partial overlap in semantics, should be kept separate.  And that custom pseudo-classes should be exposed on parts by default, without any filtering mechanism.

GitHub Notification of comment by tabatkins
Please view or discuss this issue at using your GitHub account

Received on Wednesday, 25 March 2020 21:54:25 UTC