Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[css-pseudo][css-shadow-parts] Define "part-like pseudo-element" concept #10083

Closed
tabatkins opened this issue Mar 14, 2024 · 12 comments · Fixed by #10839
Closed

[css-pseudo][css-shadow-parts] Define "part-like pseudo-element" concept #10083

tabatkins opened this issue Mar 14, 2024 · 12 comments · Fixed by #10839
Labels
css-pseudo-4 Current Work css-shadow-parts-1 HTML Requires coordination with HTML people

Comments

@tabatkins
Copy link
Member

In the WHATNOT joint session with CSSWG today, we resolved that the new details pseudos would indeed be exposed as normal UA pseudo-elements (rather than ::part()s), but that we'd define a new concept of "part-like pseudo-elements" that grants the pseudo the abilities of ::part() that we want to preserve.

Namely:

  • a part-like pseudo has the ::part() blocklist of a few pseudo-classes (the tree-structural ones) and allows all other pseudo-elements and pseudo-classes, rather than the normal pseudo allowlist of a few pseudo-classes (the logical-combination and user-action ones) and banning all the rest.
  • a part-like pseudo has a well-defined inheritance relationship with the originating element and other part-like pseudos, rather than the normal "always inherit from the originating element" behavior.
  • the exportparts attribute will gain a syntax for exporting part-like pseudos, details TBD
  • we categorize future pseudo-elements as "part-like" when appropriate, and do a review of existing pseudos for acceptability of retroactively labeling them "part-like" and giving them these new powers

IIRC, the only real point of contention left is that, for normal parts, you only have access to the parts declared or exported by the component itself. If you expose a sub-component as a part, you do not get access to its parts too (that is, you can't ever write my-el::part(my-foo)::part(their-bar)), specifically so we don't expose the fact that the part is implemented as a nested component rather than plain DOM. But parts do provide access to their pseudo-elements (you can write my-el::part(my-foo)::before), because that's useful and many pseudo-elements aren't remotely appropriate to be exported as parts themselves.

But these two points are now in conflict. If you can export a part-like pseudo to be one of your component's parts, but you can also access it as a pseudo-element, it means you can spell the same selector two distinct ways: my-el::part(my-content) and my-el::part(my-details)::details-content.

This does expose the fact that a part is a particular built-in element, but that's a fact we already explicitly exposed, so it's not a new information leak. (Any element-specific pseudo-classes also expose this.)

It also means there's two ways to refer to the same element, but that's generally true of selectors, so I don't find this a particularly compelling problem.

I suggest, then, that we simply accept this as a possibility. But we could try to work around it in a few ways:

  1. Block access to part-like pseudos from a part. But this wouldn't be retro-compatible if someone was already doing, say, my-el::part(file)::file-selector-button, and it requires people to be very aware of the part-like pseudos exposed by any particular element.
  2. Block access to part-like pseudos from a part if they were exported. So you could write my-el::part(my-details)::details-contents or my-el::part(my-contents) for a given element, but never both. This potentially allows for inconsistent behavior, where some pseudos are allowed (because they weren't exported under another name) but others aren't.
  3. Block access to all the part-like pseudos from a part if any were exported. Same as (2), but now you're at least guaranteed that all the part-like pseudos are consistently shown or consistently hidden, and there's no back-compat issues like (1). But it still means that non-part-like pseudos are exposed on the part, along with any element-specific pseudo-classes, so you're still not meaningfully hiding the implementation details. And if we add more part-like pseudos to an existing element, they'd be blocked by default if a component exposed any of them, until the component was updated.

I think all three of these have downsides, and personally consider them to outweigh the downside of just "you can access it in two different ways". So I recommend not doing anything special for this.

@tabatkins tabatkins added css-pseudo-4 Current Work css-shadow-parts-1 HTML Requires coordination with HTML people labels Mar 14, 2024
@dbaron
Copy link
Member

dbaron commented Mar 14, 2024

Note that a downside of "you can access it in two different ways" is that we need to define and implement cascading the two things together. (I don't think defining it is hard since I don't think they differ in context, and I think any specificity differences are well defined. Implementing it might be trickier.)

@tabatkins
Copy link
Member Author

Yes, same context, so that's not an issue. Cascading (ideally?) shouldn't be any more difficult than what's already possible, since you have to cascade, say, ::part(foo) with ::part(foo):hover.

@tabatkins
Copy link
Member Author

There was also a topic in the conversation about ensuring that existing pseudo-elements should be marked as part-like, when they represent something that's basically (or actually) a real element under the covers. Here's the full list of pseudo-elements that are currently defined, according to Bikeshed, and categorized according to my initial impressions:

Clearly should work

  • ::after - it's just a box, you can do whatever to it
  • ::before - ditto
  • ::backdrop - ditto (mostly?)
  • ::part() - duh
  • ::slotted() - also refers to a real element, like an inverted ::part

Probably should work

  • ::file-selector-button - basically just a button, but might restrict its properties somewhat since the rest of the element's internals aren't defined

Probably irrelevant and shouldn't work?

  • ::view-transition - all of these live in a separate tree anyway, not part of any actual element (they're more pseudo-elements of the document), and when we have scoped VTs, while they'll be originating from some element they're still basically separate
  • ::view-transition-group()
  • ::view-transition-image-pair()
  • ::view-transition-new()
  • ::view-transition-old()

Maybe should work?

  • ::placeholder - defined with limitations so that it's kinda text-like, but in theory it could be just a box inside the input
  • ::marker - some questions to answer about exactly what its layout role is, but in principle it's just as "real" as a ::before
  • ::cue - this set is all WebVTT stuff. The way they're defined, these are all basically tree-abiding, well-behaved boxes, but the "elements" are generated by WebVTT rather than DOM, and don't meaningfully exist in most ways. Because of this they're limited in what properties they can accept.
  • ::cue-region
  • ::cue-region(selector)
  • ::cue(selector)

Clearly should not work

  • ::attr() - represents an Attr node, not a layout object (just there for doing selectors in contexts where Attr nodes are relevant targets)
  • ::first-letter - all of these are text-ish, which generally aren't tree-abiding
  • ::first-line
  • ::grammar-error
  • ::highlight(<custom-highlight-name>)
  • ::postfix
  • ::prefix
  • ::selection
  • ::spelling-error
  • ::target-text
  • ::nth-fragment() - not quite tree-abiding - it lives in the box tree, but doesn't have a virtual existence in the DOM tree. Probably restricted in what properties it can take, etc.

@tabatkins
Copy link
Member Author

Pinging @rniwa in particular for the above list, since they brought up this aspect in the call.

@vmpstr
Copy link
Member

vmpstr commented Mar 15, 2024

Re view-transition pseudos: the one point that applies here is that they are currently specced to inherit from the parent vt pseudo element, not from the originating element. As you mentioned, in the future they could also be present on elements other than :root. Early in the design discussions for view transitions, we actually weighted the pros and cons of using shadow DOM vs pseudo elements for this tree construction, so vt is similar to both in how its used

I'm not clear on some of the points you mentioned that part-like pseudos would provide, but if they give an ability for shadow DOM to export them and be styled by the host, then it may be relevant to the discussion. And most of those elements do have boxes and possibly act like images

dbaron added a commit to dbaron/csswg-drafts that referenced this issue May 28, 2024
This adds the ::details-content pseudo-element as resolved in:

1. w3c#9879 (comment)

2. whatwg/html#10200 (comment) /
   w3c#9951 (comment)

3. w3c#9879 (comment)

and uses the definition added in w3c#10083.
tabatkins added a commit that referenced this issue May 30, 2024
* [css-pseudo] Define part-like pseudo-element. #10083

* [css-pseudo] Merge definitions into one section, and add example.
dbaron added a commit to dbaron/csswg-drafts that referenced this issue May 30, 2024
This adds the ::details-content pseudo-element as resolved in:

1. w3c#9879 (comment)

2. whatwg/html#10200 (comment) /
   w3c#9951 (comment)

3. w3c#9879 (comment)

and uses the definition added in w3c#10083.
dbaron added a commit that referenced this issue May 30, 2024
This adds the ::details-content pseudo-element as resolved in:

1. #9879 (comment)

2. whatwg/html#10200 (comment) /
   #9951 (comment)

3. #9879 (comment)

and uses the definition added in #10083.
@dbaron
Copy link
Member

dbaron commented Aug 27, 2024

Part of the needed spec changes here have landed.

One part that I think has not landed yet is a definition that says that part-like pseudo-elements have the same rules as ::part() does for when pseudo-elements and pseudo-classes are allowed after them. (I'd note that I just filed #10786, #10787, and #10788 on these rules.)

It's also possible that those rules should move to selectors rather than css-shadow-parts; that might also help with #10787.

@dbaron
Copy link
Member

dbaron commented Aug 27, 2024

I think there's also an open question about how to extend the rule that ::part(a)::part(b) is not allowed to part-like pseudo-elements. I think perhaps the right rule is that part-like pseudo-elements and ::part() can be combined in a compound selector subject to the constraint that the compound selector has at most one ::part(). But I'm curious if others think that's the right approach.

@dbaron
Copy link
Member

dbaron commented Aug 28, 2024

I think there's also an open question about how to extend the rule that ::part(a)::part(b) is not allowed to part-like pseudo-elements. I think perhaps the right rule is that part-like pseudo-elements and ::part() can be combined in a compound selector subject to the constraint that the compound selector has at most one ::part(). But I'm curious if others think that's the right approach.

Actually, I think that suggestion was more complex than needed. I think since pseudo-elements are built-in, and we aren't planning to expose parts on built-in things, there isn't any need for ::part() following part-like pseudo-elements. So I think probably the rule that we want is that ::part() is not allowed after ::part() or any part-like pseudo-element. Then we don't need any constraints about the entire compound selector and can continue to describe the rules in terms of "is X allowed to follow Y".

@tabatkins
Copy link
Member Author

Yeah, just disallowing ::part() after a part-like is fine; part-likes are equivalent to ::part() and similarly don't want to expose further internal structure.

@tabatkins
Copy link
Member Author

(And just to be clear, the inverse is not true; part-likes other than ::part() should be allowed after part-likes. That's already required for compat, since you can access various pseudo-elements on a ::part, and it's the right move anyway imo. This does require that we stick to our guns that built-in parts are always exposed as bespoke part-like pseudos, or at least are exposed as both bespoke pseudos and ::part() names.)

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-pseudo][css-shadow-parts] Define "part-like pseudo-element" concept.

The full IRC log of that discussion <dholbert> dbaron: this one, we already had a resolution, but I wanted to give a brief update since the state of things is confusing
<dholbert> dbaron: so we resolved to make this :part-like pseudo-element concept
<dholbert> dbaron: half of the edits are edited in, but not all of them
<dholbert> fantasai: e.g. this edit, which has been waiting for this current discussion
<fantasai> -> https://github.com//pull/10839/files
<dholbert> dbaron: we've made some of the edits, to make partlikes like ::part
<dholbert> dbaron: earlier we were discussing what selectors and pseudo-classes apply after ::part
<dholbert> dbaron: those all apply to ::part-like as well, and I'm planning to make those edits
<dholbert> dbaron: Tab made edits about what properties apply. But the selector edits have not yet been made
<dholbert> dbaron: that's all

fantasai pushed a commit that referenced this issue Oct 15, 2024
@yisibl
Copy link
Contributor

yisibl commented Oct 24, 2024

This has been renamed to element-backed pseudo-element.
24eda7a#diff-4ffcf0741c5ab1b2bc788d6e2246559785c2a7e66c89ebc463224052170c2498R1602

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
css-pseudo-4 Current Work css-shadow-parts-1 HTML Requires coordination with HTML people
Projects
Status: Friday afternoon
6 participants