Which CSS Pseudo-Classes Don't Have Specificity

Which CSS pseudo-classes don't have specificity?

If you check the specification you can find the full detail of specificity calculation. I am going to refer to CSS selectors level 4 that include all the new selectors:

A selector’s specificity is calculated for a given element as follows:

  • count the number of ID selectors in the selector (= A)
  • count the number of class selectors, attributes selectors, and pseudo-classes in the selector (= B)
  • count the number of type selectors and pseudo-elements in the selector (= C)
  • ignore the universal selector

If the selector is a selector list, this number is calculated for each selector in the list. For a given matching process against the list, the specificity in effect is that of the most specific selector in the list that matches.

A few pseudo-classes provide “evaluation contexts” for other selectors, and so have their specificity defined specially:

The specificity of an :is(), :not(), or :has() pseudo-class is replaced by the specificity of the most specific complex selector in its selector list argument.

Analogously, the specificity of an :nth-child() or :nth-last-child() selector is the specificity of the pseudo class itself (counting as one pseudo-class selector) plus the specificity of the most specific complex selector in its selector list argument (if any).

The specificity of a :where() pseudo-class is replaced by zero.

So basically, the * never counts and it's the same for :where(). You can also read that:

neither the :where pseudo-class, nor any of its arguments contribute to the specificity of the selector—its specificity is always zero. ref

For :is(), :not() and :has() you consider what is inside. This means that the following selector have the same speficity:

a:not(.class) == a.class
a:not(#id):is(.class) == a#id.class

But pay attention to the sentence: is replaced by the specificity of the most specific complex selector in its selector list argument. In the near future we can write something like :

a:not(.class, #id)

and its specificity is equal to

a#id

and not to

a.class#id

Considering this, only :where() doesn't have any specificity or, to use better words, doesn't contribute to the specificity calculation. :not(), :is() and :has() do but only based on what they have inside, unlike :nth-child() where we count it in the B and we also count what it contains.

Note that in the future we can write something like below:

 a:nth-child(-n+3 of li.important)

Which have a specificty equal to 1 class selector (.important) + 1 pseudo class (:nth-child) + 2 type selector (a li)

CSS specificity of :not() pseudo class

It's not just you; this is indeed one of the fundamental pitfalls of specificity as a CSS concept.

A simpler solution that is equally valid would be to repeat your .ticker class selector so you have this:

#column .ticker.ticker ul {
margin: 0;
}

This way you do not have to modify your element to add an extra class just for the sake of increasing selector specificity.

The spec verifies this:

Note: Repeated occurrances of the same simple selector are allowed and do increase specificity.

On a side note, remember that the specificity of the :not() pseudo-class is strictly defined (in the same section of the spec) as equal to that of its argument. So :not(#id) and #id have the same specificity, and likewise for :not(E):not(.a) and E.a. The :not portion does not count at all, not even as a pseudo-class.

This limitation in specificity will be addressed in Selectors 4, which enhances :not() to accept a comma-delimited list of selectors. The specificity of a :not() that contains a selector list will be that of the most specific selectors in the list, so the specificity of ul:not(.c, .d) is equal to 1 type selector and 1 class selector, compared to ul:not(.c):not(.d) which is equal to 1 type selector and 2 class selectors. This makes it tremendously useful in excluding any number of classes from a match.

Does the :not pseudo class increase the specificity of a selector?

Yes, it adds the specificity of its argument. Look at the first sentence:

The specificity of the :not pseudo class is the specificity of its argument. The :not() pseudo class does not add to the selector specificity, unlike other pseudo-classes.

So the specificity of .red:not(.blue) is equal to that of .red.blue — 2 class selectors, or (0, 2, 0), making it more specific than .red on its own. What the second sentence means is that the :not() itself does not contribute the additional specificity of a pseudo-class to make it (0, 3, 0), like the :hover in .red.blue:hover does for example.

Calculating CSS selector specificity for :not() pseudo-class

Assuming both of them apply to one element, should these two be calculated as (0011) and therefore they should be ordered according to order of appearance? Or should the selectors inside the :not() pseudo-class be further calculated separately depending on which one match as a second step to determine the one that have precedence over the other?

If you're implementing Selectors 3, they should not be counted at all. As already mentioned, both of your selectors are invalid according to that spec because it only defines :not() to accept a single simple selector at a time.

If you expand them out so that they validate (following the instructions/examples given here), then their specificities will be calculated as follows:

/* 2 attributes, 1 class, 1 type -> specificity = 0-3-1 */
input:not([type="text"]):not([type="password"]):not(.someClass)

/*
* 1 ID, 1 type -> specificity = 1-0-1
* 1 attribute, 1 type -> specificity = 0-1-1
*/
input:not(#someId), input:not([type="text"])

Because Selectors 3 says:

Selectors inside the negation pseudo-class are counted like any other, but the negation itself does not count as a pseudo-class.

Also, in response to your comment:

True, according to specs, simple selectors only. But some browsers support multiple ones. Some of them don't, and some dropped them later. Also, you can write the same rule even with simple selectors like this instead: input:not([type="text"]):not([type="password"]):not(.someClass) which is better and work as well. Does this mean it should be calculated as 0031, then? How about those that support multiple ones, how do they calculate?

The only browser I know of that has ever supported multiple selectors within :not() is Firefox 3.0, and it does so because of a bug. Selectors 3 never allowed :not() to contain multiple selectors — that is only introduced in Selectors 4, for which specificity calculation hasn't even been clearly defined yet1, so even if you were trying to implement Selectors 4 (which I seriously doubt you are), you'll be stuck.

It's not clear to me how Firefox 3.0 implemented specificity with its version of the :not() selector and I don't have a copy of it to test with, but I think it's safe to assume that it doesn't matter anymore as it was never the intended behavior anyway. OK so I picked up Firefox 3.0 beta 1, 3.0.0, 3.0.18 and 3.1 beta 1, and none of them reproduce this behavior at all. So there you have it.


1 Note that both the current 2013 ED and the 2011 FPWD are consistent in saying that the specificity of :not() is equal to that of its most specific argument, but this may change in the future.

Does the CSS direct decendant () not have any value in selectivity?

There's no value for > for css specificity.

Both case have 11 value for specificity:

.foo > a { color:green; }/*specificity value is 11*/
.bar a { color:red; }/*specificity value is 11*/

In your case you may use like this to have greater specificty:

.bar .foo > a { color:green; }/*greater specificity value is 21*/
.foo a { color:red; }/*specificity value is 11*/

Ok, I'm going add here how specificity works:

Selector                          Specificity         Specificity in large base
inline-style 1 0 0 0 1000
id selector 0 1 0 0 100
class,pseudo,attribute selector 0 0 1 0 10
type selector and pseudo elements 0 0 0 1 1

Will order matters when there's specificity exists?

Classes and pseudo-classes have the same specificity. .menu li.active therefore has the same 'weight' as .menu li:hover.

Formula, explained (among others) on CSS Specificity: Things You Should Know and CSS Tricks: Specifics on CSS Specificity:

Memorize how to measure specificity. “Start at 0, add 1000 for style
attribute, add 100 for each ID, add 10 for each attribute, class or
pseudo-class, add 1 for each element name or pseudo-element. So in
body #content .data img:hover the specificity value would be 122
(0,1,2,2 or 0122): 100 for #content, 10 for .data, 10 for :hover, 1
for body and 1 for img.”

Applied to your CSS, it would give 21 for both selectors. In case of equal weight, the order is important and the last rule is used.

To make the rule more specific, you can have .menu li.active for the 'default' styling, and .menu li.active:hover for the hovered version. The latter is more specific (31 vs 21) than the first and will always be applied on hovering, not matter the order in the CSS file.

Pseudo-class inheritance understanding

CSS ascertains which selector(s) 'win(s)' following a set of order of precedence rules.

For example, from MDN:

Selector Types The following list of selector types increases by
specificity:

  1. Type selectors (e.g., h1) and pseudo-elements (e.g., ::before).

  2. Class selectors (e.g., .example), attributes selectors (e.g.,
    [type="radio"]) and pseudo-classes (e.g., :hover).

  3. ID selectors (e.g., #example).

So in the example given in the question:

<a href="#" id="id-selector">ok</a>
and CSS:

#id-selector {
color: green;
}

a:any-link {
color: red;
}

The color does not turn to red because the id selector takes precedence, even though the setting for a comes after in the 'cascade'.

Here's a snippet where the color does change (for this example on a hover):

#id-selector {
color: green;
}

a#id-selector:hover {
color: lime;
}

a:hover {
color: red;
}
</style>
<a href="#" id="id-selector">ok</a>

Why does the :link pseudo class break expected CSS specificity rules?

The specification you link to states that a pseudo-class (:link in this case) has higher specificity than an element name. To be precise, using the a-b-c-d format, your three selectors come out as:

a-b-c-d
0 0 1 1
0 0 0 3
0 0 1 3

Your confusion possible comes from your use of the term "pseudo selector" which fails to recognise the distinction between pseudo-classes such as :link and pseudo-elements such as :first-line.



Related Topics



Leave a reply



Submit