-
Notifications
You must be signed in to change notification settings - Fork 672
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
[selectors][css-nesting] Move nest-containing and nest-prefixed selector definitions to Selectors #5745
Comments
So |
I suppose each API could define what it means. It could default to |
Discussing with @LeaVerou, @jensimmons, @bradkemper, @tabatkins, and @mirisuzanne today, we all agree that making & have meaning outside of nested context is a good idea, as it makes selectors containing these portable between nested context, regular context, and The definition would basically be, within nested context it refers to the nesting selector, and otherwise it's an alias for
Open questions:
|
I think there's a missing context there. At the root level of the document, |
Yup, that last bulllet point seems to have been |
Edited. Thanks. :) |
We didn't discuss it today, but I think it would make sense to also clarify in .media {
/* elements matching `.media .h-card` act as a scope root */
@scope (.h-card) {
img { object-fit: cover; }
}
/* elements matching `.media` act as a scope root */
@scope (&) {
img { object-fit: cover; }
}
} |
I think this is too different and making them aliases would be confusing.
|
@mirisuzanne @fantasai @LeaVerou I also think this could be confusing for nesting contexts opened within scoped contexts, like in the example below: .media {
/* & === .media */
@scope (&) {
/* & === :scope */
img {
/* What would `&` be here— `img` or `:scope` ? */
}
}
} What would the below example be? Would I then be unable to reference |
I don't think that's too confusing if we're consistent about it. The meaning of |
@mirisuzanne What benefit is there to aliasing Purely looking for clarification here, as I don't think I understand how this enables developers. Thanks! |
The goal is to give the So this isn't really about a special-case 'alias' between two selectors - it's just that in this case, the selectors refer to the same element. The Already, at the root level As far as selectors are concerned, a scope is already just fancy nesting. :) |
@mirisuzanne If I understand that correctly it would mean that It makes sense to give both a well defined meaning everywhere. |
Right. They're different concepts that overlap in some cases. |
@mirisuzanne Thanks, Miriam! That explanation helped to clear things up for me. I greatly appreciate it! |
The CSS Working Group just discussed this issue, and agreed to the following:
The full IRC log of that discussion<fantasai> Topic:<fantasai> 5. [selectors][css-nesting] Move nest-containing and nest-prefixed selector definitions to Selectors <fantasai> github: <fantasai> 5. [selectors][css-nesting] Move nest-containing and nest-prefixed selector definitions to Selectors <fantasai> github: https://github.com//issues/5745#issuecomment-1271874448 <fantasai> TabAtkins: separate from discussion of which exactly nesting syntax <fantasai> TabAtkins: all of our proposals use the & <fantasai> TabAtkins: we have a few different contexts where we do nesting <fantasai> TabAtkins: and they don't currently allow & <fantasai> TabAtkins: right now assumption is that & only has meaning and possibly only valid in direct nesting <fantasai> TabAtkins: this is not great, particularly if use & > .foo <fantasai> TabAtkins: meaning of this is clear in any nestable context <fantasai> TabAtkins: so being able to copy-paste rule between different things, from nesting to @scope or querySelector <fantasai> TabAtkins: even globally, makes sense, just say parent context is :root <fantasai> TabAtkins: similarly in shadow DOM <fantasai> TabAtkins: so proposal is, to avoid authors being forced to edit selectors as they move nesting context <fantasai> TabAtkins: defined & to be valid and to have meaning in other context <fantasai> TabAtkins: if not defined specially, is equivalent to :scope <fantasai> TabAtkins: and this is already defined globally, top level it is host element of shadow stylesheet or :root otherwise <fantasai> TabAtkins: so make this analogous unless context explicitly defines it analogously <fantasai> florian: Seems reasonable, but haven't thought about it much <fantasai> Rossen_: I'm convinced, too <fantasai> Rossen_: Objections? <fantasai> ??: Gotten to comments about how used inside scope would be referencing, if possible to get up to another nested context <fantasai> ??: Getting confused to understand, anyone can describe clearly? <fantasai> ??: "What would the below example be? Would be unable to reference :scope in a nested context" <TabAtkins> (comment is https://github.com//issues/5745#issuecomment-1271646202) <dbaron> s/??/PaulG/ <florian> s/??/PaulG/ <fantasai> The answer is later in the thread, where the call was to not to change meaning of :scope <fantasai> s/??/PaulG/ <fantasai> TabAtkins: Doesn't change the meaning, & is always using the local definition of it <fantasai> TabAtkins: Question was if you put nested style rule under the img style rule, what would & refer to, it would refer to img <fantasai> TabAtkins: Direct nesting doesn't change :scope <fantasai> PaulG: Thanks <fantasai> Rossen_: Back to objections? <fantasai> RESOLVED: Accept to make & valid everywhere, maps to :scope where not otherwise defined |
Can we "link" this to If implementers ship this and nesting at different times we will have issues with Currently we use it as a way to detect support for nested CSS. maybe it is obvious that these need to ship at the same time |
/* 1 */
.foo {
:has(& + .bar) { /* "&" is ".foo" */
/* styles */
}
}
/* 2 */
.other :has(& + .bar) { /* "&" is ":scope" */
/* styles */
} First example is obvious I think, second is not. /* 1 */
:has(.foo + .bar) {
/* styles */
}
/* 2.a */
.other :has(.other + .bar) { /* "&" is scope element of ":has()" */
/* styles */
}
/* 2.b */
.other :has(:root + .bar) { /* "&" is scope element outside of the selector */
/* styles */
} I think That would be .other :has(& + .bar) {
/* styles */
}
.other :has(:root + .bar) {
/* styles */
} |
This is edited into the Nesting spec in 45d2efc We will move it to Selectors in the future (but at least it's defined for now). |
@romainmenke @fantasai With that change, how could one target a selector dependant on not having a specific selector before it? Some examples: .b:is(.a ~ &) { ... }
.b:is(.a &) { ... } Surely, the expectation out, like this, right?: .some-long-classname#and-an-id:is(.prev-sibling ~ .some-long-classname#and-an-id) { ... }
.some-long-classname#and-an-id:is(.ancestor .some-long-classname#and-an-id) { ... } Would the best alternative be to swap .b:is(.a ~ *) { ... }
.b:is(.a *) { ... } Something like this ☝🏼 seems like a good alternative. I just want to clarify whether this change affects any optimization benefits achieved by using Essentially…
|
Another consideration…
The below selector would not match .a:has(:is(& > .b)) Instead, it would match So how might someone target a selector like .a:has(:is(& > .b:unsupported-pseudo, .c)) { ... } ❌ (according to this new requirement) Without .a:has(:is(> .b:unsupported-pseudo, .c)) { ... } ❌ ☝🏼 This wouldn't produce the intended result, would it, since it has no reference to use for the parent? With this new requirement, it seemingly necessitates using nesting in order to reference the parent like this. What would a viable alternative be without having to spell the entire selector out again? .long-classname:has(:is(.long-classname > .b:unsupported-pseudo, .c)) { ... } ❌ This should work, I think, but is there a way to achieve this same result without using nesting?: .a {
&:has(:is(& > .b:unsupported-pseudo, .c)) { ... } ✅
} |
Apologies, but I'm not sure what you're referring to in either of these replies. |
@tabatkins No worries. My two questions, put more succinctly and clearly (I hope) are…
|
That is not the case. & refers to the elements matched by the parent rule's selector. If you use & in a non-nested rule, it'll be the same as :scope. It's never been given a special behavior in the There was discussion a while ago about having
Note that just wrapping the whole selector argument in :is() has always not been correct if the selector contains any combinators. That is, You can either wrap the (If we did have a way of opting |
Why not just
Be aware there is an important difference: And |
I don't think we can give .a {
.b:is(.c + &) { ... }
} We can't have The current proposal doesn't require two different meanings for |
@tabatkins @Loirooriol @mirisuzanne Incredible feedback. Thank you all so much for clearing up both of those questions! 🙌🏼 Wrapping only the unsupported selector or pseudo in the Thanks again! |
Nest-prefixed: https://drafts.csswg.org/css-nesting/#nest-prefixed
Nest-containing: https://drafts.csswg.org/css-nesting/#nest-containing
These types of relative selectors are useful beyond nesting, as the ampersand can become a universal CSS
this
equivalent. E.g. I can imagine a qSA-like JS API that allows us to do things likeinput.find("& + label")
or even compositions likefind("&.foo", "& > .bar", ".baz")
, or even HTML attributes that allow us to refer to elements relative to the current element (how useful that would have been withlabel[for]
!)Sure, there is also
:scope
, but there’s a reason we didn't base nesting off that.The text was updated successfully, but these errors were encountered: