You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[selectors] Let :is() have better error-recovery behavior than normal Selectors #3264
This isn't captured in the current spec, but I remember earlier speculation that we could use :matches() as a way to get around the bad Selectors behavior of "a syntax error in one complex selector invalidates the whole sequence" that we're stuck with. Do we still think this is a worthwhile idea to pursue?
Spec-wise, what this would mean is defining the official syntax as :matches( <any-value> ), then split the result on top-level comma tokens, then attempt to parse each item as a <complex-selector>, and just ignore any invalid ones. (If all of them are invalid, the selector matches nothing.)
Then, if you're concerned about using a newer feature, you can just write your selectors like:
:matches( .foo || .bar, td.foo:first-child ) {
/* styles for the bar cell */
An unfortunately, but relatively minor, tax for getting better error-recovery behavior. (If we do end up renaming it to :is(), it's even more minor.)
Shouldn't this belong to @supports rule?
Just to imagine how bloated stylesheet would get if you have to use new selectors extensively. While with @supports you can only test it once and that's it.
Yes it doesn't cover selector check right now, but with Custom Selectors from CSS Extensions this should be possible.
@CyberAP, the point is to avoid having to write the same rules twice (for new and all browsers).
For example, consider a menu that is by default transformed into a hamburger icon (or something), but should "uncollapse" if either hovered or has a link inside it focused via keyboard. For modern browsers, I can write
...but in browsers that don't support :focus-within this would not apply at all, and I would have to write this rule twice, for each selector separately, in order to make at least the :hover part work there.
However, with the new proposal, the following hypothetical example
would be able to work in browsers that already support :is() and these two pseudo-classes, but don't support :current() yet.
How can this be solved with @supports (even if extended to test for selectors)? For me, this looks like the opposite of what @supports does (combining the code for old and new browsers into one rule, rather than separating it into different rules). Would be happy to be proven wrong!
Reacted by Oriol Brufau, Stanislav Lashmanov, ExE Boss, Sammy Marsden and juner
The CSS Working Group just discussed Let :matches() have better error-recovery behavior than normal Selectors, and agreed to the following:
RESOLVED: Use media query style invalidation inside pseudo classes that accept selector lists
The full IRC log of that discussion
<dael> Topic: Let :matches() have better error-recovery behavior than normal Selectors
<dael> github: https://github.com//issues/3264
<dael> Rossen: Is TabAtkins on?
<dael> fantasai: I can take it
<dael> fantasai: When we have a list of selectors :foo, :bar
<dael> fantasai: Browser doesn't rec :foo so the entire style rule is thrown out
<dael> fantasai: That's selectors invalidation.
<dael> fantasai: Other style of invalidation is media queries-like where you throw out a section. So if you have foo, screen we recognize screen
<dael> fantasai: Can't do MQ like because it would break the web for selectors. TabAtkins pointed out we have ability to do it within the new selectors in L4
<dael> fantasai: Issue proposes we adopt MQ-like invalidation inside the pseudoclasses that take selectors.
<dael> fantasai: :foo || :bar, [known selector] we'd only do the part we recognize.
<dael> fantasai: Makes a lot os sense to me together with @supports rule we added. Gives authors much better tools. If they want more granular they can use something else
<dael> Rossen: Sounds reasonable.
<dael> Rossen: Opinions?
<dael> myles: Thoughts from people that teach this? Seems like could be confusing
<dael> Rossen: leaverou or rachelandrew ?
<dbaron> Were you suggesting doing different things for :matches() vs. :is() ?
<tantek> agreed with myles, it could be confusing. would definitely want webdev teacher feedback on this.
<dael> fantasai: dbaron this was from before we renamed. Title should be is. Applies to is, not, has, nth-child, and maybe current
<dael> dbaron: One worry is some have been around for a while and might have compat issues
<dael> fantasai: :not with commas isn't widely supported.
<dael> fantasai: Not with a single argument that's invalid makes the whole thing invalid
<dael> florian: I think not with a comma is supported in Safari and Viviostyle
<dael> fantasai: Not is trickier because it's a negation
<dael> leaverou: Trying to decide error recovery or syntax?
<dael> fantasai: Error recovery
<dael> leaverou: Sounds amazing thing to do. It might be a little confusing, but it's worth it. In talks I only use webkit version and have to mention verbally it's just webkit. As an author I would love it
<dael> emilio: Doesn't solve unknown pseudo elements issue, right?
<dael> fantasai: No, sep.
<dael> emilio: I think that's biggest issue authors want to solve
<dael> fantasai: There's a rule for the webkit
<dael> emilio: Want to style a video control for webkit and edge it's not stanard. That's the biggest source of duplication
<dael> fantasai: This would solve, put it in all one :is
<dael> emilio: Syntax allow psuedo elements inside?
<bradk> I’m still concerned about :not. Can we find out how much authors have :-webkit and commas inside not?
<dael> fantasai: This is work for pseudo classes. Elements is next on agenda
<florian> I support this
<dael> Rossen: bradk point about not and his concern, can we address that now?
<dael> Rossen: Concern is the :not and defining how much authors have commas inside a not?
<dael> florian: It's safari only
<dael> bradk: Safari on iphone is used a lot. :not has been without commas for a while, but it's used in mobile web a lot. That's my suggestion, I'd like actual data
<dael> florian: This is hard data to get. Need to find usage that would break the page if it started working in a different way. That's a judgement call
<dael> Rossen: Another way to ask is if any Apple folks have an issue with this. If they feel comfortable with compat risk we can resolve
<dael> bradk: That solution would be okay
<dael> smfr: I don't think we have enough [missed]
<dael> smfr: I don't thinkw e have enough information to know. When we impl :not we got compat issues b/c people using it in wrong ways
<dael> smfr: No feeling for how common comma use is to know if it's risky
<dael> Rossen: Would you be okay with current proposal in the absense of this information?
<dael> smfr: I think so
<dael> Rossen: Anything else before we try and resolve?
<bradk> No strong objection
<dael> fantasai: Use media query style invalidation inside psuedo classes that accept selector lists
<dael> Rossen: Objections to ^?
<dael> emilio: Would like behavior for :not clarified
<dael> fantasai: Makes sense
<dael> RESOLVED: Use media query style invalidation inside pseudo classes that accept selector lists
<dael> Rossen: fantasai and TabAtkins will have clarification in spec
emilio pointed out that we need to be careful about how we handle :not() here. It's not obvious it would work as intended if we just ignore unknown arguments, and also a single argument to :not() likely has Web-compat implications since it's already implemented.
A related question is :nth-child(); simply ignoring one of its arguments could throw off the count. There likely wouldn't be a Web-compat problem with it, though.
changed the title [-][selectors] Let :matches() have better error-recovery behavior than normal Selectors[/-][+][selectors] Let :is() have better error-recovery behavior than normal Selectors[/+]on Nov 21, 2018
The thing about :not() is that only the single‑argument form is supported in major browsers, so we could probably apply it to the multi‑argument form and be fine.
Also, at least for unknown pseudo‑classes, we could have those match nothing, so :not(:unsupported, :unsupported-2) would match everything because :not(:is(:unsupported, :unsupported-2)) would match everything, as :is(:unsupported, :unsupported-2) would match nothing.
I agree that we could minimize the Web-compat risk by saying that error recovery only applies if two or more selectors are supplied.
:nth-last-child(:nonsense) is invalid :nth-child(:nonsense, :enabled) is :nth-child(:enabled) :not(:nonsense, :gibberish) is * :is(:nonsense, :gibberish) is :not(*).
This would have the following benefits:
:not(.) and :nth-child(.) and :nth-last-child(.) with a single selector have the same semantics as in Selectors 3.
We have error recovery for :is(...) and :where(...) and :has(...)
:not(...) and :is(:not(...)) and :not(:is(...)) are always equivalent, same for :nth-child and :nth-last-child
As a minor variation, preserving the same benefits, we could say that error recovery only applies if at least one of the selectors is valid. Then
:not(:nonsense, :gibberish) is invalid.
This might be easier to teach, and avoid developers' confusion when they see an introduced :not(*) in a Developer Tool that hasn't preserved raw text.
As a minor variation, preserving the same benefits, we could say that error recovery only applies if at least one of the selectors is valid.
Then :not(:nonsense, :gibberish) is invalid.
That should probably only apply to :not(…), as :commonly-supported, :is(:rarely-supported) can be used to avoid invalidating the entire selector list if the :rarely-supported pseudo‑class isn’t supported by the current browser but :is(…) and :commonly-supported are supported by the current browser.
Also, it might be a good idea to still parse :not(:nonsense, :gibberish) as valid, but matching nothing.
I'm happy to implement this in Gecko if people define in the spec how to deal with the empty-selector behavior. Like, should :is(:nonsense) serialize just as :is()? Should that be special-cased to never match?
Also, it may be a bit confusing if people write stuff like :is(::before, ::after), and it gets turned to :is() instead of being rejected as invalid...
The CSS Working Group just discussed [selectors] Let :is() have better error-recovery behavior than normal Selectors.
The full IRC log of that discussion
<dael> Topic: [selectors] Let :is() have better error-recovery behavior than normal Selectors
<dael> github: https://github.com//issues/3264
<dael> ericwilligers: We resolved to have better error recovery but WK shipped without and FF is about to. Is it too late or do we still want error recovery?
<dael> TabAtkins: If possible I'd like to have it. If we have to oh well, but it's bad behavior without
<dael> ericwilligers: Who wants to do spec update?
<dael> ericwilligers: We resolved on issue but spec isn't updated
<dael> TabAtkins: I'll do it if we agree today to proceed
<dael> Rossen_: We're wanting to hold previous resolution that requires error recovery and make that edit into spec? Or are we trying to relax the error recovery and not proceed with edits?
<dael> ericwilligers: [missed]
<dael> ericwilligers: I'm propsing the first, that we go ahead and make edit.
<dael> Rossen_: You're proposing add it to spec and pester impl to do that
<dael> ericwilligers: Yes
<astearns> notes that Emilio says he's OK implementing it
<dael> Rossen_: Alright. Any thoughts on that? Are we still committed and have TabAtkins add edits?
<dael> TabAtkins: We'd need FF or WK dev to be okay doing change
<dael> Rossen_: emilio is signaling he's fine with change. Only WK is going to be required to update. Anyone from WK on?
<dael> smfr: If emilio is fine changing we're fine changing
<dael> Rossen_: We have a resolution to do error recovery. Do we need resolution to have TabAtkins edit it in?
<dael> Rossen_: We'll close no change but to put in the required edits
<dael> Rossen_: Thank you
https://bugzilla.mozilla.org/show_bug.cgi?id=1664718 is the Gecko bug for this (sorry for the lag, busy month!). I plan to enable it on Nightly and beta for now to collect potential breakage, but will enable after a couple releases everywhere if there's no issue.
Just verifying as I write the spec - in #3264 (comment)@ewilligers suggested that we handle "single selector, which is invalid" differently from the rest of the cases, since that has legacy implications, but it looks like we're no longer doing that?
That is, :is(:unknown) is valid but matches nothing, just like :is() and :is(:unknown1, :unknown2)?
Cool, then please make sure the spec edits linked above are satisfactory; they apply to :is(), :where(), and :has(), but not :not(), per resolutions earlier in the thread.
They look good. I'd probably note that there's an interesting side-effect of this behavior which I'm not sure it's great, which is that the specificity of :is(:unknown, div), will change when a browser starts supporting :unknown. I don't see a great way to avoid that though.
Yeah, unless I distinguish between "parsable, just using unknown selectors" and "lol wtf is this", I can't handle that.
And even then, a "parsable but unknown" selector might have complex specificity behavior, so we actually can't even predict that. So it's just something we'll have to accept.
Activity
CyberAP commentedon Oct 29, 2018
Shouldn't this belong to
@supportsrule?Just to imagine how bloated stylesheet would get if you have to use new selectors extensively. While with
@supportsyou can only test it once and that's it.Yes it doesn't cover selector check right now, but with Custom Selectors from CSS Extensions this should be possible.
SelenIT commentedon Oct 29, 2018
@CyberAP, the point is to avoid having to write the same rules twice (for new and all browsers).
For example, consider a menu that is by default
transformed into a hamburger icon (or something), but should "uncollapse" if either hovered or has a link inside it focused via keyboard. For modern browsers, I can write...but in browsers that don't support
:focus-withinthis would not apply at all, and I would have to write this rule twice, for each selector separately, in order to make at least the:hoverpart work there.However, with the new proposal, the following hypothetical example
would be able to work in browsers that already support
:is()and these two pseudo-classes, but don't support:current()yet.How can this be solved with
@supports(even if extended to test for selectors)? For me, this looks like the opposite of what@supportsdoes (combining the code for old and new browsers into one rule, rather than separating it into different rules). Would be happy to be proven wrong!CyberAP commentedon Oct 31, 2018
@SelenIT, thanks for a great explanation! That does make sense and actually is a really great feature I would like to have.
ExE-Boss commentedon Nov 2, 2018
You mean like #3082, except without the vendor prefix exemption?
tabatkins commentedon Nov 2, 2018
Yes, and safely; I'm pretty sure we can't do #3082.
css-meeting-bot commentedon Nov 21, 2018
The CSS Working Group just discussed
Let :matches() have better error-recovery behavior than normal Selectors, and agreed to the following:RESOLVED: Use media query style invalidation inside pseudo classes that accept selector listsThe full IRC log of that discussion
<dael> Topic: Let :matches() have better error-recovery behavior than normal Selectors<dael> github: https://github.com//issues/3264
<dael> Rossen: Is TabAtkins on?
<dael> fantasai: I can take it
<dael> fantasai: When we have a list of selectors :foo, :bar
<dael> fantasai: Browser doesn't rec :foo so the entire style rule is thrown out
<dael> fantasai: That's selectors invalidation.
<dael> fantasai: Other style of invalidation is media queries-like where you throw out a section. So if you have foo, screen we recognize screen
<dael> fantasai: Can't do MQ like because it would break the web for selectors. TabAtkins pointed out we have ability to do it within the new selectors in L4
<dael> fantasai: Issue proposes we adopt MQ-like invalidation inside the pseudoclasses that take selectors.
<dael> fantasai: :foo || :bar, [known selector] we'd only do the part we recognize.
<dael> fantasai: Makes a lot os sense to me together with @supports rule we added. Gives authors much better tools. If they want more granular they can use something else
<dael> Rossen: Sounds reasonable.
<dael> Rossen: Opinions?
<dael> myles: Thoughts from people that teach this? Seems like could be confusing
<dael> Rossen: leaverou or rachelandrew ?
<dbaron> Were you suggesting doing different things for :matches() vs. :is() ?
<tantek> agreed with myles, it could be confusing. would definitely want webdev teacher feedback on this.
<dael> fantasai: dbaron this was from before we renamed. Title should be is. Applies to is, not, has, nth-child, and maybe current
<dael> dbaron: One worry is some have been around for a while and might have compat issues
<dael> fantasai: :not with commas isn't widely supported.
<dael> fantasai: Not with a single argument that's invalid makes the whole thing invalid
<dael> florian: I think not with a comma is supported in Safari and Viviostyle
<dael> fantasai: Not is trickier because it's a negation
<dael> leaverou: Trying to decide error recovery or syntax?
<dael> fantasai: Error recovery
<dael> leaverou: Sounds amazing thing to do. It might be a little confusing, but it's worth it. In talks I only use webkit version and have to mention verbally it's just webkit. As an author I would love it
<dael> emilio: Doesn't solve unknown pseudo elements issue, right?
<dael> fantasai: No, sep.
<dael> emilio: I think that's biggest issue authors want to solve
<dael> fantasai: There's a rule for the webkit
<dael> emilio: Want to style a video control for webkit and edge it's not stanard. That's the biggest source of duplication
<dael> fantasai: This would solve, put it in all one :is
<dael> emilio: Syntax allow psuedo elements inside?
<bradk> I’m still concerned about :not. Can we find out how much authors have :-webkit and commas inside not?
<dael> fantasai: This is work for pseudo classes. Elements is next on agenda
<florian> I support this
<dael> Rossen: bradk point about not and his concern, can we address that now?
<dael> Rossen: Concern is the :not and defining how much authors have commas inside a not?
<dael> florian: It's safari only
<dael> bradk: Safari on iphone is used a lot. :not has been without commas for a while, but it's used in mobile web a lot. That's my suggestion, I'd like actual data
<dael> florian: This is hard data to get. Need to find usage that would break the page if it started working in a different way. That's a judgement call
<dael> Rossen: Another way to ask is if any Apple folks have an issue with this. If they feel comfortable with compat risk we can resolve
<dael> bradk: That solution would be okay
<dael> smfr: I don't think we have enough [missed]
<dael> smfr: I don't thinkw e have enough information to know. When we impl :not we got compat issues b/c people using it in wrong ways
<dael> smfr: No feeling for how common comma use is to know if it's risky
<dael> Rossen: Would you be okay with current proposal in the absense of this information?
<dael> smfr: I think so
<dael> Rossen: Anything else before we try and resolve?
<bradk> No strong objection
<dael> fantasai: Use media query style invalidation inside psuedo classes that accept selector lists
<dael> Rossen: Objections to ^?
<dael> emilio: Would like behavior for :not clarified
<dael> fantasai: Makes sense
<dael> RESOLVED: Use media query style invalidation inside pseudo classes that accept selector lists
<dael> Rossen: fantasai and TabAtkins will have clarification in spec
fantasai commentedon Nov 21, 2018
emilio pointed out that we need to be careful about how we handle
:not()here. It's not obvious it would work as intended if we just ignore unknown arguments, and also a single argument to:not()likely has Web-compat implications since it's already implemented.A related question is
:nth-child(); simply ignoring one of its arguments could throw off the count. There likely wouldn't be a Web-compat problem with it, though.[-][selectors] Let :matches() have better error-recovery behavior than normal Selectors[/-][+][selectors] Let :is() have better error-recovery behavior than normal Selectors[/+]ExE-Boss commentedon Nov 21, 2018
The thing about
:not()is that only the single‑argument form is supported in major browsers, so we could probably apply it to the multi‑argument form and be fine.Also, at least for unknown pseudo‑classes, we could have those match nothing, so
:not(:unsupported, :unsupported-2)would match everything because:not(:is(:unsupported, :unsupported-2))would match everything, as:is(:unsupported, :unsupported-2)would match nothing.ewilligers commentedon Nov 23, 2018
I agree that we could minimize the Web-compat risk by saying that error recovery only applies if two or more selectors are supplied.
:nth-last-child(:nonsense)is invalid:nth-child(:nonsense, :enabled)is:nth-child(:enabled):not(:nonsense, :gibberish)is*:is(:nonsense, :gibberish)is:not(*).This would have the following benefits:
:not(.)and:nth-child(.)and:nth-last-child(.)with a single selector have the same semantics as in Selectors 3.:is(...)and:where(...)and:has(...):not(...)and:is(:not(...))and:not(:is(...))are always equivalent, same for:nth-childand:nth-last-childAs a minor variation, preserving the same benefits, we could say that error recovery only applies if at least one of the selectors is valid. Then
:not(:nonsense, :gibberish)is invalid.This might be easier to teach, and avoid developers' confusion when they see an introduced
:not(*)in a Developer Tool that hasn't preserved raw text.ExE-Boss commentedon Nov 23, 2018
That should probably only apply to
:not(…), as:commonly-supported, :is(:rarely-supported)can be used to avoid invalidating the entire selector list if the:rarely-supportedpseudo‑class isn’t supported by the current browser but:is(…)and:commonly-supportedare supported by the current browser.Also, it might be a good idea to still parse
:not(:nonsense, :gibberish)as valid, but matching nothing.15 remaining items
emilio commentedon Aug 24, 2020
I'm happy to implement this in Gecko if people define in the spec how to deal with the empty-selector behavior. Like, should
:is(:nonsense)serialize just as:is()? Should that be special-cased to never match?Also, it may be a bit confusing if people write stuff like
:is(::before, ::after), and it gets turned to:is()instead of being rejected as invalid...emilio commentedon Aug 24, 2020
Then, of course
:is()should be parseable too...ewilligers commentedon Aug 25, 2020
Yes, I was going to serialize
:is(:nonsense)as:is()in dev tools, and treat:is()as a valid selector that never matches.Is serialization of selectors web exposed, i.e. testable in WPTs?
Loirooriol commentedon Aug 25, 2020
@ewilligers Sure, include
/css/support/parsing-testcommon.jsand thencss-meeting-bot commentedon Sep 2, 2020
The CSS Working Group just discussed
[selectors] Let :is() have better error-recovery behavior than normal Selectors.The full IRC log of that discussion
<dael> Topic: [selectors] Let :is() have better error-recovery behavior than normal Selectors<dael> github: https://github.com//issues/3264
<dael> ericwilligers: We resolved to have better error recovery but WK shipped without and FF is about to. Is it too late or do we still want error recovery?
<dael> TabAtkins: If possible I'd like to have it. If we have to oh well, but it's bad behavior without
<dael> ericwilligers: Who wants to do spec update?
<dael> ericwilligers: We resolved on issue but spec isn't updated
<dael> TabAtkins: I'll do it if we agree today to proceed
<dael> Rossen_: We're wanting to hold previous resolution that requires error recovery and make that edit into spec? Or are we trying to relax the error recovery and not proceed with edits?
<dael> ericwilligers: [missed]
<dael> ericwilligers: I'm propsing the first, that we go ahead and make edit.
<dael> Rossen_: You're proposing add it to spec and pester impl to do that
<dael> ericwilligers: Yes
<astearns> notes that Emilio says he's OK implementing it
<dael> Rossen_: Alright. Any thoughts on that? Are we still committed and have TabAtkins add edits?
<dael> TabAtkins: We'd need FF or WK dev to be okay doing change
<dael> Rossen_: emilio is signaling he's fine with change. Only WK is going to be required to update. Anyone from WK on?
<dael> smfr: If emilio is fine changing we're fine changing
<dael> Rossen_: We have a resolution to do error recovery. Do we need resolution to have TabAtkins edit it in?
<dael> Rossen_: We'll close no change but to put in the required edits
<dael> Rossen_: Thank you
emilio commentedon Sep 13, 2020
https://bugzilla.mozilla.org/show_bug.cgi?id=1664718 is the Gecko bug for this (sorry for the lag, busy month!). I plan to enable it on Nightly and beta for now to collect potential breakage, but will enable after a couple releases everywhere if there's no issue.
tabatkins commentedon Sep 14, 2020
Just verifying as I write the spec - in #3264 (comment) @ewilligers suggested that we handle "single selector, which is invalid" differently from the rest of the cases, since that has legacy implications, but it looks like we're no longer doing that?
That is,
:is(:unknown)is valid but matches nothing, just like:is()and:is(:unknown1, :unknown2)?[selectors-4] Specify forgiving-selector parsing, and use it in :is()…
emilio commentedon Sep 14, 2020
Yes, that's what I implemented, fwiw. I don't see why "single invalid selector" should be different.
tabatkins commentedon Sep 14, 2020
Cool, then please make sure the spec edits linked above are satisfactory; they apply to :is(), :where(), and :has(), but not :not(), per resolutions earlier in the thread.
emilio commentedon Sep 14, 2020
They look good. I'd probably note that there's an interesting side-effect of this behavior which I'm not sure it's great, which is that the specificity of
:is(:unknown, div), will change when a browser starts supporting:unknown. I don't see a great way to avoid that though.tabatkins commentedon Sep 15, 2020
Yeah, unless I distinguish between "parsable, just using unknown selectors" and "lol wtf is this", I can't handle that.
And even then, a "parsable but unknown" selector might have complex specificity behavior, so we actually can't even predict that. So it's just something we'll have to accept.
[selectors-4] :nth-child() and :nth-last-child() also use forgiving-s…