Open jakearchibald opened 2 years ago
Related: #4565
Interesting! So with this proposal:
*, * :>> * { box-sizing: border-box }
…would apply border-box to everything.
I hashed this out with Jake last week, and like this proposal; it addresses several of the pain points I've run into with pseudo-element syntax over the years.
Small detail, probably obvious from the examples: when selecting into the "pseudo tree" of an element, the pseudo-elements are treated as having tagnames equal to their names, so you use type selectors to target them. div :> before
hits a ::before
pseudo, for example. This requires us to broaden the syntax of Selectors slightly to allow type selectors to also be functions, for things like div :> slotted(.foo)
, but that shouldn't be problematic.
Also, in chained pseudo-elements like ::before::marker
, the ::marker
is part of the pseudo tree" of the before, so you do have to write ` :> before :> marker;
:> before > markerwon't work, as the before pseudo-element doesn't have a
child element. The pseudo-descendant combinator
:>>just descends down the pseudo-children links, so
Jake's listed motivations are all great, and why I'd like this. In particular, it would allow us to finally revise the data model of pseudo-elements to make some dang sense when combined with other features like relative selectors and nesting, which makes me very happy. The foo::bar
syntax will finally no longer be considered a weird compound selector; it's just a legacy way to write the complex selector foo :> bar
, with a proper combinator and everything.
If :>
is just a normal combinator, then this can address part of #2284 too:
something#very.long :> :is(before, after) /* something#very.long::before, something#very.long::after */
But this wouldn't work:
something#very.long:is(*, * :> after) /* something#very.long, something#very.long::after */
Right, it would do the first but not the second. The second is still changing the subject of the selector, which is something that a pseudo-class fundamentally cannot do. We don't allow .foo:is(*, * > *)
(to select the foo element and its direct children) either.
just a note
:>
seems to cause issues in tooling.
Quickly tested this in a sass and parcel-css playground and both just give up entirely.
PostCSS with popular plugins does a bit better and outputs something but also gives warnings. VSCode colors a lot in red but eventually recovers.
Making :
the start of a combinator instead of a pseudo class or pseudo element will require a lot of work. It will take a while for the ecosystem to catch up to something like this :)
Update :
This would be a major improvement imo.
:>
seems to cause issues in tooling.
I don't think any new combinator will work out of the box.
I don't think any new combinator will work out of the box.
That I understand, but there is a subtle difference between a new combinator which isn't recognised as a combinator and something that causes tools to stop processing CSS.
This is all fixable, but it will take time and considerable effort.
I do think we need the :>
operator or something very similar.
Maybe we're saying the same thing, but every potential new combinator I try in https://parcel-css.vercel.app/ fails, except something like ::descendant(foo)
, and that kind of thing is prohibitively long.
Have you found something that could work better?
Have you found something that could work better?
No and I think :>
is very expressive and the best choice for the feature.
I've also been looking at the other side in tools -> how many bugs there are because .foo::before
is seen as a compound selector and this is quite extensive. 4 of those were created by me in PostCSS plugins (all which have been fixed).
So my initial opinion has changed from "this will be a lot of work" to "this will be a lot of work and a whole class of bugs might get fixed"
Typically, if a complex selector matches an element, you can take just the last compound selector and it will still match.
So if div :> before
matches a pseudo-element and :>
is a normal combinator, does it mean that a before
alone will start matching pseudo-elements? Or are pseudo-elements featureless and can only be matched if there is a ::
, :>
, :>>
? Will before :> marker
match a ::before::marker
?
This cuts into the same issue as when we had the /deep/
combinator proposed for Shadow DOM. Short answer is no, the invariant tightens to being "last compound selector before a tree-hopping combinator", since the initial set of elements visible to selectors are only those in a given tree.
Suggest using ::
instead of :>
and :::
instead of :>>
.
Tab previously looked into ::
: https://github.com/w3c/csswg-drafts/issues/2284#issuecomment-364580632
Yeah, ::
is entirely unusable for reasons we will never be able to get around. That well is poisoned, dug up, then exploded for good measure.
And without ::
, :::
doesn't make a lot of sense.
I'd like to bring up @bradkemper’s response to that old thread here:
I disagree. I don't find it "much more understandable." In fact, I think having "::foo" as one chunk of text (no spaces) makes it much more clear that "foo" is not looking a
element in the markup, but is instead something special (an element created by the CSS and then selected). It might be a little more clear if there is a space before the "::". But honestly, I didn't have any problem parsing "p::before:hover::marker" in my mind. Because I have learned what "::" means already, and learning it with no spaces wasn't hard.
The CSS Working Group just discussed [selectors] Child & descendant pseudo element combinators
, and agreed to the following:
RESOLVED: Start selectors 5 with the experimental work from this issue
Some pseudo elements refer to real elements like ::slotted. Right now can't access further into tree
Actually, the spec allows things like ::shadow > p
, https://drafts.csswg.org/selectors-4/#pseudo-element-structure
Yeah, but we undefined ::shadow
, so no currently-existing pseudo-element allows it, I think. ^_^
Right now,
div::before::marker
means: Select the::marker
pseudo elements, that are a child of a::before
pseudo element, that originate from adiv
.There isn't a way to say: Select the
::marker
pseudo elements, that originate somewhere within the pseudo element tree of adiv
.It'd be great if we had a way to achieve this, such as:
:>
- select child pseudo elements. As in,div :> before :> marker
would be equivalent todiv::before::marker
:>>
- select descendant pseudo elements. As in,div :>> marker
would enable the use-case above.Future additions could include other combinators prefixed with
:
, such as:+
, to target adjacent sibling pseudo elements.This use-case came up in shared element transitions. Over there, we create pseudo-trees that are more complicated than (I think) we've seen in other features:
Our current model is to expose these through the following selectors:
html::page-transition-container(id)
html::page-transition-image-wrapper(id)
html::page-transition-outgoing-image(id)
html::page-transition-incoming-image(id)
However, these don't really communicate the structure, and won't play well with upcoming features like nesting, or
:has
.We'd like to expose the pseudos as they're structured, so instead of:
It would be:
Which plays well with nesting:
This would also allow for selectors like:
Note: According to https://github.com/w3c/csswg-drafts/issues/7463, the above won't be possible.
…which selects the
outgoing-image
pseudo element within apage-transition
pseudo element, that doesn't also have anincoming-image
. Although, for that particular case, I hope we can make something like this work: