-
Notifications
You must be signed in to change notification settings - Fork 673
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] Functional pseudo-class like :matches() with 0 specificity #1170
Comments
cc @gregwhitworth who is a long-proponent of a similar solution. I was initially skeptical but then found some cases where this would be useful; all in all I would be in favor of such a change to :matches. FWIW one such example is that sometime you need to apply some form of css reset for an element to work properly (because it comes from another framework and doesn't like the fact you override flex-direction to column by default for all elements) so you want to unset flex-direction again inside that element but doing so means that the reset will override any selector targeting the content of the element if its selector is less specific than the selector you use to target the reset root. |
What's wrong with WebKit's implementation of |
I think about this nearly every single day. Agree this would be worth doing! |
(For those wondering where my comment went, I integrated it into the first post and then deleted it) |
I have a component with a lot of states, but I want to have a single class. I do this with data attributes now, but it increases specificity. Currently it's something like this (Suggest naming this :is)... .button {
...styles
&:is([data-size=small]) {
...styles
}
&:is(:focus) {
...styles
}
&:is([disabled]) {
...styles
}
&:is([data-selected]) {
...styles
}
} |
I'm convinced by this reasoning; in particular, that "div, but minus these three specific ones" should be capable of being overridden reasonably, instead of blowing up to a 3-ID specificity. Do we want to stick with this "something that makes its contents specificity-0", or try and generalize this to solve specificity hacks more generally? I'm not 100% sure how we'd do the latter in a future-friendly way (we've gone back and forth with precisely how many categories are in "specificity"), but I guess just sticking with the Big Three would be fine. (When we've tried to insert something else, it's for significant semantic differences, like scoped vs not, or inline vs not.) Hmm. If we simplify and say that it has to be a compound selector only, then the grammar's pretty easy; if you want more complexity, you just nest a (I suppose we could allow a full complex selector, as commas don't interfere with that, and just require :matches() if you want a selector list. But maybe good to keep it simple - it's just a modifier to a single selector. We let you do a full compound selector, rather than limiting to a single simple, to avoid verbosity in a common case - |
I'm in favor of a generalized approach. What about something that just turns off specificity at the stylesheet level, and for those stylesheets everything is order based? This would allow authors to easily control when they wanted specificity to work as an override and when they wanted to ignored it and treat it purely as a query. This would also help with resets and third-party stylesheets, and make it much easier for them to never clash with first-party styles. Maybe something like: <link nospecificity rel="stylesheet" hef="path/to/reset.css"> |
@tabatkins |
While something like this could be useful in some cases (as you mention, resets are a prime candidate), I'm not sure it should be the only way to override the specificity heuristic. Specificity can be a useful concept, it's just that the inferred specificity is often wrong. Also, you don't want any author rule to be able to override any rule in the third-party stylesheet. E.g. your library may have a |
Having something like that Though, I'm still thinking about (maybe alongside this I understand that that goes beyond the scope of this exact issue, but in my opinion this would cover some of the cases much better and with much less code from the authors (and I think it should be also really easily implementable, the main problem would be in coming up with syntax and handling all the nuances). Below I'd briefly write what I managed to come up with, if that sounds good and there is interest for something like that or at least for discussion, I could create a new issue for it maybe (for
Basically, that's it. There are a lot of nuances and ways to implement some of this stuff, but: the main idea is to have a simple way of separating big chunks of code into independent layers similar to how the cascade of origins works now, basically allowing authors to create multiple author origins (or — split the current author's origin into multiple layers, as all those styles should still be at its place in the cascade). And, yes, the idea is still raw, and if there is interest we can discuss it in a new issue I guess? And, anyway, I think there regardless should be a way to handle the specificity of selectors like with |
Ok, I'll try to find time on this weekend to write up what I have in more details :) And I'm glad to hear someone already had similar ideas! If there are any links to something like that (maybe in csswg mailing lists or elsewhere?), I would be happy to read it. |
As @FremyCompany mentioned, I've toyed with a few ideas in this space - primarily enforcing a specificity score at the stylesheet level. That said, there is the potential for more author confusion in this space as you're essentially introducing another layer of specificity (eg: on or off per origin/document/at-rule/stylesheet whatever is decided). The only reason I had opted for stylesheet level (I initially wanted to just opt for off/on for an entire document) is because with components you won't have the same control over whether the components you may be utilizing want/need to cascade and depend on specificity. I would discourage this one selector being the only selector that nullifies the specificity because that is even more confusing to the additional layer IMO. I think if we do decide this is worth addressing then we should really consider all of the use cases and the way to make this as intuitive to authors as possible. |
|
But... isn't that exactly what Actually, the more I think about it, the more |
@FremyCompany The nested Though, I agree that we can totally have |
@kizu I see. That's a creative use of |
I would prefer something that states very clear what the purpose is. Something like |
@dansajin I understand the need for explicitness but |
@FremyCompany and I spoke about this at length, walked through a few different scenarios and the design of I'm not saying I have the perfect solution here, but what about an at-rule that within any selector gets the same treatment as |
I was actually going to suggest that an at-rule might be good here too, but didn't want to get ahead of myself. Pretty nice for custom elements with stuff that exists in the light DOM I think, I've been in a bunch of scenarios where I wanted my default styles to have very low specificity but require attributes or classes to differentiate |
I think both are useful. Regarding the name, I like how |
This is a good proposition because it does what |
This would be magically useful!
The terseness of Although, considering:
Presuming there are reasons why I guess it would be more difficult to talk about without an explicit name… |
@bradkemper I’m really not following what exactly is the problem you’re seeing with nested |
FWIW, I'm not supportive of this proposal either. I was just pointing out that its technically expensive to do and therefore unlikely to get any implementer traction. Saying whether or not it has enough use cases is beyond my desire to debate this topic, so I just didn't comment on that (but I tend to agree I do not believe people want this behavior in the uses cases I've seen; in the cases where you want use this, you usually want to use source order and direct control). EDIT: Just to clarify, these previous comments do not apply to Lea's original idea. That one is easy to implement and both greg and myself have seen clear user cases and demands for such a feature. |
The problem is not with nested :is(). It is that having zero specificity inside the function just seems too drastic to me. The problem you described is that it it increases the specificity of the whole complex selector to such a degree that it makes it harder to override, or that it itself overrides things you don’t want it to. I think that problem can be overcome by just extending the existing specificity rules, so that the specificity inside the function doesn’t significantly influence selecting, except to break ties in specificity outside the function (just as class selectors don’t matter for specificity except to break ties in ID selectors). So, the old specificity rules is (roughly) that when there is a tie in ID selectors, you look at class selectors. If that is a tie, you look at type selectors. I would add to that, so that if type selectors are tied, then look at ID selectors inside the This allows you to write selectors that are as weak as you like compared to existing selectors, solving your originally stated problems. And inside the functions, you still get the great and important benefits of specificity-based cascading. This would allow authors to write rules that don’t override things they don’t want to override, and would allow library authors to write rules that were easy for others to override, but without submitting to the tyranny of rule order. And you could write many rules, with extremely low specificity, without worrying about something like PS for my scheme to work, the |
A lot of people just want to use specificity for the core of their matching, but source order for tiny variations on a selector, without having to refactor anything if they change some condition from "a class being present" to "a pair of attributes having certain values" as their apps evolve; your proposal would not help them achieve that. |
From what I understand of the current state of css usage, with websites moving towards component-splitting of their files instead of type-splitting for their files, stylesheets files used in development have grown smaller and smaller, and targetted to specific portions of the application; using source order makes more sense than using the specificity cascade at that scale. You still want specificity rules for more generic theming purposes, and to allow the style of one component to override its sub component' styles if needed, but "local state" of a component is something people don't want to have an impact on specificity, hence this proposal generating zero specificity. It doesn't require major changes to how browsers do cascade unlike your proposal, and solves the currently most dire issue. I haven't heard anyone desiring your proposed behavior before. I don't want to say it doesn't have value, but it doesn't seem like an appropriate design to solve the most frequently-heard-about problem. |
The second best thing about the zero-specificity matching (after its zero specificity itself) would be that it would work in the most straightforward and easily understandable (after you'll once get what it is doing) way. You won't need to do any calculations, as zero is the easiest stuff to operate. Having something inbetween would introduce an extra cognitive load: you'll suddenly need to look at the selectors in order to understand which exact specificity they have. And if selector would have multiple instances of this semi-specificity stuff? I already can see how the zero-specificity stuff would be useful (and maybe when we'd have them we wouldn't even need that much the proposal that I described above?) for the stuff I do in CSS, and I need it yesterday. While just thinking about how to manage the sublevels of specificity makes me a bit dizzy. |
Exactly what @kizu and @FremyCompany said. This kind of thing is needed yesterday, so complicating it further really doesn't help. If some specificity is desired, one can always make it so by using |
I want to emphasis this note above^. I'd rather deal with specificity than file/rule loading order. E.g.:
... and ...
Again: Would declarative-shadow DOMs help because they could reset styles and work around specificity issues on a component based level? |
For my use cases — no. I've already mentioned ITCSS — I'm using something similar to this, and saw other people using something similar to this. For our cases, having a better control over specificity means:
All of those cases (and there are more of them) don't have anything shadow DOM would help with. |
As for default styles, having a way to disable or lower specificity would really be great 👍 That covers your 1. and 2. IMHO (I am a user/contributor of Marx.css which is IMHO an opinionated normalize.css and having lower specificity will help doing what Marx/normalize do a lot easier). However do you agree that |
I'm more for |
@Stolzenhain , For the longest time, I've half-jokingly suggested to my coworkers that we need a complementary |
Something like this would be nice:
|
The Working Group just discussed
The full IRC log of that discussion<leaverou> topic: Functional pseudo-class like :matches() with 0 specificity<eae> leaverou: As we all know specificity tries to infer importance from selectors. Resulting specificity might not always be logical. <astearns> github: https://github.com//issues/1170 <Chris_> github, delete all comments <eae> leaverou: My favorite example of specificity not being a good heuristic is using the not pseudo class. <leaverou> div:not(#foo):not(#bar):not(#baz) <eae> leaverou: ^^^ <eae> leaverou: In this example we want to target all dives except three, yet gets very high specificity. <eae> leaverou: Especially for libraries, when trying to be specific, it is hard to override. <eae> leaverou: uses something called BAM(?) encodes selector in class name and uses js to apply it. <bradk_> BEM <TabAtkins> s/BAM/BEM/ <Chris_> .how__many__people__have__hear__of__bem <eae> leaverou: Willing to not use selectors at all, to bypass specificity. <bradk_> http://getbem.com/introduction/ <eae> leaverou: One way to fix this would be to define selectors with a known specificity of one, would allow last selector win and make it easier to override. <fantasai> q+ <eae> leaverou: I.e. put all nots in a single pseduo class. Easy solution vs other ones like per origin. <eae> leaverou: Lets one be very specific with regardess to specificty, can be selected per selector and solves most of the known problems. <eae> leaverou: Something that could be done as a pre-processor. <eae> leaverou: Many names have been proposed, we can bikeshed that. <eae> fantasai: Seems like a reasonable idea to me. The fact that authors can chose which parts of the selector applies to specificity sounds good to me. <bkardell_> ?+ <eae> fantasai: Lower priority for an entire style sheet, library author might want to allow the entire stylesheet to be allowed to be overriden by author rules. <Rossen> q? <eae> leaverou: Example: in ?? there is a toolbar that has a class of mv-bar, also class of mv-ui that means applpy default style. Want author to be able to overrule using just mv-bar but I don't want their "div" rules to override. <bradk_> q+ <astearns> s/??/mavo/ <Rossen> ack fantasai <Rossen> ack bkardell_ <eae> bkardell_: I don't think they are mutually exclusive. May be that you can do an awful lot for a whole bunch of things and we know we can do that easily. <eae> bkardell_: For a number of polyfills that I have worked on this would have been really useful. <eae> bkardell_: There are things like headings that need to take aria roles into account that make them have very high specificity which makes it hard to override. <TabAtkins> q+ <Rossen> ack bradk_ <eae> bradk_: Seems to me like we could solve this without taking specificity all the way down to zero. <eae> bradk_: Specificty only matters when there is a tie. <eae> bradk_: If there is a tie when it comes to ID or class, if the ID selector inside the funciton was more specific then outside the function that would solve the same problem while still allowoing specificity. <Rossen> q? <fantasai> Dael, here's the diagrams for Nat's presentation on line grids; please insert them in the correct section of the minutes. :) https://lists.w3.org/Archives/Public/www-archive/2017Nov/att-0008/CSSWG_2017.11.6_nmccully.pdf <eae> leaverou: As dicussed in issue, if we have fractions of specificity that introduces new levels of specificity and no one is suggestion that the entire thing doesn't have specicifity. All the classes and IDs outside of it still applies. It allows the specificity to be controlled. To have it count as a class you would just add it to the end, a bit hacky. <eae> bradk_: No use case for specifying it for the entire selector? <eae> leaverou: Yes but usaully not. You would only want to [put some criteria in the pseduo class. Like all the not for example. <eae> leaverou: Makes it less predictable. Now we know that we can count the number of IDs, number of classes, tags etc. If it is always zero it is very obiovus, if not it makes it much more complciated to compute it. <Rossen> q? <Rossen> ack TabAtkins <eae> TabAtkins: I agree in general with what leaverou is saying. Authors can often do just fine when specificity is in order. Opting out or in-order seems ot match what users want. <eae> TabAtkins: AS for chrome, we're happy with this, and would like to implement the stornger version of matches. This is easier, we might do this firts. <eae> TabAtkins: We're supportive. <Rossen> q? <eae> florian: Let's put this in level 4 for now, we don't have a five. <eae> fantasai: <eae> Rossen: Let's add it to four. <eae> fantasai: Very simple feature, as long as we're happy with the name. <eae> ericwillgers: What about web platform tests? <eae> fantasai: Not in CR yet, not needed. <eae> <laughter> <dbaron> q+ <eae> dbaron: One comment: I think it is worth nothing that it is not clear how easy it would be to implement matches. This has most of the same risk. <Rossen> ack dbaron <eae> dbaron: One of the things with selectors, they're heavily optimized. Implementing without optimizaions not very useful. With optimizations is quite a bit of work and not clear how quickly that can happen. <eae> fantasai: Start with implemented subset. <eae> TabAtkins: WebKit goes beyond spec, also has support for pseduo elements. <eae> dbaron: Caution in that it's being tied to a feature which has an uncertain future. <eae> fantasai: De-risk by having certain things in level 4 vs 5. <eae> dbaron: Other alternatives that solves leaverou use cases. <eae> leaverou: I don't want to reduce *all* of it to zero but only a part. <bkardell_> ?+ <Chris_> q? <eae> dbaron: I don't have a syntax proposal at this time but would avoid tie to branching combinators. <eae> TabAtkins: Earliest version didn't support that, does not. <eae> does now. <bkardell_> ?- <eae> fantasai: Making up a completely new unrelated syntax seems silly and doesn't make sense. Cut down on the syntax instead. <Rossen> q? <eae> dbaron: What doesn't make sense to me is spec moving so far ahead of what implementors are willing to do. Implementor by-in was not a factor. <eae> fantasai: Disagree, you're arguing against it based on implementation priority. <Chris_> s/by-in/buy-in <eae> dbaron: you're acting like we can only do everything or nothing. <fantasai> eae, that's not what I said <eae> fantasai: Not what I said. <fantasai> at all <eae> dbaron: I'm fine with the proposal but I do not think it's the only one. <eae> leaverou: If implementors aren't willing to do that at the moement shouldn't we discuss that? <eae> dbaron: I think I bought this up when we went to CR <eae> fantasai: Selectors are not in CR yet. <eae> dbaron: But people are goimg ahead to implement it. <leaverou> s/aren't willing to do that/aren't willing to implement a part of :matches()/ <eae> dbaron: This is the problem with sticking things in editors draft. <fantasai> There shouldn't be anything in Selectors that didn't have a WG resolution to add. <eae> Rossen: Would you prefer to see this go in in another way? If not can we try to settle on a resoluiton and go on. <fantasai> If there is, the editors made a mistake. <fantasai> If there was a resolution and you didn't like it you should have objected. <eae> dbaron: Fine with matches without combinators under a different name. <fantasai> Being angry about it now is neither helpful nor fair. <bkardell_> q+ <eae> leaverou: I'm fine with that (adding combinators later) <eae> TabAtkins: The no combinators version is the one that desugars effecitvely. Start with simple version. <eae> leaverou: Still supports commas, right? <Rossen> q? <eae> TabAtkins: Yes <eae> bkardell_: Is there a suggestion to take combinators out of matches level 4 as well? <eae> TabAtkins: Yes <eae> Rossen: Are you OK with that? <eae> leaverou: Yes, can we have a resolution? <dbaron> fwiw, we did discuss moving selectors4 to cr at least in https://lists.w3.org/Archives/Public/www-style/2015Jul/0349.html <eae> Proposed resolution: Add to selectors level 4 with a TBD name <eae> RESOLVED: Add to selectors level 4 with a TBD name. <eae> fantasai: Do we have a list of candidate names? <eae> leaverou: is, when, and filter <eae> Rossen: Any particular favorite we can resolve on right now? <eae> ericwilingers: I like filter <eae> Rossen: filter has the most plusses on github? <eae> leaverou: No, I think it was either is or when. <dbaron> filter is also the name of a property <eae> Rossen: You all work it out. <eae> Rossen: let's move on. |
For what it's worth (responding to my own comments in the minutes above), I might be wrong about which aspects of |
The issue raised at the end of the IRC discussion is important. Having a property and a selector named the same thing isn't ideal so I think |
Add :target-within to Changes section w3c/csswg-drafts@0fdb0f8ec4b9d6dcb2302715ee4ab 35013a1a000 Add :target-within, resolves w3c/csswg-drafts#457 w3c/csswg-drafts@d517bca980d01d13a1dc3765c0a3a 059c948402e Add :focus-within to Overview w3c/csswg-drafts@0933c5482c1ac25edf165e6c7b28c 66f1e561780 Add :is() example in specificity section, and add it in… w3c/csswg-drafts@a9fddaf185d9885bd657cd222956b ccc35486939 Add :is(), resolves w3c/csswg-drafts#1170 w3c/csswg-drafts@5948b1ce37463aa7f87fede040fac 9dd2c233329 Remove issue about :any-link naming, now that it's widely implemented. w3c/csswg-drafts@33ae0979aff2c9a87cbd1e0f15ce5 632b1b1eaf3
…tches() pseudo is not decided. #1170
I filed #2143 for further discussion on the name, as we haven't decided on it. I also marked this as an open issue in the draft and named the pseudo-class |
The problem
Sometimes, specificity is a blessing. However, more often than not, it gets in the way, and authors have to resort to hacks in order to artificially adjust specificity, such as repeating classes (
.foo.foo.foo
), turning id selectors to attribute selectors ([id=foo]
instead of#foo
), or adding pointless:not()
s (e.g..foo:not(.enough):not(.specificity)
).A common example where specificity goes against author intent is
:not()
. The default specificity rules on:not()
are almost never helpful. One often includes lots of filters, one after the other, but that increases specificity and makes these rules hard to override. E.g. good luck overridingdiv:not(#foo):not(#bar):not(#baz)
in a sane way, even though this selector is essentially as general asdiv
minus three specific divs.In the last few years, it has even become common to avoid selector logic altogether, just to avoid specificity woes. This is basically what conventions like BEM are all about: turning combinators and pseudo-classes into a single class selector so that every selector has the same specificity and the cascade only goes top to bottom. For example, authors would write
.form__input--disabled
instead ofform input[disabled]
.Specificity becomes an even bigger problem when authoring UI libraries, where you want your styles to be easy to override by the end author without them having to keep up with your stylesheet, but you still want to provide sensible defaults. The default Mavo stylesheet has many good examples of this. It's a stylesheet that's meant to be easy to override, and in practice, authors have to artificially increase specificity of their rules to override it because I had to be very conservative in what is styled so the selectors have to be very specific. However, being specific doesn't always mean they are high priority.
All in all, specificity is a heuristic. It's assuming that it can deduce rule priority from the selection logic, but the selector is not really expressing priority, it's expressing a query. Heuristics are very helpful when they are correct, but a PITA when they fail, so there always needs to be a way to override them. Especially in this case, specificity is a heuristic that fails so often that many authors have decided the benefit they get from selectors is not worth the specificity trouble and prefer to ditch selectors altogether and just use classes + JS for the querying logic!
Proposed solution
A new pseudo-class (name TBB, perhaps
:filter()
?) that works exactly like:matches()
but with zero specificity. That way, authors have control over the specificity of their rules, and can choose what is best for their use case.Or, even
:matches()
itself, since it has not yet been implemented, and changing its specificity would solve #1027 at once.Edited on Sep 15 to add
:filter()
and some more thoughtsThe text was updated successfully, but these errors were encountered: