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

[selectors4] Name the “functional pseudo-class like :matches() with 0 specificity” #2143

Open
fantasai opened this Issue Dec 28, 2017 · 98 comments

Comments

Projects
None yet
@fantasai
Contributor

fantasai commented Dec 28, 2017

In Issue #1170 we decided to add a functional pseudo-class that is exactly like :matches() but has zero specificity. However, we didn't decide on a name.

Suggestions in that thread included :is(), :when(), :filter(), and :nospecificity(). It was noted that having :filter() as a selector and filter s a property might be confusing, and that :nospecificity() is a pain to type. Additional suggestions, comments, and clarifications welcome.

@LeaVerou

This comment has been minimized.

Show comment
Hide comment
@LeaVerou

LeaVerou Dec 28, 2017

Contributor

I would vote for :is(). It's short, and it explains what it does. IIRC most people were in favor of :is() both in the thread and at the F2F. If you want an explicit resolution, we could add it to the agenda.

This is the twitter thread where I asked authors for ideas: https://twitter.com/LeaVerou/status/928426600558862337
As you will see, the results are not very useful.

Regarding the other names:

  • :when sounds time-related
  • :nospecificity is ridiculously long and prevents any possibility of expansion in the future to allow setting specificity, as @tabatkins suggested.
  • :filter is confusing because it sounds related to the filter property and the filter() function.
Contributor

LeaVerou commented Dec 28, 2017

I would vote for :is(). It's short, and it explains what it does. IIRC most people were in favor of :is() both in the thread and at the F2F. If you want an explicit resolution, we could add it to the agenda.

This is the twitter thread where I asked authors for ideas: https://twitter.com/LeaVerou/status/928426600558862337
As you will see, the results are not very useful.

Regarding the other names:

  • :when sounds time-related
  • :nospecificity is ridiculously long and prevents any possibility of expansion in the future to allow setting specificity, as @tabatkins suggested.
  • :filter is confusing because it sounds related to the filter property and the filter() function.
@SebastianZ

This comment has been minimized.

Show comment
Hide comment
@SebastianZ

SebastianZ Dec 29, 2017

Contributor

:nospecificity is ridiculously long and prevents any possibility of expansion in the future to allow setting specificity, as @tabatkins suggested.

At least the latter point could be avoided, of course, by using :specificity() instead. Nonetheless it's still very long and hard to write.

So, I also vote for :is(), even when it rather indicates a check and not something to set the specificity.

Sebastian

Contributor

SebastianZ commented Dec 29, 2017

:nospecificity is ridiculously long and prevents any possibility of expansion in the future to allow setting specificity, as @tabatkins suggested.

At least the latter point could be avoided, of course, by using :specificity() instead. Nonetheless it's still very long and hard to write.

So, I also vote for :is(), even when it rather indicates a check and not something to set the specificity.

Sebastian

@SelenIT

This comment has been minimized.

Show comment
Hide comment
@SelenIT

SelenIT Dec 29, 2017

Collaborator

I'm for :is(), as it :is() easy to memorize as kind of acronym for "Ignore Specificity" :)

Collaborator

SelenIT commented Dec 29, 2017

I'm for :is(), as it :is() easy to memorize as kind of acronym for "Ignore Specificity" :)

@Loirooriol

This comment has been minimized.

Show comment
Hide comment
@Loirooriol

Loirooriol Dec 29, 2017

Collaborator

Since #1170 was already resolved it may be too late for this, but shouldn't #1027 be resolved before adding a new functional pseudo-class?

It seems probable to me that the specificity of :matches needs to be fixed to avoid performance problems in some cases, and then :is could become redundant.

Collaborator

Loirooriol commented Dec 29, 2017

Since #1170 was already resolved it may be too late for this, but shouldn't #1027 be resolved before adding a new functional pseudo-class?

It seems probable to me that the specificity of :matches needs to be fixed to avoid performance problems in some cases, and then :is could become redundant.

@SelenIT

This comment has been minimized.

Show comment
Hide comment
@SelenIT

SelenIT Dec 30, 2017

Collaborator

I’m not sure that :matches() can, and needs to, be “fixed”. For me, the summary of #1027 is that :matches() doesn’t have any special performance problems per se (except it allows to write seemingly short selectors equivalent to terribly long selector lists, but this problem is not new, it often occurs with CSS preprocessors as well). Also, :matches() has been supported in Safari since 2015, and there is intent to implement it in Chrome, so changing it can break the interoperability. And, AFAIK, it wasn’t meant to affect specificity in any way, it’s sole purpose was to be syntactic sugar for shortening the lists of selectors with common parts for readability and reducing style bloating. The :is() selector, on the other hand, was introduced specifically to explicitly control the specificity, so I doubt that it could become redundant.

Collaborator

SelenIT commented Dec 30, 2017

I’m not sure that :matches() can, and needs to, be “fixed”. For me, the summary of #1027 is that :matches() doesn’t have any special performance problems per se (except it allows to write seemingly short selectors equivalent to terribly long selector lists, but this problem is not new, it often occurs with CSS preprocessors as well). Also, :matches() has been supported in Safari since 2015, and there is intent to implement it in Chrome, so changing it can break the interoperability. And, AFAIK, it wasn’t meant to affect specificity in any way, it’s sole purpose was to be syntactic sugar for shortening the lists of selectors with common parts for readability and reducing style bloating. The :is() selector, on the other hand, was introduced specifically to explicitly control the specificity, so I doubt that it could become redundant.

@Loirooriol

This comment has been minimized.

Show comment
Hide comment
@Loirooriol

Loirooriol Dec 30, 2017

Collaborator

Yes, just syntax sugar, but the problem is that if you just desugar it naively then you get an exponentially-long selector. Preprocessors don't matter, what matters is the CSS they produce, and it's acceptable for a CSS engine to take more time if the CSS is absurdly huge. But if the CSS is not huge, then it should be fast (e.g. that's why :has is not allowed in the dynamic profile).

If Safari implemented :matches, maybe there is some efficient algorithm. But I suspect they might not be calculating the specificity of :matches on the element that ensures a maximum specificity, which probably is what the spec should properly define. Sadly I have no Mac to test.

Collaborator

Loirooriol commented Dec 30, 2017

Yes, just syntax sugar, but the problem is that if you just desugar it naively then you get an exponentially-long selector. Preprocessors don't matter, what matters is the CSS they produce, and it's acceptable for a CSS engine to take more time if the CSS is absurdly huge. But if the CSS is not huge, then it should be fast (e.g. that's why :has is not allowed in the dynamic profile).

If Safari implemented :matches, maybe there is some efficient algorithm. But I suspect they might not be calculating the specificity of :matches on the element that ensures a maximum specificity, which probably is what the spec should properly define. Sadly I have no Mac to test.

@fantasai

This comment has been minimized.

Show comment
Hide comment
@fantasai

fantasai Jan 1, 2018

Contributor

My concern with :is() is that it will be logically seen as the inverse of :not(), even though the inverse of :not() is :matches(): the specificity behavior of :not() and :matches() are matched, and :not(:not(selector)) behaves as :matches(selector), not as :is(selector). But linguistically “is” and “not” seem to form a pair.

Contributor

fantasai commented Jan 1, 2018

My concern with :is() is that it will be logically seen as the inverse of :not(), even though the inverse of :not() is :matches(): the specificity behavior of :not() and :matches() are matched, and :not(:not(selector)) behaves as :matches(selector), not as :is(selector). But linguistically “is” and “not” seem to form a pair.

@Loirooriol

This comment has been minimized.

Show comment
Hide comment
@Loirooriol

Loirooriol Jan 1, 2018

Collaborator

What about swapping :is and :matches, then? So :is would be analogous to :not and :matches would have 0 specificity.

Collaborator

Loirooriol commented Jan 1, 2018

What about swapping :is and :matches, then? So :is would be analogous to :not and :matches would have 0 specificity.

@SelenIT

This comment has been minimized.

Show comment
Hide comment
@SelenIT

SelenIT Jan 2, 2018

Collaborator

Well, maybe then we can avoid the confusion if we could use the pattern proposed in #1170 by @tabatkins and add an extra optional argument to the existing :matches() instead of duplicating its functionality in a new pseudo-class at all?

So while :matches(...) would behave as it currently does, for example, :matches(... as 0,0,0) would have the behavior of the proposed :is() (zero-specificity matching), and :matches(... as 0,1,0) would match any of its arguments with the specificity of the single class. It would be backwards compatible with the existing :matches() implementations and wouldn't introduce any ambiguity in what is the inverse of :not(). Moreover, the similar extra argument could be added to :not() as well, making the behavior of :not() and :matches() symmetric.

Or is it too late to make such changes in Level 4?

Collaborator

SelenIT commented Jan 2, 2018

Well, maybe then we can avoid the confusion if we could use the pattern proposed in #1170 by @tabatkins and add an extra optional argument to the existing :matches() instead of duplicating its functionality in a new pseudo-class at all?

So while :matches(...) would behave as it currently does, for example, :matches(... as 0,0,0) would have the behavior of the proposed :is() (zero-specificity matching), and :matches(... as 0,1,0) would match any of its arguments with the specificity of the single class. It would be backwards compatible with the existing :matches() implementations and wouldn't introduce any ambiguity in what is the inverse of :not(). Moreover, the similar extra argument could be added to :not() as well, making the behavior of :not() and :matches() symmetric.

Or is it too late to make such changes in Level 4?

@LeaVerou

This comment has been minimized.

Show comment
Hide comment
@LeaVerou

LeaVerou Jan 2, 2018

Contributor

Yup, we could rename :matches() to :is() and introduce an extra argument, which could only be 0 at first, and later expand to the full specificity triplet.
I.e. div:is(#foo, as 0).

Contributor

LeaVerou commented Jan 2, 2018

Yup, we could rename :matches() to :is() and introduce an extra argument, which could only be 0 at first, and later expand to the full specificity triplet.
I.e. div:is(#foo, as 0).

@SelenIT

This comment has been minimized.

Show comment
Hide comment
@SelenIT

SelenIT Jan 2, 2018

Collaborator

Wouldn’t renaming :matches() require making it an alias for :is()? Or will it make the existing WebKit’s shipped implementation (and probably the Blink’s implementation being currently developed) non-conforming?

Collaborator

SelenIT commented Jan 2, 2018

Wouldn’t renaming :matches() require making it an alias for :is()? Or will it make the existing WebKit’s shipped implementation (and probably the Blink’s implementation being currently developed) non-conforming?

@tabatkins

This comment has been minimized.

Show comment
Hide comment
@tabatkins

tabatkins Jan 2, 2018

Member

We're not renaming :matches(); it's already shipped in Safari and I think Firefox and Chrome are soon to ship?

I'm still okay with just adding the functionality to :matches(), with the default being the current "specificity of the most specific branch that matched". If we do make a separate function, I'm still happy with :is(); the possibility for confusion is there, but oh well.

Member

tabatkins commented Jan 2, 2018

We're not renaming :matches(); it's already shipped in Safari and I think Firefox and Chrome are soon to ship?

I'm still okay with just adding the functionality to :matches(), with the default being the current "specificity of the most specific branch that matched". If we do make a separate function, I'm still happy with :is(); the possibility for confusion is there, but oh well.

@inoas

This comment has been minimized.

Show comment
Hide comment
@inoas

inoas Jan 3, 2018

So since when has short-lived vendor implementation ruled what happens to a standard?

I don't get it what's wrong, this is not yet another piece of software that you (or someone else at big-vendor-x) can throw away in some years and replace it with something else entirely and somehow I got the feeling that that's the recent predominant mind set. /rant :-(

Without much details to rephrase what @fantasai says:

  • If :not() and :matches() are the inverse of each other they should have inversed names for easy understanding and usage.
  • If :is() and :not() have inversed semantic meaning, they should behave like that and not unexpectedly do something else.

Logical options that make sure developers in future are not the hell confused about it (and TWBS-div-soup-hell shows most, are):

  • A) Rename matches() to is() - use matches() for the new 0-specificity feature discussed here.
  • B) Alias is() and matches() and find a new name for the feature proposed here instead of matches().

So why don't we collect alternate names for the current matches()-proposal and if we can find something really reasonable we can alias is() and matches() and deprecate matches()? What's so wrong about that?

inoas commented Jan 3, 2018

So since when has short-lived vendor implementation ruled what happens to a standard?

I don't get it what's wrong, this is not yet another piece of software that you (or someone else at big-vendor-x) can throw away in some years and replace it with something else entirely and somehow I got the feeling that that's the recent predominant mind set. /rant :-(

Without much details to rephrase what @fantasai says:

  • If :not() and :matches() are the inverse of each other they should have inversed names for easy understanding and usage.
  • If :is() and :not() have inversed semantic meaning, they should behave like that and not unexpectedly do something else.

Logical options that make sure developers in future are not the hell confused about it (and TWBS-div-soup-hell shows most, are):

  • A) Rename matches() to is() - use matches() for the new 0-specificity feature discussed here.
  • B) Alias is() and matches() and find a new name for the feature proposed here instead of matches().

So why don't we collect alternate names for the current matches()-proposal and if we can find something really reasonable we can alias is() and matches() and deprecate matches()? What's so wrong about that?

@inoas

This comment has been minimized.

Show comment
Hide comment
@inoas

inoas Jan 3, 2018

If my last comment is true, that would leave us with:

  • :nospecificity() - Agreed that it is hard to type, long, wants me to write :no-specificity() instead, wants me to add typos to "specifity" by accident and leaves no option to add specificity features later on. Pretty much worse than :is(), IMHO.
  • :filter() - I rather much would have the same word in different contexts doing different things (as expected), than logical pairs of words doing something else (:is() vs :not()).
  • :when() - I don't think the interpretation as only "time" related is a problem @LeaVerou? LESS, Elixir and PostgreSQL use it for logical conditions just fine, so why is ":when sounds time-related" a problem?

So these are all sane options without creating unexpected developer pain:

  1. Use matches() for the new implementation and rename current matches() implementation in the wild to is() - Issue: Vendor Safari has to adapt.
  2. Use when() for the new implementation and alias current matches() to is() and deprecate matches() - Issue: None?
  3. Use filter() for the new implementation and alias current matches() to is() and deprecate matches() - Small Issue: re-use of the same word in different context means it does different things.

Edit, here are some more from @LeaVerou's Twitter post.

  • :extend()
  • :decorate()
  • :augment()
  • :if()
  • :catch(), and I'd like to add:
  • :apply()
  • :case()

inoas commented Jan 3, 2018

If my last comment is true, that would leave us with:

  • :nospecificity() - Agreed that it is hard to type, long, wants me to write :no-specificity() instead, wants me to add typos to "specifity" by accident and leaves no option to add specificity features later on. Pretty much worse than :is(), IMHO.
  • :filter() - I rather much would have the same word in different contexts doing different things (as expected), than logical pairs of words doing something else (:is() vs :not()).
  • :when() - I don't think the interpretation as only "time" related is a problem @LeaVerou? LESS, Elixir and PostgreSQL use it for logical conditions just fine, so why is ":when sounds time-related" a problem?

So these are all sane options without creating unexpected developer pain:

  1. Use matches() for the new implementation and rename current matches() implementation in the wild to is() - Issue: Vendor Safari has to adapt.
  2. Use when() for the new implementation and alias current matches() to is() and deprecate matches() - Issue: None?
  3. Use filter() for the new implementation and alias current matches() to is() and deprecate matches() - Small Issue: re-use of the same word in different context means it does different things.

Edit, here are some more from @LeaVerou's Twitter post.

  • :extend()
  • :decorate()
  • :augment()
  • :if()
  • :catch(), and I'd like to add:
  • :apply()
  • :case()
@tabatkins

This comment has been minimized.

Show comment
Hide comment
@tabatkins

tabatkins Jan 3, 2018

Member

Aliasing :matches() doesn't do much of value; the name as it stands is fine, so it's not worth adding yet another name for the same functionality. That just makes it harder to teach and understand.

Renaming :matches() is rude to the implementors at this point, and to the authors who've already learned it. We've had the function for a long time, implementors have begun implementing with it. If the name was truly bad or misleading we could probably rename it, but it's not - at worst, it's slightly annoying that :is() appears to be a slightly better antonym to :not() than :matches() is, so people new to the feature might get confused, but it's quick to learn which is which. At this point in the feature's maturity, I don't think this justifies making a name change.

Member

tabatkins commented Jan 3, 2018

Aliasing :matches() doesn't do much of value; the name as it stands is fine, so it's not worth adding yet another name for the same functionality. That just makes it harder to teach and understand.

Renaming :matches() is rude to the implementors at this point, and to the authors who've already learned it. We've had the function for a long time, implementors have begun implementing with it. If the name was truly bad or misleading we could probably rename it, but it's not - at worst, it's slightly annoying that :is() appears to be a slightly better antonym to :not() than :matches() is, so people new to the feature might get confused, but it's quick to learn which is which. At this point in the feature's maturity, I don't think this justifies making a name change.

@SelenIT

This comment has been minimized.

Show comment
Hide comment
@SelenIT

SelenIT Jan 3, 2018

Collaborator

@inoas, AFAIK, :matches() is not just "short-lived vendor implementation". It has been in the standard draft for years (although the definition has changed a little), and its shipped implementation, without any flag/prefix, has existsed since mid-2015 (deprecating the old vendor-specific :-webkit-any() implementation), and there is intent to implement it in Blink (1, 2). So there should be some really solid reasons to rename/alias it (IMO).

Hovewer, I agree with Tab that the potential confusion in meaning between :matches() and :is() is not a very important issue. So I'm also fine with the both options — either introducing :is() as a functional analog for :matches() that ignores specificity (as currently specified), or "overloading" :matches() with an optional extra argument, without adding new pseudo classes at all.

Collaborator

SelenIT commented Jan 3, 2018

@inoas, AFAIK, :matches() is not just "short-lived vendor implementation". It has been in the standard draft for years (although the definition has changed a little), and its shipped implementation, without any flag/prefix, has existsed since mid-2015 (deprecating the old vendor-specific :-webkit-any() implementation), and there is intent to implement it in Blink (1, 2). So there should be some really solid reasons to rename/alias it (IMO).

Hovewer, I agree with Tab that the potential confusion in meaning between :matches() and :is() is not a very important issue. So I'm also fine with the both options — either introducing :is() as a functional analog for :matches() that ignores specificity (as currently specified), or "overloading" :matches() with an optional extra argument, without adding new pseudo classes at all.

@LeaVerou

This comment has been minimized.

Show comment
Hide comment
@LeaVerou

LeaVerou Jan 3, 2018

Contributor

In the past we would keep bad things only to avoid breaking web compat. Now we're keeping bad things to not be "rude" to implementors? What fresh hell is this?! 😛

:matches() has shipped in one browser and is not used by anyone. Renaming things is typically easy for implementors, and since nobody uses it (outside of postCSS) it doesn't break web compat.

What troubles me more is that I expect :is() to be used many more times in a selector and the lengthier :matches() would end up messing with readability. :matches() is only used for > 1 alternatives, whereas :is() is useful for singular things too. I think a name for :is() that's longer than 2-3 characters would be a pain.

Contributor

LeaVerou commented Jan 3, 2018

In the past we would keep bad things only to avoid breaking web compat. Now we're keeping bad things to not be "rude" to implementors? What fresh hell is this?! 😛

:matches() has shipped in one browser and is not used by anyone. Renaming things is typically easy for implementors, and since nobody uses it (outside of postCSS) it doesn't break web compat.

What troubles me more is that I expect :is() to be used many more times in a selector and the lengthier :matches() would end up messing with readability. :matches() is only used for > 1 alternatives, whereas :is() is useful for singular things too. I think a name for :is() that's longer than 2-3 characters would be a pain.

@fantasai

This comment has been minimized.

Show comment
Hide comment
@fantasai

fantasai Jan 3, 2018

Contributor

@LeaVerou I will note that most of your examples are chains of :not() which in Level 4 could be expressed within a single :not(), which won't explode the specificity in the same way. E.g. :not(.A):not(.B):not(.C) (specificity=0,3,0) can be written as :not(.A, .B, .C) (specificity=0,1,0).

Contributor

fantasai commented Jan 3, 2018

@LeaVerou I will note that most of your examples are chains of :not() which in Level 4 could be expressed within a single :not(), which won't explode the specificity in the same way. E.g. :not(.A):not(.B):not(.C) (specificity=0,3,0) can be written as :not(.A, .B, .C) (specificity=0,1,0).

@tabatkins

This comment has been minimized.

Show comment
Hide comment
@tabatkins

tabatkins Jan 3, 2018

Member

In the past we would keep bad things only to avoid breaking web compat. Now we're keeping bad things to not be "rude" to implementors? What fresh hell is this?! 😛

It's not a "bad" thing. It's a perfectly serviceable and appropriate name that people were happy with for multiple years; two people in this thread suddenly expressing a preference for a slightly different name does not make the original name bad.

Changing things that have shipped, are about to be shipped, that have tutorials written about them, etc., is expensive. It's one more thing a browser implementor has to do (even if it's "trivial", it's still a chunk of time out of someone's day to update the parser, the tests, maybe the DevTools support, and that chunk could have been spent doing something more valuable), and it means a lot of tutorial content in the wild is invalid and needs to be updated, or is just confusing in the future. In other words, renaming is not free, and the longer the name lives out in the wild, the more expensive it becomes. This doesn't mean we can't do it, but it does mean we need a decent reason to do so, and "some people suddenly decided they like this alternate name slightly better" is not a very good reason, or at least not a good enough one to override the costs in this case, in my opinion.

Member

tabatkins commented Jan 3, 2018

In the past we would keep bad things only to avoid breaking web compat. Now we're keeping bad things to not be "rude" to implementors? What fresh hell is this?! 😛

It's not a "bad" thing. It's a perfectly serviceable and appropriate name that people were happy with for multiple years; two people in this thread suddenly expressing a preference for a slightly different name does not make the original name bad.

Changing things that have shipped, are about to be shipped, that have tutorials written about them, etc., is expensive. It's one more thing a browser implementor has to do (even if it's "trivial", it's still a chunk of time out of someone's day to update the parser, the tests, maybe the DevTools support, and that chunk could have been spent doing something more valuable), and it means a lot of tutorial content in the wild is invalid and needs to be updated, or is just confusing in the future. In other words, renaming is not free, and the longer the name lives out in the wild, the more expensive it becomes. This doesn't mean we can't do it, but it does mean we need a decent reason to do so, and "some people suddenly decided they like this alternate name slightly better" is not a very good reason, or at least not a good enough one to override the costs in this case, in my opinion.

@inoas

This comment has been minimized.

Show comment
Hide comment
@inoas

inoas Jan 3, 2018

Well @tabatkins it is not like the only option is renaming aliasing and adding is(), actually when using when() here for this proposal there is the option (not) to later on alias is() with matching() and/or make it a separate stand-alone issue as it is not strictly related then, anymore.

Using when() would also circumvent the issue of semantic (but proposed non-functional) pairing of is() vs not().

Would that be viable?

@LeaVerou maybe Brent Spiner knows.

inoas commented Jan 3, 2018

Well @tabatkins it is not like the only option is renaming aliasing and adding is(), actually when using when() here for this proposal there is the option (not) to later on alias is() with matching() and/or make it a separate stand-alone issue as it is not strictly related then, anymore.

Using when() would also circumvent the issue of semantic (but proposed non-functional) pairing of is() vs not().

Would that be viable?

@LeaVerou maybe Brent Spiner knows.

@LeaVerou

This comment has been minimized.

Show comment
Hide comment
@LeaVerou

LeaVerou Jan 3, 2018

Contributor

@LeaVerou I will note that most of your examples are chains of :not() which in Level 4 could be expressed within a single :not(), which won't explode the specificity in the same way. E.g. :not(.A):not(.B):not(.C) (specificity=0,3,0) can be written as :not(.A, .B, .C) (specificity=0,1,0).

Just because it's easier to demonstrate the problem with :not() because typically removing something very specific from a large set still leaves you with a large set, so its specificity is almost always not what an author wants. Note that div:not(#foo, #bar, #baz) has the same problem, just less specificity. It's still pretty hard to override even just one id selector.

However, the problem :is() is trying to solve extends way beyond :not(). I could go through my stylesheets and compile a list of use cases that have specificity problems and don't include :not(). Thankfully, we have a resolution on this, so my time can be spent more productively.
Can we please focus on the name?

I think @SelenIT made an excellent point about :is() being an acronym for Ignore Specificity.

Contributor

LeaVerou commented Jan 3, 2018

@LeaVerou I will note that most of your examples are chains of :not() which in Level 4 could be expressed within a single :not(), which won't explode the specificity in the same way. E.g. :not(.A):not(.B):not(.C) (specificity=0,3,0) can be written as :not(.A, .B, .C) (specificity=0,1,0).

Just because it's easier to demonstrate the problem with :not() because typically removing something very specific from a large set still leaves you with a large set, so its specificity is almost always not what an author wants. Note that div:not(#foo, #bar, #baz) has the same problem, just less specificity. It's still pretty hard to override even just one id selector.

However, the problem :is() is trying to solve extends way beyond :not(). I could go through my stylesheets and compile a list of use cases that have specificity problems and don't include :not(). Thankfully, we have a resolution on this, so my time can be spent more productively.
Can we please focus on the name?

I think @SelenIT made an excellent point about :is() being an acronym for Ignore Specificity.

@fantasai

This comment has been minimized.

Show comment
Hide comment
@fantasai

fantasai Jan 4, 2018

Contributor

Thankfully, we have a resolution on this, so my time can be spent more productively. Can we please focus on the name?

I'm not disputing that we should have this feature, just the frequency with which it would be needed that is driving you to require its name to be two characters.

I think @SelenIT made an excellent point about :is() being an acronym for Ignore Specificity.

And as you so often like to point out, nobody reads documentation, so my point about the misleading pairing stands, however clever the backronym might be.

Contributor

fantasai commented Jan 4, 2018

Thankfully, we have a resolution on this, so my time can be spent more productively. Can we please focus on the name?

I'm not disputing that we should have this feature, just the frequency with which it would be needed that is driving you to require its name to be two characters.

I think @SelenIT made an excellent point about :is() being an acronym for Ignore Specificity.

And as you so often like to point out, nobody reads documentation, so my point about the misleading pairing stands, however clever the backronym might be.

@Nadya678

This comment has been minimized.

Show comment
Hide comment
@Nadya678

Nadya678 Jan 7, 2018

Should the :is() have specificity like :not()?
What specificity has :has()?

Nadya678 commented Jan 7, 2018

Should the :is() have specificity like :not()?
What specificity has :has()?

@ewilligers

This comment has been minimized.

Show comment
Hide comment
@ewilligers

ewilligers Jan 7, 2018

Contributor

What specificity has :has()?

:has is only available in the snapshot profile, thus I suspect it doesn't need to have defined specificity. The query() method does not consider specificity.

Contributor

ewilligers commented Jan 7, 2018

What specificity has :has()?

:has is only available in the snapshot profile, thus I suspect it doesn't need to have defined specificity. The query() method does not consider specificity.

fantasai added a commit that referenced this issue Jan 16, 2018

@fantasai

This comment has been minimized.

Show comment
Hide comment
@fantasai

fantasai Jan 16, 2018

Contributor

Should the :is() have specificity like :not()?

@Nadya678 :matches() behaves like :not(): it takes the specificity of its argument. The proposed :is(), which otherwise behaves exactly like :matches(), has a specificity of zero.

What specificity has :has()?

Good question. It looks like we forgot to define it when we switched from having a subject indicator to using :has() syntax. I've updated the Specificity section now--it has the specificity of its argument, just like :matches().

Contributor

fantasai commented Jan 16, 2018

Should the :is() have specificity like :not()?

@Nadya678 :matches() behaves like :not(): it takes the specificity of its argument. The proposed :is(), which otherwise behaves exactly like :matches(), has a specificity of zero.

What specificity has :has()?

Good question. It looks like we forgot to define it when we switched from having a subject indicator to using :has() syntax. I've updated the Specificity section now--it has the specificity of its argument, just like :matches().

@fantasai

This comment has been minimized.

Show comment
Hide comment
@fantasai

fantasai Jan 16, 2018

Contributor

Another option, fwiw, would be to use bare parens for eliminating specificity. There are two possible interpretations of this idea:

A. Treat it like a pseudo-class syntax, except without the preceding :foo.
B. The selector is interpreted exactly as if the parentheses weren't there, except that any selectors inside the parentheses aren't counted for specificity.

Example: foo(.a > .b) .c -- A would match this as foo:matches(.a > .b) .c whereas B would match it as foo.a > .b .c

For case B, if it's considered weird, we could say that the parens can't cross hierachical boundaries, i.e. that example above would be invalid, but you could write foo(.a) > (.b .c) to mean the same thing.

Contributor

fantasai commented Jan 16, 2018

Another option, fwiw, would be to use bare parens for eliminating specificity. There are two possible interpretations of this idea:

A. Treat it like a pseudo-class syntax, except without the preceding :foo.
B. The selector is interpreted exactly as if the parentheses weren't there, except that any selectors inside the parentheses aren't counted for specificity.

Example: foo(.a > .b) .c -- A would match this as foo:matches(.a > .b) .c whereas B would match it as foo.a > .b .c

For case B, if it's considered weird, we could say that the parens can't cross hierachical boundaries, i.e. that example above would be invalid, but you could write foo(.a) > (.b .c) to mean the same thing.

@tabatkins

This comment has been minimized.

Show comment
Hide comment
@tabatkins

tabatkins Jan 16, 2018

Member

That syntax doesn't work; it confuses the parser and produces paren-blocks in some cases, but function-blocks in others, and when it looks like a function, it isn't one. foo(.a > .b) appears to be a function named foo() to the parser.

Member

tabatkins commented Jan 16, 2018

That syntax doesn't work; it confuses the parser and produces paren-blocks in some cases, but function-blocks in others, and when it looks like a function, it isn't one. foo(.a > .b) appears to be a function named foo() to the parser.

@LeaVerou

This comment has been minimized.

Show comment
Hide comment
@LeaVerou

LeaVerou Jul 6, 2018

Contributor

Hi all,

I know that when it comes to names, we all have opinions. However, the group has been discussing this for months (possibly over a year) at this point. We've recently discussed it again in the F2F, and narrowed it down to :if() and :where(). I think it's highly unlikely that we will go back and reconsider other names at this point. Therefore, it would be far more productive to argue which one of these is a better choice.

Personally, I see myself using this A LOT so I think brevity is important. Roman Komarov seems to think the same. So, I would vote for :if(). Also, I think :if() stands much better when it's by itself than :where().
However, the community so far seems to favor :where(): https://twitter.com/LeaVerou/status/1014767203508338688

Based on their comments, I do worry a little that this is because they hope to see if used for something else, or because many of them are programmers and are used to if from there (whereas if has a much stronger association with natural language, if your brain has not been tainted by programming languages).

Contributor

LeaVerou commented Jul 6, 2018

Hi all,

I know that when it comes to names, we all have opinions. However, the group has been discussing this for months (possibly over a year) at this point. We've recently discussed it again in the F2F, and narrowed it down to :if() and :where(). I think it's highly unlikely that we will go back and reconsider other names at this point. Therefore, it would be far more productive to argue which one of these is a better choice.

Personally, I see myself using this A LOT so I think brevity is important. Roman Komarov seems to think the same. So, I would vote for :if(). Also, I think :if() stands much better when it's by itself than :where().
However, the community so far seems to favor :where(): https://twitter.com/LeaVerou/status/1014767203508338688

Based on their comments, I do worry a little that this is because they hope to see if used for something else, or because many of them are programmers and are used to if from there (whereas if has a much stronger association with natural language, if your brain has not been tainted by programming languages).

@bradkemper

This comment has been minimized.

Show comment
Hide comment
@bradkemper

bradkemper Jul 6, 2018

My last two suggestions weren’t serious.

But I liked :is() more than :if(). Sorry, but :if() just feels wrong to me. I think for Web authors, knowing a little (or more) JavaScript is more common than not. And you don’t need to know a lot of it to see if(foo) { } as script instead of CSS. :if() makes me expect there should also be an :else(). Or maybe a :then()

The older proposal of @if and @else for media queries seemed more natural to me.

bradkemper commented Jul 6, 2018

My last two suggestions weren’t serious.

But I liked :is() more than :if(). Sorry, but :if() just feels wrong to me. I think for Web authors, knowing a little (or more) JavaScript is more common than not. And you don’t need to know a lot of it to see if(foo) { } as script instead of CSS. :if() makes me expect there should also be an :else(). Or maybe a :then()

The older proposal of @if and @else for media queries seemed more natural to me.

@Tyler-H

This comment has been minimized.

Show comment
Hide comment
@Tyler-H

Tyler-H Jul 6, 2018

For what it's worth, languages I'm familiar with don't require an else or then statement for an if, but a lot of people assume one is required because many use cases do call for one. While English and basic science class does drill into our heads that "if" statements need to be followed by "then" statements, a lot of programming syntax is counter-intuitive when compared with lexical rules. I don't see an :if without a :then being any more of a problem than being recalcitrant to changing :matches while it is still in draft.

Tyler-H commented Jul 6, 2018

For what it's worth, languages I'm familiar with don't require an else or then statement for an if, but a lot of people assume one is required because many use cases do call for one. While English and basic science class does drill into our heads that "if" statements need to be followed by "then" statements, a lot of programming syntax is counter-intuitive when compared with lexical rules. I don't see an :if without a :then being any more of a problem than being recalcitrant to changing :matches while it is still in draft.

@tzi

This comment has been minimized.

Show comment
Hide comment
@tzi

tzi Jul 6, 2018

I have some mixed feelings here. In one hand, it is one of the feature that we want for like 10 years. In the other hand, I didn't read a comment from a developer that say "I don't car of the name, just ship it, we need it". Perhaps, it's because it's weird to have another operator to do almost the same thing that :matches?

A long time ago, for every CSS developers width was actually the content-width. And we switch, slowy, and now we see width as the box-width. I see this as a great success, because it's a change of the core of CSS, that didn't introduce too much complexity, we didn't introduce a new regularly used property. We handle it like a setting, something you decide at the start of your project. Most developer adopted it, but not everybody use it, it's a choice. We could have introduce a new property like box-width but it would have changed our daily syntax, allow mixed code, and the history of CSS would have been something hard to forget when we code.

I saw an idea of @philipwalton (#1170 (comment)) to add an attribute on the link tag. I think it could be a great solution.

My proposal is to add a specificity attribute, and a value to switch the specificity of :matches(), :not(), and :has() to zero. For example:

<link specificity="no-pseudo-container" rel="stylesheet" hef="path/to/reset.css">

Pros:

  • No operator added, I think it would be simpler for the beginner (ex: every code example use the same syntax)
  • :matches() is still the opposite of :not()
  • It's a setting, something we thing about at the start of our project, and we forget
  • it prevents from mixing :matches() and :if() and reduce our project complexity
  • It's explicit, it's not a short name that hide something big
  • We allow us to make a mistake. If at a time, we think the specificity should be handled differently (Ex: every pseudo selector should have no weight?) we could add a new value for the specificity attribute.

Cons:

  • It prevents mixing :matches() and :if(), so no fine tuning of our selectors
  • It's not the way we dig for the past months

What do you think about it, not this exact solution, but this kind of solution?

tzi commented Jul 6, 2018

I have some mixed feelings here. In one hand, it is one of the feature that we want for like 10 years. In the other hand, I didn't read a comment from a developer that say "I don't car of the name, just ship it, we need it". Perhaps, it's because it's weird to have another operator to do almost the same thing that :matches?

A long time ago, for every CSS developers width was actually the content-width. And we switch, slowy, and now we see width as the box-width. I see this as a great success, because it's a change of the core of CSS, that didn't introduce too much complexity, we didn't introduce a new regularly used property. We handle it like a setting, something you decide at the start of your project. Most developer adopted it, but not everybody use it, it's a choice. We could have introduce a new property like box-width but it would have changed our daily syntax, allow mixed code, and the history of CSS would have been something hard to forget when we code.

I saw an idea of @philipwalton (#1170 (comment)) to add an attribute on the link tag. I think it could be a great solution.

My proposal is to add a specificity attribute, and a value to switch the specificity of :matches(), :not(), and :has() to zero. For example:

<link specificity="no-pseudo-container" rel="stylesheet" hef="path/to/reset.css">

Pros:

  • No operator added, I think it would be simpler for the beginner (ex: every code example use the same syntax)
  • :matches() is still the opposite of :not()
  • It's a setting, something we thing about at the start of our project, and we forget
  • it prevents from mixing :matches() and :if() and reduce our project complexity
  • It's explicit, it's not a short name that hide something big
  • We allow us to make a mistake. If at a time, we think the specificity should be handled differently (Ex: every pseudo selector should have no weight?) we could add a new value for the specificity attribute.

Cons:

  • It prevents mixing :matches() and :if(), so no fine tuning of our selectors
  • It's not the way we dig for the past months

What do you think about it, not this exact solution, but this kind of solution?

@cork

This comment has been minimized.

Show comment
Hide comment
@cork

cork Jul 6, 2018

I have to say i have problem with both :if and :where cause in a bigger selector it feels like it moves the focus #select-this :if/:when(.this-is-true)

And I don't feel the same focus move from :matches, or :is. Sorry

cork commented Jul 6, 2018

I have to say i have problem with both :if and :where cause in a bigger selector it feels like it moves the focus #select-this :if/:when(.this-is-true)

And I don't feel the same focus move from :matches, or :is. Sorry

@FremyCompany

This comment has been minimized.

Show comment
Hide comment
@FremyCompany

FremyCompany Jul 8, 2018

Contributor

I feel like we should probably add real-world examples and see how they fare with the most common proposals:

Use case 1: specificity tweaks

textarea, input:if([type=text], [type=number], [type=email]) {
   /* default text box styling, with tag specificity */
   /* overridden by a css class, unlike what would happen without the no-specificity attribute check */
}

a:if([href^="http"]) {
    /* external link default style */
    /* does not override any 'a:hover' style defined before */
}

:any-link:if(:not(:hover):not(:active):not(:focus)) {
   /* by default, links have no own color */
   color: inherit; outline: none;
}
:any-link:focus {
    /* this rule overrides the previous one */
    outline: 1px dotted currentColor;
}
:any-link:hover {
    /* this rule overrides the previous ones */
    color: red; outline: none;
}

vs

textarea, input:where([type=text], [type=number], [type=email]) {
   /* default text box styling, with tag specificity */
   /* overridden by a css class, unlike what would happen without the no-specificity attribute check */
}

a:where([href^="http"]) {
    /* external link default style */
    /* does not override any 'a:hover' style defined before */
}

:any-link:where(:not(:hover):not(:active):not(:focus)) {
   /* by default, links have no own color */
   color: inherit; outline: none;
}
:any-link:focus {
    /* this rule overrides the previous one */
    outline: 1px dotted currentColor;
}
:any-link:hover {
    /* this rule overrides the previous ones */
    color: red; outline: none;
}

Use case 2: opting out of specificity entirely

:if(.class1) { … }
:if(.class1.class1--optionA) { … }
:if(.class2) { … } /* overrides .class1.class1-optionA style */
:if(.class2.class2--optionA) { … }
:if(.class2.class2--optionB) { … }

vs

:where(.class1) { … }
:where(.class1.class1--optionA) { … }
:where(.class2) { … } /* overrides .class1.class1-optionA style */
:where(.class2.class2--optionA) { … }
:where(.class2.class2--optionB) { … }
Contributor

FremyCompany commented Jul 8, 2018

I feel like we should probably add real-world examples and see how they fare with the most common proposals:

Use case 1: specificity tweaks

textarea, input:if([type=text], [type=number], [type=email]) {
   /* default text box styling, with tag specificity */
   /* overridden by a css class, unlike what would happen without the no-specificity attribute check */
}

a:if([href^="http"]) {
    /* external link default style */
    /* does not override any 'a:hover' style defined before */
}

:any-link:if(:not(:hover):not(:active):not(:focus)) {
   /* by default, links have no own color */
   color: inherit; outline: none;
}
:any-link:focus {
    /* this rule overrides the previous one */
    outline: 1px dotted currentColor;
}
:any-link:hover {
    /* this rule overrides the previous ones */
    color: red; outline: none;
}

vs

textarea, input:where([type=text], [type=number], [type=email]) {
   /* default text box styling, with tag specificity */
   /* overridden by a css class, unlike what would happen without the no-specificity attribute check */
}

a:where([href^="http"]) {
    /* external link default style */
    /* does not override any 'a:hover' style defined before */
}

:any-link:where(:not(:hover):not(:active):not(:focus)) {
   /* by default, links have no own color */
   color: inherit; outline: none;
}
:any-link:focus {
    /* this rule overrides the previous one */
    outline: 1px dotted currentColor;
}
:any-link:hover {
    /* this rule overrides the previous ones */
    color: red; outline: none;
}

Use case 2: opting out of specificity entirely

:if(.class1) { … }
:if(.class1.class1--optionA) { … }
:if(.class2) { … } /* overrides .class1.class1-optionA style */
:if(.class2.class2--optionA) { … }
:if(.class2.class2--optionB) { … }

vs

:where(.class1) { … }
:where(.class1.class1--optionA) { … }
:where(.class2) { … } /* overrides .class1.class1-optionA style */
:where(.class2.class2--optionA) { … }
:where(.class2.class2--optionB) { … }
@FremyCompany

This comment has been minimized.

Show comment
Hide comment
@FremyCompany

FremyCompany Jul 8, 2018

Contributor

Typing it out, I think :if is the best solution by far. I only had a very slight preference for :if during the meeting, and voted for both proposals, but after seeing this, I'm strongly in favor of :if now.

Contributor

FremyCompany commented Jul 8, 2018

Typing it out, I think :if is the best solution by far. I only had a very slight preference for :if during the meeting, and voted for both proposals, but after seeing this, I'm strongly in favor of :if now.

@tzi

This comment has been minimized.

Show comment
Hide comment
@tzi

tzi Jul 10, 2018

And what about a :0() selector?
It's actually mean that it does something special. The zero is not explicit, but it is at least logical.

a:0([href^="http"]) {
    /* external link default style, does not override any 'a:hover' styles */
}
:0(.block) {
    /* opting out of specificity entirely */
}

Cheers,
Thomas.

tzi commented Jul 10, 2018

And what about a :0() selector?
It's actually mean that it does something special. The zero is not explicit, but it is at least logical.

a:0([href^="http"]) {
    /* external link default style, does not override any 'a:hover' styles */
}
:0(.block) {
    /* opting out of specificity entirely */
}

Cheers,
Thomas.

@nico3333fr

This comment has been minimized.

Show comment
Hide comment
@nico3333fr

nico3333fr Jul 10, 2018

Love the :0 idea, a nice smiley => :0 => OMG, specificity to 0 ???? :D

nico3333fr commented Jul 10, 2018

Love the :0 idea, a nice smiley => :0 => OMG, specificity to 0 ???? :D

@Loirooriol

This comment has been minimized.

Show comment
Hide comment
@Loirooriol

Loirooriol Jul 10, 2018

Collaborator

For me the problem with :if is not that it needs an :else. Instead it's that intuitively I would think it would remove part of the selector if a condition is false, i.e. foo bar:if(#cond) baz would behave like foo bar#cond baz, foo baz.
Of course this doesn't really make sense, but it's what the selector seems to mean to me at first glance.

I don't really like :where either.

:0 is not intuitive, it would be difficult to find its meaning using a search engine, and would require syntax changes because identifiers cannot begin with a digit.

Collaborator

Loirooriol commented Jul 10, 2018

For me the problem with :if is not that it needs an :else. Instead it's that intuitively I would think it would remove part of the selector if a condition is false, i.e. foo bar:if(#cond) baz would behave like foo bar#cond baz, foo baz.
Of course this doesn't really make sense, but it's what the selector seems to mean to me at first glance.

I don't really like :where either.

:0 is not intuitive, it would be difficult to find its meaning using a search engine, and would require syntax changes because identifiers cannot begin with a digit.

@iamvdo

This comment has been minimized.

Show comment
Hide comment
@iamvdo

iamvdo Jul 10, 2018

:0() is one of the best idea so far. As identifiers cannot begin with a digit, what about :zero() or :void() or even :null() (even if I don't like this one)?

iamvdo commented Jul 10, 2018

:0() is one of the best idea so far. As identifiers cannot begin with a digit, what about :zero() or :void() or even :null() (even if I don't like this one)?

@inoas

This comment has been minimized.

Show comment
Hide comment
@inoas

inoas Jul 11, 2018

@LeaVerou
:if has a strong procedural meaning whereas :where has not and I share your fear that :if may or may not become useful for other purposes in future.

@CyberAP

... from a functional side of things. What this selector ... tries to do is to set a default value that can be easily overridden, so :defaults() comes to mind.
However, there is a :default pseudo-class and that may of course confuse people.

What about :init() or :initial() then. It behaves similiar to resetting a bunch of values to property: initial.

a:initial(.toggled) {
    /* external link default style, does not override any 'a:hover' styles */
}

Edit:
:cond(...) > :where(...) > :initial(...) > :when(...) > :specificity(0, ...) > :for() > :if(...)

Edit: Fixed example @tzi

inoas commented Jul 11, 2018

@LeaVerou
:if has a strong procedural meaning whereas :where has not and I share your fear that :if may or may not become useful for other purposes in future.

@CyberAP

... from a functional side of things. What this selector ... tries to do is to set a default value that can be easily overridden, so :defaults() comes to mind.
However, there is a :default pseudo-class and that may of course confuse people.

What about :init() or :initial() then. It behaves similiar to resetting a bunch of values to property: initial.

a:initial(.toggled) {
    /* external link default style, does not override any 'a:hover' styles */
}

Edit:
:cond(...) > :where(...) > :initial(...) > :when(...) > :specificity(0, ...) > :for() > :if(...)

Edit: Fixed example @tzi

@CyberAP

This comment has been minimized.

Show comment
Hide comment
@CyberAP

CyberAP Jul 13, 2018

Hmm, what about :foreach? It basically takes an array of arguments and for each item performs some operation, without affecting anything, similar to the forEach array method in JS.

So if with JS we would write:

['.a', '.b', '.c'].forEach(item => doSomething(item + ' el'));

In CSS it would become:

:foreach(.a,.b,.c) el {
  // doSomething
}

CyberAP commented Jul 13, 2018

Hmm, what about :foreach? It basically takes an array of arguments and for each item performs some operation, without affecting anything, similar to the forEach array method in JS.

So if with JS we would write:

['.a', '.b', '.c'].forEach(item => doSomething(item + ' el'));

In CSS it would become:

:foreach(.a,.b,.c) el {
  // doSomething
}
@tomhodgins

This comment has been minimized.

Show comment
Hide comment
@tomhodgins

tomhodgins Jul 13, 2018

I'm still fond of :is() because of the brevity and clarity, but if that's been decided against would a name like :selector() be a good replacement? Something like :selector() indicates to me that it would match a CSS selector, and when styled like a pseudo-class (:()) it can modify any part of a selector as well. Here are some examples so you can see what it might look like and how it reads in code:

a:selector(.active) { }

:selector(header, footer, nav) a { }

It's not explicit that it's ignoring specificity, but it seems less permanent, or less strong than if I had written the same selector directly. What do you think?

tomhodgins commented Jul 13, 2018

I'm still fond of :is() because of the brevity and clarity, but if that's been decided against would a name like :selector() be a good replacement? Something like :selector() indicates to me that it would match a CSS selector, and when styled like a pseudo-class (:()) it can modify any part of a selector as well. Here are some examples so you can see what it might look like and how it reads in code:

a:selector(.active) { }

:selector(header, footer, nav) a { }

It's not explicit that it's ignoring specificity, but it seems less permanent, or less strong than if I had written the same selector directly. What do you think?

@SelenIT

This comment has been minimized.

Show comment
Hide comment
@SelenIT

SelenIT Jul 13, 2018

Collaborator

What about :and()? It's short, it seems intuitive (a conjunction of two conditions, the target element should match both main and additional part), but in the same time it separates the selector into the non-parenthesized and parenthesized parts, implying that the latter is "special" (so its specificity might
be not counted as usually).

Though it might look a bit odd in the beginning of the selector, the general rule that absence of the elemental selector means * should help to understand the intent to express the selector that has the specificity of the universal selector (i.e. zero) and still selects only specific elements...

Collaborator

SelenIT commented Jul 13, 2018

What about :and()? It's short, it seems intuitive (a conjunction of two conditions, the target element should match both main and additional part), but in the same time it separates the selector into the non-parenthesized and parenthesized parts, implying that the latter is "special" (so its specificity might
be not counted as usually).

Though it might look a bit odd in the beginning of the selector, the general rule that absence of the elemental selector means * should help to understand the intent to express the selector that has the specificity of the universal selector (i.e. zero) and still selects only specific elements...

@CyberAP

This comment has been minimized.

Show comment
Hide comment
@CyberAP

CyberAP Jul 13, 2018

@SelenIT :and() can be confused with a comma combinator a, b.

CyberAP commented Jul 13, 2018

@SelenIT :and() can be confused with a comma combinator a, b.

@Loirooriol

This comment has been minimized.

Show comment
Hide comment
@Loirooriol

Loirooriol Jul 13, 2018

Collaborator

What about :any? It's short, intuitive, and it's already implemented (but with a prefix and specificity 0,1,0 instead of 0,0,0).

There are also some old documentations mentioning :any, so people may get confused about :any, :matches and :something. Renaming :something to :any reduces the cognitive load, it's one option less to remember. Sure, if some documentation mentioned the specificity of :any then it will become invalid, but usually the focus was in the functionality instead of the specificity.

Collaborator

Loirooriol commented Jul 13, 2018

What about :any? It's short, intuitive, and it's already implemented (but with a prefix and specificity 0,1,0 instead of 0,0,0).

There are also some old documentations mentioning :any, so people may get confused about :any, :matches and :something. Renaming :something to :any reduces the cognitive load, it's one option less to remember. Sure, if some documentation mentioned the specificity of :any then it will become invalid, but usually the focus was in the functionality instead of the specificity.

@SelenIT

This comment has been minimized.

Show comment
Hide comment
@SelenIT

SelenIT Jul 13, 2018

Collaborator

@CyberAP, the comma clearly means "or", not "and" :)

@Loirooriol, wouldn't it increase the cognitive load instead if for some period of transition there would co-exist two completely different experimental implementations of :any/:-*-any — the old deprecated one with the specificity of the single pseudo-class and the new one with no specificity at all?

But I agree that it's confusing to have several things that effectively do nearly the same job, but a bit differently. That's why, ideally, I would prefer the single pseudo-class with some (optional) modifier for turning off/adjusting the specificity to the current solution with 2 very similar pseudo-classes (plus the old deprecated-but-yet-supported one).

Collaborator

SelenIT commented Jul 13, 2018

@CyberAP, the comma clearly means "or", not "and" :)

@Loirooriol, wouldn't it increase the cognitive load instead if for some period of transition there would co-exist two completely different experimental implementations of :any/:-*-any — the old deprecated one with the specificity of the single pseudo-class and the new one with no specificity at all?

But I agree that it's confusing to have several things that effectively do nearly the same job, but a bit differently. That's why, ideally, I would prefer the single pseudo-class with some (optional) modifier for turning off/adjusting the specificity to the current solution with 2 very similar pseudo-classes (plus the old deprecated-but-yet-supported one).

@bkardell

This comment has been minimized.

Show comment
Hide comment
@bkardell

bkardell Jul 17, 2018

I've been hesitant to comment because I've not had anything particularly thoughtful to add beyond varying degrees of conflicting thoughts on each one. However, over the past week or so I have informally talked to a (small) number of people with various degrees of skill who either independently arrived at :zero(...) or :nil(...) when posed the description without options, or, having known options agreed that this is among the better ones in this thread.

I just want to note that is isn't in the table above and I am in that camp, I think that either of those makes more sense to me than many of the others and I think the big strike against it in terms of the table is that it would prevent from later extending it to be arbitrary specificity. I'm not entirely convinced that is actually a big strike though.

bkardell commented Jul 17, 2018

I've been hesitant to comment because I've not had anything particularly thoughtful to add beyond varying degrees of conflicting thoughts on each one. However, over the past week or so I have informally talked to a (small) number of people with various degrees of skill who either independently arrived at :zero(...) or :nil(...) when posed the description without options, or, having known options agreed that this is among the better ones in this thread.

I just want to note that is isn't in the table above and I am in that camp, I think that either of those makes more sense to me than many of the others and I think the big strike against it in terms of the table is that it would prevent from later extending it to be arbitrary specificity. I'm not entirely convinced that is actually a big strike though.

@jonathantneal

This comment has been minimized.

Show comment
Hide comment
@jonathantneal

jonathantneal Jul 26, 2018

Contributor

TL;DR: I like :any().


Forking @LeaVerou’s list from earlier to express sentiments on keywords I was drawn to:

Name Pros Cons
:any() short, meaningful prefixed version had differing specificity characteristics
:as() short, frequently used suggests transformation of preceeding selector
:if() short suggests logical opposite of non-existent :else()
:is() short, meaningful, backronym for "Ignore Specificity". suggests logical opposite of existing :not without matching specificity characteristics
:nil() short infrequently used in language, explicitly not extendable
:nospecificity() meaningful long, explicitly not extendable
:when() meaningful suggests a change of context (when some time is.., when some element query is...)
:zero() meaningful explicitly not extendable

* The table is sorted alphabetically by name
* nil is an infrequent word according to https://www.wordfrequency.info/free.asp?s=y

Example Usages:

Forking @FremyCompany’s usages with minor changes to further share my impressions:

/* :any() → actually looks okay especially because any is also used in any-link */

:any(textarea, input:matches([type=text], [type=number], [type=email], :not([type])) {
  /* weightless text box styling */
}

:any(:any-link:not(:hover):not(:active):not(:focus)) {
  /* weightless link styling */
}
/* :as() → looks visually similar to :has and tripped me up when following it with :any-link */

:as(textarea, input:matches([type=text], [type=number], [type=email], :not([type])) {
  /* weightless text box styling */
}

:as(:any-link:not(:hover):not(:active):not(:focus)) {
  color: inherit;
}
/* :if() → so is :else like :not but with zero-specificity? */

:if(textarea, input:matches([type=text], [type=number], [type=email], :not([type])) {
  /* weightless text box styling */
}

:if(:any-link:not(:hover):not(:active):not(:focus)) {
  /* weightless link styling */
}
/* :nil() → i get it, but looks like programmer-speak not found elsewhere in css */

:nil(textarea, input:matches([type=text], [type=number], [type=email], :not([type])) {
  /* weightless text box styling */
}

:nil(:any-link:not(:hover):not(:active):not(:focus)) {
  /* weightless link styling */
}
/* :nospecificity() → hello, spell check, my old friend */

:nospecificity(textarea, input:matches([type=text], [type=number], [type=email], :not([type])) {
  /* weightless text box styling */
}

:nospecificity(:any-link:not(:hover):not(:active):not(:focus)) {
  /* weightless link styling */
}
/* :when() → it feels like this should not take css selectors */

:when(textarea, input:matches([type=text], [type=number], [type=email], :not([type])) {
  /* weightless text box styling */
}

:when(:any-link:not(:hover):not(:active):not(:focus)) {
  /* weightless link styling */
}
/* :zero() → it feels like :zero is supposed to represent a state */

:zero(textarea, input:matches([type=text], [type=number], [type=email], :not([type])) {
  /* weightless text box styling */
}

:zero(:any-link:not(:hover):not(:active):not(:focus)) {
  /* weightless link styling */
}

My overall impression is that :any seems the most clear and consistent.

Contributor

jonathantneal commented Jul 26, 2018

TL;DR: I like :any().


Forking @LeaVerou’s list from earlier to express sentiments on keywords I was drawn to:

Name Pros Cons
:any() short, meaningful prefixed version had differing specificity characteristics
:as() short, frequently used suggests transformation of preceeding selector
:if() short suggests logical opposite of non-existent :else()
:is() short, meaningful, backronym for "Ignore Specificity". suggests logical opposite of existing :not without matching specificity characteristics
:nil() short infrequently used in language, explicitly not extendable
:nospecificity() meaningful long, explicitly not extendable
:when() meaningful suggests a change of context (when some time is.., when some element query is...)
:zero() meaningful explicitly not extendable

* The table is sorted alphabetically by name
* nil is an infrequent word according to https://www.wordfrequency.info/free.asp?s=y

Example Usages:

Forking @FremyCompany’s usages with minor changes to further share my impressions:

/* :any() → actually looks okay especially because any is also used in any-link */

:any(textarea, input:matches([type=text], [type=number], [type=email], :not([type])) {
  /* weightless text box styling */
}

:any(:any-link:not(:hover):not(:active):not(:focus)) {
  /* weightless link styling */
}
/* :as() → looks visually similar to :has and tripped me up when following it with :any-link */

:as(textarea, input:matches([type=text], [type=number], [type=email], :not([type])) {
  /* weightless text box styling */
}

:as(:any-link:not(:hover):not(:active):not(:focus)) {
  color: inherit;
}
/* :if() → so is :else like :not but with zero-specificity? */

:if(textarea, input:matches([type=text], [type=number], [type=email], :not([type])) {
  /* weightless text box styling */
}

:if(:any-link:not(:hover):not(:active):not(:focus)) {
  /* weightless link styling */
}
/* :nil() → i get it, but looks like programmer-speak not found elsewhere in css */

:nil(textarea, input:matches([type=text], [type=number], [type=email], :not([type])) {
  /* weightless text box styling */
}

:nil(:any-link:not(:hover):not(:active):not(:focus)) {
  /* weightless link styling */
}
/* :nospecificity() → hello, spell check, my old friend */

:nospecificity(textarea, input:matches([type=text], [type=number], [type=email], :not([type])) {
  /* weightless text box styling */
}

:nospecificity(:any-link:not(:hover):not(:active):not(:focus)) {
  /* weightless link styling */
}
/* :when() → it feels like this should not take css selectors */

:when(textarea, input:matches([type=text], [type=number], [type=email], :not([type])) {
  /* weightless text box styling */
}

:when(:any-link:not(:hover):not(:active):not(:focus)) {
  /* weightless link styling */
}
/* :zero() → it feels like :zero is supposed to represent a state */

:zero(textarea, input:matches([type=text], [type=number], [type=email], :not([type])) {
  /* weightless text box styling */
}

:zero(:any-link:not(:hover):not(:active):not(:focus)) {
  /* weightless link styling */
}

My overall impression is that :any seems the most clear and consistent.

@phistuck

This comment has been minimized.

Show comment
Hide comment
@phistuck

phistuck Jul 26, 2018

Contributor

Is :matches(0, selector, selector...)) not an option?

Contributor

phistuck commented Jul 26, 2018

Is :matches(0, selector, selector...)) not an option?

@tzi

This comment has been minimized.

Show comment
Hide comment
@tzi

tzi Jul 26, 2018

@jonathantneal I don't see the cons about a pseudo-class that is "explicitly not extended-able". Can you give some context?

:nil() and :zero() seems to be the only ones where it will be clear "why we used this pseudo-selector". So they've got my vote.

tzi commented Jul 26, 2018

@jonathantneal I don't see the cons about a pseudo-class that is "explicitly not extended-able". Can you give some context?

:nil() and :zero() seems to be the only ones where it will be clear "why we used this pseudo-selector". So they've got my vote.

@jonathantneal

This comment has been minimized.

Show comment
Hide comment
@jonathantneal

jonathantneal Jul 26, 2018

Contributor

Hey @tzi, I was under the impression that folks wanted the ability to possibly extend the functionality of this selector in the future. I considered “explicitly not extendable” to be taken similarly as one of Lea’s cons — “can't be extended to specify specificity”. Sorry if I missed that this concern is no longer relevant.

@ionas, I’m not opposed to extending matches, either, especially if it’s readable and doesn’t require look-ahead. Thanks for looking at it anyway. 😄

Contributor

jonathantneal commented Jul 26, 2018

Hey @tzi, I was under the impression that folks wanted the ability to possibly extend the functionality of this selector in the future. I considered “explicitly not extendable” to be taken similarly as one of Lea’s cons — “can't be extended to specify specificity”. Sorry if I missed that this concern is no longer relevant.

@ionas, I’m not opposed to extending matches, either, especially if it’s readable and doesn’t require look-ahead. Thanks for looking at it anyway. 😄

@Tyler-H

This comment has been minimized.

Show comment
Hide comment
@Tyler-H

Tyler-H Jul 26, 2018

Most of the comments from the last three weeks seem to not respect the fact that the WG has narrowed their choices down to :if and :where.

Tyler-H commented Jul 26, 2018

Most of the comments from the last three weeks seem to not respect the fact that the WG has narrowed their choices down to :if and :where.

@jonathantneal

This comment has been minimized.

Show comment
Hide comment
@jonathantneal

jonathantneal Jul 26, 2018

Contributor

Until we see implementation, @Tyler-H, I would actively encourage you to challenge any consensus you think could be better. Make your differing evaluation clear. Keep a positive tone. That’s showing respect for the web and the people who make it.

get it wrong & it's on the web & pissing off devs forever
— Bruce Lawson https://twitter.com/brucel/status/1022394427225063425

Contributor

jonathantneal commented Jul 26, 2018

Until we see implementation, @Tyler-H, I would actively encourage you to challenge any consensus you think could be better. Make your differing evaluation clear. Keep a positive tone. That’s showing respect for the web and the people who make it.

get it wrong & it's on the web & pissing off devs forever
— Bruce Lawson https://twitter.com/brucel/status/1022394427225063425

@Tyler-H

This comment has been minimized.

Show comment
Hide comment
@Tyler-H

Tyler-H Jul 26, 2018

@jonathantneal The thing is this topic has been taken to the bike shed already, and Lea requested to focus discussion on the two WG front-runners. Ignoring the request without providing critiques of the front-runners is not respect, it's... ignoring the request. Only two or three comments have provided concerns/critiques with the front-runners, while others (like yours) just take other suggestions and run with them.

As far as "pissing off devs forever" frankly the concerns of people who have visceral reactions to naming things should have their concerns notched far below those with actual logic-based reasons.

Tyler-H commented Jul 26, 2018

@jonathantneal The thing is this topic has been taken to the bike shed already, and Lea requested to focus discussion on the two WG front-runners. Ignoring the request without providing critiques of the front-runners is not respect, it's... ignoring the request. Only two or three comments have provided concerns/critiques with the front-runners, while others (like yours) just take other suggestions and run with them.

As far as "pissing off devs forever" frankly the concerns of people who have visceral reactions to naming things should have their concerns notched far below those with actual logic-based reasons.

@jonathantneal

This comment has been minimized.

Show comment
Hide comment
@jonathantneal

jonathantneal Jul 26, 2018

Contributor

I’m legitimately (and not sarcastically) sorry to you if my critique was not profound enough, but please do re-read my post, looking specifically for :if().

All of that aside, if you wish to continue bikeshedding how we ought to bikeshed, I would kindly ask that we move that discussion to Twitter, where civilized discourse may abound. 😄

Contributor

jonathantneal commented Jul 26, 2018

I’m legitimately (and not sarcastically) sorry to you if my critique was not profound enough, but please do re-read my post, looking specifically for :if().

All of that aside, if you wish to continue bikeshedding how we ought to bikeshed, I would kindly ask that we move that discussion to Twitter, where civilized discourse may abound. 😄

@bkardell

This comment has been minimized.

Show comment
Hide comment
@bkardell

bkardell Jul 26, 2018

:nil() and :zero() seems to be the only ones where it will be clear "why we used this pseudo-selector". So they've got my vote.

@tzi - and :nospecificity

@Tyler-H FWIW, while I definitely share the sentiment that perpetual bikeshed isn't a good thing, it seems to me that part of why the WG has made no real decision is that none of the options seemed particularly great (at least this has been my feeling - kind of noncommittal/least worst). Since then, I feel like some options that seem at least as good have been added. From other conversations I am led to think that perhaps some other WG members feel this. I think that updating the table and asking whether it is more valuable for the pseudo to be clear or extendable to including a numeric specificity seems valid and potentially helpful. Perhaps the WG can resolve one way or another on that much at least and we can eliminate several items?

bkardell commented Jul 26, 2018

:nil() and :zero() seems to be the only ones where it will be clear "why we used this pseudo-selector". So they've got my vote.

@tzi - and :nospecificity

@Tyler-H FWIW, while I definitely share the sentiment that perpetual bikeshed isn't a good thing, it seems to me that part of why the WG has made no real decision is that none of the options seemed particularly great (at least this has been my feeling - kind of noncommittal/least worst). Since then, I feel like some options that seem at least as good have been added. From other conversations I am led to think that perhaps some other WG members feel this. I think that updating the table and asking whether it is more valuable for the pseudo to be clear or extendable to including a numeric specificity seems valid and potentially helpful. Perhaps the WG can resolve one way or another on that much at least and we can eliminate several items?

@sarimarton

This comment has been minimized.

Show comment
Hide comment
@sarimarton

sarimarton Aug 1, 2018

I've read the whole thread, and I'm aware of that now it's supposedly only an :if vs. :when question. But I'd like to get back a little bit (if only to show why :if and :when are mistaken tracks).

It's notable how far we've got from :is. :is is by far the most intuitive choice, everybody gets that in their mind in the first (or second) place, and there's a reason for that (no, not the funny acronym, which in my view has zero relevance). A selector, by nature, is a predicate, which says something about the matching element. Linguistically, nouns, adjectives and verbs and negation play well as predicates. Not surprisingly, if we look around among the current pseudo-classes, we find exactly these word classes. Any other word class, which the alternatives belong to, including :if, :when, :nospecificity, :zero etc., essentially breaks this logic and so hits a little bit in the gut. Obviously they can been read as parts of a sentence, but the reading logic is custom in that case. :if is not really a first-order predicate in itself.

Now came the problem with the imperfect contrast with :not. I'm not sure how this can be more important, than the above. :is is an exception in the system which has no specificity calculation, and having the most generic name plays well with this fact. Technically this imperfect contrast is not a problem at all. It's actually beneficial, because it makes :is(:not( meaningful, and it reads well.

On the other hand, in the future, other strategies might appear for specificity management. To me, for example, using the zero-specificity selector (whatever name it has, and however short it is) extensively, particularly with wrapping the whole selector to keep it on zero, is not a good long-term solution. It's just code repetition. We might have a higher order scope for specificity, maybe something like this:

<link rel="stylesheet" type="text/css" href="framework.css" specificity=1>
<link rel="stylesheet" type="text/css" href="mycustom.css" specificity=x>

which might act as the highest order in the calculation placeholders: x,0,0,0. I know it's out of scope (and maybe it is proposed already?), but my point is that specificity strategies can easily evolve above the selector level, so we shouldn't get lost in the supposed significance of the :is vs. :not question. IMO, :is would nicely fill a gap in the API (linguistically) and the zero-specificity function is just a good motive to get it not just as an alias (to :matches). Imperfect contrast with :not is not a problem at all. And we can leave :matches as it is now.

Sorry for being too long.

sarimarton commented Aug 1, 2018

I've read the whole thread, and I'm aware of that now it's supposedly only an :if vs. :when question. But I'd like to get back a little bit (if only to show why :if and :when are mistaken tracks).

It's notable how far we've got from :is. :is is by far the most intuitive choice, everybody gets that in their mind in the first (or second) place, and there's a reason for that (no, not the funny acronym, which in my view has zero relevance). A selector, by nature, is a predicate, which says something about the matching element. Linguistically, nouns, adjectives and verbs and negation play well as predicates. Not surprisingly, if we look around among the current pseudo-classes, we find exactly these word classes. Any other word class, which the alternatives belong to, including :if, :when, :nospecificity, :zero etc., essentially breaks this logic and so hits a little bit in the gut. Obviously they can been read as parts of a sentence, but the reading logic is custom in that case. :if is not really a first-order predicate in itself.

Now came the problem with the imperfect contrast with :not. I'm not sure how this can be more important, than the above. :is is an exception in the system which has no specificity calculation, and having the most generic name plays well with this fact. Technically this imperfect contrast is not a problem at all. It's actually beneficial, because it makes :is(:not( meaningful, and it reads well.

On the other hand, in the future, other strategies might appear for specificity management. To me, for example, using the zero-specificity selector (whatever name it has, and however short it is) extensively, particularly with wrapping the whole selector to keep it on zero, is not a good long-term solution. It's just code repetition. We might have a higher order scope for specificity, maybe something like this:

<link rel="stylesheet" type="text/css" href="framework.css" specificity=1>
<link rel="stylesheet" type="text/css" href="mycustom.css" specificity=x>

which might act as the highest order in the calculation placeholders: x,0,0,0. I know it's out of scope (and maybe it is proposed already?), but my point is that specificity strategies can easily evolve above the selector level, so we shouldn't get lost in the supposed significance of the :is vs. :not question. IMO, :is would nicely fill a gap in the API (linguistically) and the zero-specificity function is just a good motive to get it not just as an alias (to :matches). Imperfect contrast with :not is not a problem at all. And we can leave :matches as it is now.

Sorry for being too long.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment