-
Notifications
You must be signed in to change notification settings - Fork 689
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-nesting] How to resolve nested CSS with pseudo elements in the parent #7433
Comments
I think you did the right thing reverting the change that tried to correct it for developers. I'm thinking wait/lean on devtools to help developers learn that : agree that this is a tough lesson in nesting, since the 1st lesson to be learned with i believe this is how the nested selectors can be rewritten. .anything {
&::before {}
@nest .something_else > &::before {
color: black
}
} this makes me think we could add a warning to the spec that any nesting inside a |
Adding a warning to the spec would be great! Anything that is invalid in At the moment this is implied but not clear beyond any possible confusion. It's true that with nesting specifically stylesheet authors need to re-learn the feature and this is a painful process :/ It was inspired by sass nesting but it is completely different from that. So stylesheet authors have an intuitive sense of how the feature should work that doesn't match how it will eventually work in browsers. We already took some steps with the last major version of the PostCSS plugin but clearly more work is needed :) A (style)lint rule that can spot invalid nesting would be awesome and doesn't exist yet. |
I am not sure I understand why |
As an author, I hope we don't drop nesting inside pseudo-elements just because it's not supported in :is() today. Nested conditional rules div::before {
color: blue;
@media (min-width: 480px) {
color: red;
}
} **Nested user action pseudo classes ** div::before {
color: blue;
&:hover {
color: red;
}
} Nested pseudo-elements article::fist-letter {
initial-letter: 3;
&::prefix {
font-size: 0.5em;
vertical-align: top;
}
} It's not just that we are used to the existing feature. Making nesting inside pseudo-elements invalid will block many use cases, and will make it harder to teach and understand nesting. The specificity side-effects of using :is() will be surprising enough as it is. I understand making it invalid would make the feature easier to implement through :is() at the moment. I hope we can prioritise authors over ease of implementation, and perhaps look at extending support for nested pseudo-elements in :is(). Improved syntax for selecting pseudo-element children and descendants as described in 7346 would be great too. |
The :is() desugaring, taken literally, just advice for tools, yeah. We don't actually produce an :is() internally. But the restrictions should exist in both places or neither. |
FWIW, while the implementation in Blink doesn't literally produce an :is() internally (it couldn't, due to CSSOM demands), it produces something that is awfully close. It goes through the exact same path while matching, for instance. (See https://chromium-review.googlesource.com/c/chromium/src/+/3934883/16/third_party/blink/renderer/core/css/selector_checker.cc#1315 if you're interested) |
TL;DR;
I was looking at this from a feature detection perspective and I think a couple of things intersect here.
Chrome 106:
Chrome 109:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
<style>
html {
& p { /* this is irrelevant, I just wanted to nest something */
color: green;
}
}
@supports selector(& p) {
p {
/* Version 109.0.5372.0 (Official Build) canary (arm64) */
/* Experimental platform features on */
text-decoration: underline solid green;
}
}
@supports selector(:is(::before)) {
p {
/* Version 106.0.5249.119 (Official Build) (arm64) */
text-decoration: underline wavy red;
}
}
@supports selector(:is(::before)) and selector(& p) {
p {
text-decoration: underline dotted purple;
}
}
</style>
</head>
<body>
<p>Hello!</p>
</body>
</html> |
Conceptually, there is no reason to disallow this: .anything::before {
.something_else > & {
color: black
}
} So it would be unfortunate if we need to for what is basically an implementation detail. Perhaps we can just change how selectors get rewritten? Or maybe we could allow pseudo-elements in |
Can you clarify? |
E.g. .a, .b::before {
.c > & {
color: black
}
} could be rewritten as: .c > .a,
.c > .b::before {
color: black
} |
Yes, but your comment hints at a larger change. An example where .parent .a, .other-parent .b::before {
.c > & {
color: black
}
} currently matches like : .c > :is(.parent .a), .c > :is(.other-parent .b::before) {
color: black
} Which is also why I agree with @tabatkins to keep these linked. |
"How selectors get rewritten" has only a few possibiltiies:
We're currently doing 1 (effectively). 2 is right out. 3 just raises the question of why we can't do that stuff in |
If I’m not mistaken, precompilers used to fully expand nested selectors. I’m not sure if it was a huge problem? If nested rules were fully expanded, we could rely on linters to warn authors of the expanded complexity. :is() can then be added selectively where needed. I think I might prefer this over nesting not working inside pseudo-elements and the unexpected specificity. This would also need linting to warn about invalid use. |
preprocessor tools come in roughly two categories :
Those that attempt to follow the specification had/have bugs and deviations. No tool that I know of ever fully expanded nested selectors as would be required to follow the specification.
I think everyone agrees that this should work (from an author perspective) and that it is more a matter of finding the right way and time to fix this. |
I don't agree this should be solved for css-nesting-1. I'm fine with some followup spec (be it css-nesting or css-selectors) making :is() more flexible (thus trickling down to nesting), as long as the edge cases are clearly worked out. |
My intention was to clarify that not fixing it now (in css-nesting-1) doesn't mean it won't ever be updated. I've updated my comment. |
Indeed, why? |
@LeaVerou I disagree: the reason is that
.something_else > .anything::before { color: black }
.anything::before { .something_else > & { color: black }} Just like these are different: .something_else > .anything > before { color: black }
.anything > before { .something_else > & { color: black }} |
@Loirooriol |
Strictly speaking is not a combinator but it kinda behaves like a combinator and probably should have been a combinator and #7346 resolved to work on actual combinators for accessing pseudo-elements. I don't get your rearranging argument, |
yup :
You cannot add spaces around it ( |
I didn't mean that kind of rearranging, but that with combinators you can do things like |
They did not, precisely because this is a huge problem. Sass uses some heuristics to guess what subset of the possible selectors to actually emit. I presume other langs do so as well (or just have a combinatorial explosion in their output sometimes).
Yup, that's my intent as well. |
@LeaVerou The difference is that And I think |
I’m sorry, my mental model was off. I didn’t see the full complexity. Adding an explanation of why Linting and devtools will help, and hopefully nesting in pseudo-elements / a more flexible :is() can be solved in a later spec 🤞🏼 |
Not that it matters much, but the explosion is worse than that, because the simple selectors can refer to the same element: |
Right, I allowed for that in my calculation - that's why it's 5-choose-2. Note that the expanded selector has to end with |
I now have an explicit restriction against & representing pseudo-elements in the spec, along with an issue noting that we'd like to fix it, but need to do so simultaneously for :is() since they rely on the same mechanisms. |
Thanks! |
I know this is an old conversation, but just FWIW:
Less essentially clamps the number of combinations you can have. Otherwise it's a terrible problem, and at least in Less threads / documentation, we warn authors that this is one of the dangers of over-nesting lists. Interestingly, I was going to open a suggestion in Less to, at some point, output |
Hang on, pseudo-elements are not valid in |
Because an element is never a pseudo-element (and you can't generically select a pseudo-element and then test its name in an That is, Ultimately the problem is that early CSS screwed up the pseudo-element syntax, making it look like a pseudo-class (literally spelled it |
This limitation is sure to confuse more and more authors, even developers of browser engines, see https://issues.chromium.org/issues/40278599 |
@yisibl can you please post this in 9492 which is the actual open issue for this? |
No problem. Done |
first reported here : csstools/postcss-plugins#510
relevant specification parts:
https://www.w3.org/TR/css-nesting-1/#nest-selector
https://www.w3.org/TR/selectors-4/#matches-pseudo
For this CSS :
&
should represent the matched element of the enclosing rule.Which in this case is
before
.This CSS :
.something_else > .anything::before
.something_else.anything::before
(or.something_else.anything :> before
)Also relevant :
:is()/:matches()
? #2284 (comment)The text was updated successfully, but these errors were encountered: