-
Notifications
You must be signed in to change notification settings - Fork 708
[selectors][css-conditional] Pseudo-class or combinator that is syntactic sugar for wrapping in an @rule
#11969
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
I think this doesn't make things better. Nesting is already quite compact. I don't see a reason to need or significantly gain enough with this given the loses in readability it causes when trying to quickly scan the code looking for content. |
@brunoais Making nesting more compact is not what this proposal is about at all, it's about reducing duplication (of dozens or hundreds of declarations in many cases…). To be fair, that was my bad; I assumed readers were familiar with the use cases, and forgot there are always new folks around. I just edited the OP to add an example that more clearly illustrates the problem in the first section, and ensure all examples include at least one duplicated block of declarations in the "becomes" code block so that someone skimming and skipping most of the examples can still see the duplication. |
I think this only makes sense for rules implementing the
Doing it this way implies restricting to nested group rules.
Do you mean that CSSOM would see the "rewritten" rules? Then you get into combinatorial explosions very easily.
Rather than
This makes me think that it should be a combinator (could even go with .small-foo,
.foo @media(width < 500px) @layer {
/* declarations */
} (If a combinator needs to be a delim token, then |
Some really great feedback @Loirooriol, thanks!
I think that would satisfy > 99% of use cases, so if it makes things easier, let's do that.
I think that’s probably fine. I don’t see
No strong opinion, fine to go with whichever option is easiest to implement. The former also makes debugging easier, as dev tools don’t need to catch up and design new UI for this, it just works (this is a low priority reason as we don't design syntax to aid debugging, but it's still a small benefit). I think using more than one
Ah good point.
Oooh that's a very good point! I really like this. Funny, I’m usually the one shouting "won't someone please think of combinators?" and I totally didn't think of them this time 😅 I was trying to avoid an actual .foo /at media(width < 500px)/ /at layer/ {
/* declarations */
} But obviously, the syntax you’re proposing is by far the most natural, so if the grammar can accommodate it, that would be fantastic. Perhaps @tabatkins can shed some light on this. The main requirement is that going forwards, future grouping rules should not require spec changes to be used in that way, but if we define it in the way you just described, that seems to be satisfied. 👍🏼 |
@rule
— any at-rule@rule
The CSS Working Group just discussed
The full IRC log of that discussion<TabAtkins> lea: a while back i posted a proposal for :media() pseudo-class<TabAtkins> lea: was well received, tho didn't get finished <TabAtkins> lea: since then i've been spotting a more general problem, not just MQs <TabAtkins> lea: combining filterering logic from other at-ruels with selectors <TabAtkins> lea: conditional rules in general are obvious example <astearns> (we just added media() to @import: https://github.com//issues/10972#issuecomment-2773298233) <TabAtkins> lea: but also @layer. use-case is you want to style regular elements by tag name, but give them lower priority by defualt, or offer a class name to apply them as well <TabAtkins> lea: or @scope, with one selector in the scope and another selector outside <TabAtkins> lea: @starting-style too, might want it to share some styles with a regular selector <TabAtkins> lea: so at this point I think we need a more general solution <TabAtkins> lea: i posted a proposal but am not married to it, just want to solve the problem <TabAtkins> lea: proposal is a pseudo-class that is generic enough, letting people specify any at-rule <TabAtkins> lea: it's sugar for nesting <TabAtkins> lea: in every example i gave, the "after" version includes duplicates <TabAtkins> lea: someone thought this was a compact way to do nesting; that's a benefit, sure, but not the point <TabAtkins> lea: right now if you have something applied via an at-rule and a selctor, duplication is all you can do. and someontimes that can be *dozens* of declarations <TabAtkins> lea: Oriol suggested it only makes sense for CSSGroupingRule <TabAtkins> lea: that drops @page, but that's not a major use-case <kizu> q+ <TabAtkins> lea: someone suggested the opposite, having an at-rule that allows combining with selector logic <oriol> q+ <TabAtkins> lea: that could work, but it's less flexible, selectors are better known <TabAtkins> TabAtkins: you also ccan't combine multiple at-rules that way <astearns> ack kizu <TabAtkins> kizu: i think it would be great to have something like that. the problem is quite common, for darkmode for example <TabAtkins> kizu: have a selector that applies it, and an at-rule that applies it by default <TabAtkins> kizu: this could potentially be done with block-level mixins, but it woudln't be a sconvenient as what's expressed in this issue for one-offs <lea> Dark mode is actually a good use case for combining the logic with `@scope`, if you have e.g. `.dark` and `.light` and `.invert` classes <emilio> q+ <TabAtkins> kizu: you could make a mixin that wraps any number of rules and use it in multiple spots <TabAtkins> kizu: but for one-offs, having this just live in the selector is convenient <astearns> ack oriol <TabAtkins> oriol: mentioning a point i raise din the issue <TabAtkins> oriol: this is equivalent to some desugaring, and we have options <fantasai> I have to drop soon, but I have some concerns about using a pseudo-class here. <TabAtkins> oriol: expand to the desugar, or say the behavior is equivalent <lea> q? <lea> q+ <TabAtkins> oriol: my concern with the first is it's easy to have combinatorial explosions <TabAtkins> oriol: with second option it seems more complex to implement <TabAtkins> oriol: so a bit tricky <fantasai> Because a pseudo-class is classifying an element, and these conditions aren't connected to an element but to a selector as a whole <TabAtkins> oriol: since the order of these things matter, i don't think a pseudo-class is right <TabAtkins> oriol: should be a combinator maybe, use the @ as a combinator <lea> qq+ <astearns> ack lea <Zakim> lea, you wanted to react to oriol <TabAtkins> lea: right, i forgot about the combinator idea, i do like that better. Tab will have to commont on whether it's possible grammar-wise <TabAtkins> TabAtkins: i'll have to think about it <astearns> ack emilio <TabAtkins> emilio: i'm curious about the ocmbinator idea. what does it combine with? <TabAtkins> emilio: at least for some of these reules it's quite complicated. @media and @supprots are fine, but @laer and @scope change the cascade order <TabAtkins> emilio: i'm just not sure how we'd define mixing multiple of these <TabAtkins> emilio: for each rule that provides a boolean answer, that's fine. for @scope and @container, it might make sense as long as we prevent the cascade-order side effects, otherwise i don't know how it works. how does multiple @scope work <TabAtkins> emilio: so i'd rather not start making selectors affect the cascade order other than via specificity <lea> q? <astearns> ack dbaron <ntim> Not super convinced about the cost/benefit ratio of the idea <TabAtkins> dbaron: another concern is if we want to do this, we shoudl figure out the perf characteristic we want <TabAtkins> dbaron: some filtering at-rules have an expectation that the filter happens at a particiular stage. when thye look like selectors, the normal expectation for selector matching is a different stage of the process; might happen later and many more times than as an at-rule <TabAtkins> dbaron: and that's tied into the question of how they're represented in the cssom, and if this expands the way oriol said <TabAtkins> q+ <astearns> ack lea <TabAtkins> lea: to emilio, @scope/layer/container ar emajor use-cases. <TabAtkins> lea: just solving for conditionals would be useful, but it doesn't reach the 80% case <TabAtkins> lea: @container is biggest from the non-conditional cases, then @scope then @layer <TabAtkins> lea: to dbaron, i think perf should be similar to the desugared veresion or better, since we're not duplciating declarations <TabAtkins> lea: to oriol, combinatorial is less here than regular nesting, less at-rules to combine with <emilio> q+ <TabAtkins> lea: so even if we go that way, it's not as bad, and we cna introduce a maximum limit <TabAtkins> lea: i think the number of at-rules in actual use is fine, two or three <TabAtkins> lea: thinking more abou tthe combinator idea, while i like the syntax, i don't like that it muddles what a combinator is <TabAtkins> lea: combinators get you from one element to another element <TabAtkins> lea: here we're not doing that, at-rule can be wrapping the whole thing, or something at the end, etc <TabAtkins> lea: not sure about this <TabAtkins> lea: another benefit of doing this in selector land, selectors have forgiving handling for selectors we don't understand in :is()/:where() <TabAtkins> lea: so authors could use this immediately and wrap in an :is() <TabAtkins> lea: while an at-rule that's misunderstood gets dropped entirely <TabAtkins> lea: last, i was thinking what if instead of a pseudoclass, could we @sheet andd import that internally? <TabAtkins> lea: could have the style rule and at-rule, then just import the @sheet inside of each <TabAtkins> lea: tim, you said not sure about cost-benefit. is it the cost of the syntax or the benefit of the results? <TabAtkins> ntim: cost of the syntax, mostly, and having devs understand the syntax <TabAtkins> ntim: i'm not convinced the current proposals are easy to understand <TabAtkins> ntim: so those combined vs the benefit from it... <TabAtkins> lea: fair. i htink if the syntax was as close to the at-rule as possible would help. but there might be value in two separate resolutions <TabAtkins> lea: (a) is this a problem to solve in some way, and (b) the specific syntax <TabAtkins> ntim: also, where the combinator/pseudo-class fits in the order... <TabAtkins> lea: that's explained in the proposal <TabAtkins> lea: the pseudo-class downside is the relative order of pseudo-classes mattered, which is new <lea> scribe+ <lea> TabAtkins: This is a problem worth solving. I had to do this duplication before, avoided doing somethings because of the duplication. I think the right place to solve it is on the declaration, neither the @rules or the selectors. <lea> Combining our existing rules because they work in many ways is not super feasible in an understandable way. Giving us a mechanism to easily dedupe chunks of declarations would be good. Mixins are the obvious way, or your idea of being able to reference an `@sheet` <kbabbitt> q+ to comment on the @sheet approach <lea> TabAtkins: It requires more work for one-off cases, but I think that's still justified for the overall improvement. Some way to do rule-level deduplication of contents <astearns> ack TabAtkins <TabAtkins> dbaron: i probably agree witih Tab <lea> q+ <emilio> q- <TabAtkins> dbaron: i think there are widely different solutions to the underlying problems <TabAtkins> dbaron: i'm very worried about the complexity of this particular set of solutions <TabAtkins> dbaron: two things. first is comment about perf <TabAtkins> dbaron: lea said shoudl be faster because less duplication <TabAtkins> dbaron: what i meant was, if you have @media or @support that doesn't match, those rules get thrown out earlier than doing selector matching <emilio> +1 to dbaron <emilio> (Was on the queue for that) <TabAtkins> dbaron: whereas if it looks like as elector, the syntax is saying "we'll try to match all of these", then we get to the top and see the :media(). we've spent all this time on selecto rmatching for rules that we could ahve avoided <TabAtkins> lea: doesn't that depend on how it's implemented? <oriol> This is my combinatorial explosion concern https://www.irccloud.com/pastebin/3PIUwxZd/ <TabAtkins> dbaron: yes, but if you assume it's going to be implemented by desugaring to some other syntax, you need to make sure that's practical to <TabAtkins> dbaron: so i don't think it's trivial to say "it'll be faster", you'd need a practical proposal for the underlying model <astearns> ack dbaron <TabAtkins> dbaron: and ahve a plan for what to do about it <TabAtkins> dbaron: second point, when you say "is it worth solving", i think we still need to udnerstand what cost <ntim> +1 to dbaron <TabAtkins> dbaron: when i look at this proposal, i ask how much complexity is this adding to selector matching. is it 20% more complex, or twice as complicated? <lea> q+ <TabAtkins> dbaron: i dont' know the answer off the top of my head, but I think when i look at a proposal like this, we're really asking the question "is it worth potentially doubling the complexity of selector matching to solve this", which i think is a real possibility <TabAtkins> dbaron: while I think Tab's mixin idea is much lower cost and does get the bulk of the use-cases <astearns> ack kbabbitt <Zakim> kbabbitt, you wanted to comment on the @sheet approach <TabAtkins> kbabbitt: wrt @sheet approach, we're very limited now in where we can put @import to reference the sheet <TabAtkins> kbabbitt: apart from that, i tend to agree with tab adn david that i worry about the complexity this is adding <TabAtkins> kbabbitt: i think it's worth exploring if we can solve this with mixins <astearns> ack lea <TabAtkins> lea: i agree with Tab as well. like I said in the beginning, i just want to solve the problem, not married to a solution <TabAtkins> lea: since i've seen a lot of use-cases, their characteristics is usually you don't have a ton of them, but th eones you have are big. wrapping an entire stylesheet, an entire theme <TabAtkins> lea: and you just want to wrap the whole thing with a selector *and* an at-rule <TabAtkins> lea: btw to dbaron, i said we *might* gain some perf from deduplication. unsure if it's a net positive or not. <TabAtkins> lea: but i might be convinced this can be done with mixins <TabAtkins> lea: so, if mixins are solving this, does it chagne the cost-benefit to drop some cases? like dropping @layer and only doing conditionals or @scope <astearns> q+ <TabAtkins> lea: is there a subset that we could carve out that makes it easier to solve with a pseudo-class? <lea> `:if()` even <TabAtkins> TabAtkins: [i wasn't minuted] <TabAtkins> lea: smaller places come out of here, like @supports <dbaron> (though on second thought, earlier filtering probably looks a bit like the bloom filter optimizations that we already have so it's probably not too hard) <TabAtkins> lea: some new CQs like :stuck <dbaron> (but many of the other issues remain) <kizu> q+ <astearns> (unminuted back and forth) <TabAtkins> astearns: i know mixins are still vague, but are there problems outlined in this issue that they're not a solution for? <astearns> ack astearns <lea> scribe+ <lea> TabAtkins: Mixins should be able to trivially address anything that can be nested in a style rule today <lea> TabAtkins: Any model we're thinking for mixins allows that. Conditionals work, scope works, container works. layer currently doesn't, but should be fine to amend it to do so <lea> TabAtkins: I think those are all the rule examples that have been given, so it should be fine <lea> q+ <TabAtkins> astearns: so everything in this issue should be input ot our mixins work <lea> TabAtkins: Yes, nested @layer is the only new use case <astearns> ack kizu <TabAtkins> kizu: to answer lea's q about if there's anything form what we were talkinga bout that would b euseful in a selector space, i can only really think of CQs <TabAtkins> kizu: with CQs you can have a named container, then choose a container for which you're running the query <TabAtkins> kizu: with selectors it could be useful as a shortcut to be able to indicate at which element you're running the querys <TabAtkins> kizu: right now to do this you ahve to write a seaprate selector creating a named container <TabAtkins> kizu: so kinda convenient in selectors. exception is probably not usable inside a :has() <astearns> ack lea <TabAtkins> lea: right now, proposals around mixins have been global. that works for the big-chunk use-cases, but less for the smaller use-cases <TabAtkins> lea: liek "i just want to add two delcarations if this is supported" <TabAtkins> lea: wonder if some small version of scoped mixins would be useful here <TabAtkins> lea: naming things globally is hard. small targeted mixins with local names could be more lightweight <lea> scribe+ <kizu> (https://github.com//issues/11798 — an issue about scoping named things) <lea> TabAtkins: The possibilities there are interesting. Mixins will get applied before the cascade, at sheet parsing layer. We could have a notion of lexical scope, that is I think a viable thing to explore. <lea> TabAtkins: No comment on whether we'll for that but it sound plausible <TabAtkins> astearns: so queue is empty. close this discussion for now? <TabAtkins> lea: yeah. fine to even close the issue. looks like we want to take this as input into mixins. <TabAtkins> lea: so i'm hearing we're sorta in agreement about solving this as long as cost is reasonable. doing it in selector isn't reasonable in cost/complexity. so we'll take it as input to ensure mxiins satisfy these <TabAtkins> TabAtkins: can I have lea open an issue on Mixins spec? <TabAtkins> lea: yeah, will tag in this issue <lea> ACTION: Lea to open issue with these use cases and ideas tagged mixins <lea> PROPOSED RESOLUTION: We agree the problem needs solving, but doing it selectors seems too complex. We'll instead close this and make sure mixins satisfy these use cases. <TabAtkins> astearns: objections? <TabAtkins> RESOLVED: We agree the problem needs solving, but doing it selectors seems too complex. We'll instead close this and make sure mixins satisfy these use cases. |
I was reminded of this by today’s resolution to remove
:host-context()
. While I think it was a good call, one issue is that style queries involve an@rule
, so there is no easy way to combine them with selectors without duplicating code.My older proposal for a
:media()
pseudo-class was fairly well received. However, since then, I have been stumbling on many more use cases that require combining selector logic with @-rule logic, beyond just@media
. Many examples were mentioned in that thread.I think all conditional @-rules would benefit from something like this, e.g.:
@media
@supports
@container
But also several grouping rules:
@starting-style
was mentioned in the thread above@layer
(see below for an example)@scope
, e.g. to combine donut scope (not possible via a selector) with regular selectors.@page
, to share styles between a printed page, and the rendering of the printed page on screenAs an example of the problem, consider this pattern I recently encountered.
Including a CSS theme file sets all its design tokens on
:root
using a layer, but also on a class name, which is unlayered:Proposal
At this point I'm convinced these use cases will keep creeping up, and rather than trying to tailor something to specific use cases, we should design a generic solution that will not need to be updated for every new grouping rule we add.
Possibly a generic pseudo-class (let's call it
:at()
for now, TBB) that operates merely as syntactic sugar and is completely agnostic to the rules it is specifying. Something like:It does not affect specificity. While it would make sense to make it increase specificity, this ensures that the result is equivalent to doing the transformation manually, so it can be polyfilled via preprocessors.
Why not do this with a preprocessor?
Doing this with a preprocessor involves duplicating a lot of CSS, which will need to be sent down the wire, while it's a relatively simple transformation for UAs to apply.
What about doing the opposite: introducing a
@selector()
rule?Selectors are generally more flexible, more composable, and more well understood.
Alternate names
The advantage of
:at()
is that it connects it directly to the@
syntax. However, it is also misleading, as it looks like a preposition.Other naming ideas:
:if()
: More clear, but could also restrict us down the line if we introduce a different type of conditional:atrule()
: More clear, but also verbose and awkwardExamples
becomes:
The position of the pseudo-class in the complex selector affects how the rewriting happens, essentially as an extension of nesting:
becomes:
If the compound selector consists entirely of
:at()
and is using a descendant selector, the@-rule
is terminal (i.e. the declarations are placed directly within it):becomes:
becomes:
becomes:
While the relative order of
:at()
pseudo-classes and other simple selectors in the same compound selector doesn't make a difference, the relative order of:at()
pseudo-classes if multiple are present is reflected in the nesting:becomes:
Non-descendant combinators are simply applied to the selector that remains after all the
:at()
have been removed:becomes:
Being purely syntactical, this allows nonsense CSS to be created:
becomes:
That’s okay. Simply writing CSS also allows nonsense CSS to be created 😀
The text was updated successfully, but these errors were encountered: