-
Notifications
You must be signed in to change notification settings - Fork 717
Mixing :is() (or equivalent) with pseudo-elements #9702
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
Comments
Yeah, I'll give it some more time to percolate, but some variant on my "Second Try" idea is probably what I'm aiming for. Pulling it out to make it easier to find/reference: Problem statement: we'd like the following to be valid: .foo {
&, &::before, &::after {
&:hover {
color: blue;
}}} ...and have the obvious result, equivalent to: .foo:hover, .foo::before:hover, .foo::after::hover {
color: blue;
} Solution: introduce a new combinator,
Nesting can then define itself on top of this:
|
While this can be useful on its own, I don't see why nesting needs
into
|
No, because you still need to distinguish |
Well yeah,
|
I'm not sure what you mean by this. It's a combinator (just a no-op one). |
The |
Yes, we need this feature. For example, if you want to set the input track for both browsers at the same time, you need to write the same rule set twice, and it won't work in input[type=range]:is(
::-moz-range-track,
::-webkit-slider-runnable-track
) {
background: #ddd;
} |
@yisibl That should already be doable as input[type=range] {
&::-moz-range-track, &::-webkit-slider-runnable-track {
background: #ddd;
}
} Edit: Sorry, still half asleep. This actually this only works in Firefox because unrecognized ::-webkit- pseudo-elements are accepted, but other browsers will drop the entire selector due to not supporting Or #7346 proposes input[type=range] :> :is(-moz-range-track, -webkit-slider-runnable-track) {
background: #ddd;
} |
@Loirooriol O(∩_∩)O Haha, yes the core point here is the need for a pseudo-element selector that supports forgiving-selector-list. |
So i kind of got redirected here :) I ran into the following issue: This works: sl-month-view::part(finish) {
background: var(--sl-color-success-plain);
border-radius: 50%;
color: var(--sl-color-text-inverted);
}
sl-month-view::part(finish):hover {
background: var(--sl-color-success-bold);
} But this doesn't: sl-month-view::part(finish) {
background: var(--sl-color-success-plain);
border-radius: 50%;
color: var(--sl-color-text-inverted);
&:hover {
background: var(--sl-color-success-bold);
}
} This behavior is not obvious to me and it feels like both should work just fine (from a web dev point of view). @tabatkins can you confirm i'm in the right issue for this? |
@jpzwarte I think |
Then ideally we would disallow
This would then desugar to:
Are there any restrictions on where lists with pseudos can appear
What does
Is there anything that could be improved in this proposal if we first fix that mistake (as an alternative way to access pseudo elements), and then make that form work well with nesting? |
In #7346 (comment) we already resolved on an (experimental) solution. |
Well, we don't currently have that limitation, and Nesting has been out for long enough that we probably can't change that.
I think it is somewhat unreasonable. For any normal element matched by the parent rule, If you wanted the other way, you'd need the pseudo combinator, and you'd write
No, since they're no longer nesting the
No. It's just another option for the
Yes, that's valid, and equivalent to
No, that's not the side that's problematic for Nesting. Nesting has issues due to our decision to keep it as purely a sugar as possible, without causing combinatorial or exponential blowup in the desugaring. That means we're limited to what The problem is that, unless we introduce a third way to refer to the "subject" coming from outside the That is, we could move the selector into the parentheses, and then The fact that |
Uh oh!
There was an error while loading. Please reload this page.
The original version of
:is()
,:-webkit-matches()
, allowed pseudo-elements in its argument list. So you could write.foo:-webkit-matches(*, ::before, ::after)
, and it would be equivalent to.foo, .foo::before, .foo::after
.We ended up removing this, because it conflicts with the Selectors data model - simple selectors, like pseudo-classes, only ever filter the current set of matched elements; only combinators can change the set to new elements (and pseudo-elements). The pseudo-element "selector" is a legacy syntax mistake from the early days of CSS, and today would have been instead done as a type of combinator (as discussed in #7346, which still might happen).
But this means that you can't easily write useful selectors like the above. You can write them with Nesting, but you can't then nest further:
.foo { *, &::before, &::after {...}}
is fine, but.foo, .bar::before { &:hover {...}}
won't match any hovered ::before pseudos, despite.bar::before:hover
being a valid and meaningful selectors, because it's treated identically to:is(.foo, .bar::before):hover
, which drops the.bar::before
arg from the:is()
.This isn't great! And it makes Nesting less useful for future cases of nested pseudo-elements, in addition to anything built on Nesting concepts, like Mixins probably will be.
I don't have an answer ready for this, I just needed to raise it for broader discussion and thought.
Some loosely-ordered thoughts:
:is()
is trying to replicate the functionality of lists, but pulling it into the syntactic space of simple selectors, thus the conflict. You can combine:is()
with other simple selectors, so the final subject of each selector inside of:is()
needs to be the same element.:hover:is(*, ::before)
would have to be equivalent to:hover, ::before:hover
, not:hover, :hover::before
..foo:is(::before, ::after)
, wanting to avoid repeating the preceding selector. And simple selector order can't matter;.foo:is()
and:is().foo
have to be identical. So that also prevents reasonable stuff like.foo:is(::before, ::after):hover
from working..foo(*, ::before, ::after):hover
.foo(
is a function token. I need the parens to be separate from other bits of the compound selector, but I also need to still be able to glom a compound selector before or after it..foo = (*, ::before, ::after) = :hover
.=
is a new combinator that doesn't change the set of matched elements at all; it's a no-op.(...)
is a new term in the<complex-selector-unit>
grammar, that takes a selector list and matches any of them. Since it's part of a complex selector and guaranteed to be surrounded by combinators, we avoid the issues from earlier..foo
elements, (2) without changing the set of matched elements, select*, ::before, ::after
elements (aka the element, plus their before/after pseudos), (3) without changing the set of matched elements, select:hover
elements..foo > (*, ::before)
selects all children of a.foo
element and their before pseudos.()
can be full complex selectors, just like in:is()
, so.foo = (.bar *, #baz > *)
selects.foo
elements that have a.bar
ancestor or a#baz
parent..foo { &, & > .bar {...}}
because the current match set (whatever the preceding combinator yielded) is always the subject of the selectors in the paren list. That's probably okay. We'd have to invent a new way to refer to the elements being matched if we wanted to expand that (not:scope
or&
, but a secret third way). I dunno, maybe we do need it.:is()
, to avoid the same sort of "which way did it match" exponential explosion.X { Y {...}}
desugars to(X) = (Y) {...}
, with the magic ability to use&
to explicitly refer to the current match elements. If I'm thinking about this correctly, this is a no-op change except that it allows::before, ::after { &:hover {...}}
to work, where today it doesn't.(X) = (Y)
is different - currently we do care about which way you matched the last bit. We'd probably just make that magic, then - I can't see a reasonable way to define()
behavior to get that back in general. Nesting would just have the special behavior that it does care about the branch you actually matched in the final bit.&
still doesn't matter in a compound selector, and it still "represents the elements matched by the parent selector", so::before, ::after { &:hover {...}}
and::before, ::after { :hover& {...}}
mean the exact same thing. (Both equivalent to::before:hover, ::after:hover {...}
.)The text was updated successfully, but these errors were encountered: