Can Type Selectors Be Repeated to Increase Specificity

Can type selectors be repeated to increase specificity?

It is possible to increase the specificity of a selector using type selectors, but not conventionally. The reason for this is explained below, but for those who are simply looking for an alternative, there are two of these. You can either chain :not() pseudo-classes containing type selectors in a single compound selector:

h1                      {} /* 1 type  -> specificity = 0-0-1 */
h1:not(_) {} /* 2 types -> specificity = 0-0-2 */
h1:not(_):not(_) {} /* 3 types -> specificity = 0-0-3 */
h1:not(_):not(_):not(_) {} /* 4 types -> specificity = 0-0-4 */

Or, if you need to support legacy browsers that don't support :not(), you can add redundant type selectors such as html and body to the beginning of a complex selector, although you are far more limited in this case as you may not be able to account for all elements:

h1                {} /* 1 type  -> specificity = 0-0-1 */
body h1 {} /* 2 types -> specificity = 0-0-2 */
html body h1 {} /* 3 types -> specificity = 0-0-3 */
html body tr > td {} /* 4 types -> specificity = 0-0-4, assumes every td is a child of tr */

Needless to say, these are considered specificity hacks; as with all other CSS hacks, use them sparingly, if at all.


A compound selector may only have at most exactly one type selector preceding all other simple selectors. From Selectors 3 (which calls this a sequence of simple selectors):

A sequence of simple selectors is a chain of simple selectors that are not separated by a combinator. It always begins with a type selector or a universal selector. No other type selector or universal selector is allowed in the sequence.

And Selectors 4:

A compound selector is a sequence of simple selectors that are not separated by a combinator. If it contains a type selector or universal selector, that selector comes first in the sequence. Only one type selector or universal selector is allowed in the sequence.

Only type and universal selectors are subject to this rule; you may combine and repeat other simple selectors to increase specificity. Perhaps the spec could have reminded the reader about this in the section on calculating specificity, but I don't think it's absolutely necessary.

The reason for this rule is never stated explicitly, but it is fairly easy to deduce:

  • Remember that a type selector consists of simply an identifier, e.g. h1. This is unlike other simple selectors which have their own distinguishing symbols in the grammar, such as an ID (#), a class (.), a pseudo-class (:), or an attribute selector ([]). You would not be able to have multiple consecutive type selectors without a way to parse them separately.

  • And even if you could chain type selectors, for example if you had another simple selector between them, the only possible use for this would be as a specificity hack, as described in the question, which means you would only be able to use it if all the type selectors were the same; no other combination of type selectors could work.

    This is because Selectors assumes that the document language defines every element to have exactly one element type. For example, in HTML, an h1 is always an h1; it can never be any other type of element. A compound selector asking for an element that is both an h1 as well as a p can never match anything, for the same reason something like [type=text][type=password] can never match anything in a document language that does not support duplicate attributes.

However, with the above points in mind, it is still possible to create a compound selector that contains more than one type selector for specificity — by using the :not() pseudo-class:

  • The specificity of a :not() pseudo-class is equal to its argument. The pseudo-class itself is not counted. This is mentioned in the first link. This means the specificity of :not(h1) is equivalent to h1 — one type selector.

  • Since an element can only be of exactly one type, this means :not() with any other type selector will be a guaranteed match.

  • Since a compound selector may include any number of pseudo-classes, you can repeat the negation as many times as necessary, even if the negations all use the same type selector.

  • And since Selectors doesn't care if a selector makes sense in the context of any particular document language, you can use a type selector that is guaranteed to never match any element in a conforming HTML document as long as it satisfies the Selectors grammar for a type selector. A type selector consists of nothing but a CSS identifier, so any CSS identifier is fair game. Including _.

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.

CSS class repetition to increase specificity

Yes, it is possible and intentionally so. While this is not mentioned in the CSS2 spec, it is explicitly mentioned in the Selectors 3 spec:

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

Therefore browsers must increase the specificity when encountering repeated simple selectors, as long as the selector is valid and applicable. This not only applies to repeated classes, but also applies to repeated IDs, attributes and pseudo-classes.

Given your code, .qtxt.qtxt.qtxt.qtxt.qtxt will have the highest specificity. The other two selectors are equally specific; combinators have no bearing in specificity calculations at all:

/* 5 classes -> specificity = 0-5-0 */
.qtxt.qtxt.qtxt.qtxt.qtxt

/* 2 classes -> specificity = 0-2-0 */
.qtxt.lalgn

/* 2 classes -> specificity = 0-2-0 */
.lalgn .qtxt

Also, the space in your last selector is the descendant combinator; the child combinator is >.

Nesting CSS selectors without increasing specificity

These days, in 2018, this is getting close to possible.

First of all, CSS4 will have a way that allows you to create more specific selectors without increasing specificity:

:where(.special-section) p {
color: red;
}

This will set the paragraph color inside .special-section to red, but with a specificity of 001 (i.e. the same specificity that a plain p selector would have).

The spec still calls this special pseudo-class :something(), but chances are it's going to be called :where(). (Side note: I really want this to be known as the "honey badger selector").

But that's still in the future.

However, there is actually a way to achieve this today, if you don't have to support IE anymore (or are happy with less-than-perfect fallbacks), and that is by using custom properties a.k.a. CSS variables.

So you want this:

.special-section p { color: red; }
.weird-font { color: magenta; }
p { color: green; }

but with the first part having a specificity that's lower than any selector with a class in it. You can do it like this:

.special-section p { --low-specificity-color: red; }
.weird-font { color: magenta; }
p { color: var(--low-specificity-color, green); }

If you run the below snippet in a modern browser, you should notice that the second paragraph is red, because it's in a special section, but the third paragraph is magenta, because it's .weird-font -- even though .weird-font has 010 specificity and .special-section p has 011.

.special-section p { --low-specificity-color: red; }.weird-font        { color: magenta; }p                  { color: var(--low-specificity-color, green); }
<p>This is a paragraph.</p>
<section class="special-section"> <p>This is a paragraph inside a special section.</p> <p class="weird-font">This is a paragraph with a weird font inside a special section.</p></section>
<p class="weird-font">This is a paragraph with a weird font.</p>
<div class="weird-font">This is a div with a weird font.</div>

How to add CSS specificity without relying on parent selectors or using !important?

Increase selector specificity without the need of parents, using :not() + non-existing selectors of desired specificity:

any-tag:not(#bogus_id):not(#bogus_id) {  color: blue; }
#one any-tag, #two any-tag { color: red;}
<div id="one">  <any-tag>blue</any-tag>  <any-tag style="color:orange">orange</any-tag></div>
<div id="two"> <any-tag style="color:green">green</any-tag> <any-tag>blue</any-tag></div>

Programmatically set CSS selector specificity

The specificity of a selector is determined solely by its component simple selectors (and pseudo-element, if any). You cannot change the specificity of a selector without altering the selector itself.

If you're worried about changing the meaning of a selector (or its set of potential matches), there are certain dummy simple selectors that you can add while keeping the semantics intact. In your example, you can increase the specificity of UL OL LI to match UL OL LI.red without actually taking a dependency on an arbitrary class name by either adding an unqualified :root to the beginning of the selector (with a descendant combinator) or adding :nth-child(n) to any one of the three type selectors — both are guaranteed matches, and pseudo-classes are equally specific to class selectors.

Also see my answers to the following questions for more options:

  • Can type selectors be repeated to increase specificity?
  • CSS class repetition to increase specificity

There is no similar way to decrease selector specificity, however, neither programmatically nor by altering the selector (except of course by removing redundant selectors like the aforementioned unqualified :root, :nth-child(n), or repeated type/class selectors).

Increase specificity weight with double selector via Sass

There's a special trick in SASS for doubling specificity using interpolation brackets (more on that here and here) since two &'s next to each other is invalid SASS syntax.

.parent {
&__child {
&#{&} {
color: red;
}
}
}

// compiles to
// .parent__child.parent__child { color: red; }

// You can also do this with deeper nested elements by
// prefacing the interpolation with the ancestor selectors:
.parent {
&__child {
.some .ancestor .elements &#{&} {
color: red;
}
}
}

// compiles to
// .some .ancestor .elements .parent__child.parent__child { color: red; }

For those of you who stumble upon this and use LESS, double &&'s are allowed:

.parent {
&__child {
&& {
color: red;
}
}
}

How can I override a selector with many negations (:not)?

Unfortunately, in Selectors 3, that's the best you can do since :not() only accepts a single simple selector. Having said that, the selector in your overriding rule can be replaced with input.red.red — you don't need more class selectors than attribute selectors unless that rule has to appear earlier in the stylesheet.

This is another problem that Selectors 4 solves with its enhancement of :not(): specificity. In Selectors 4, you will be able to replace all those negations with just one :not() pseudo, effectively decreasing its specificity within your original selector to just one attribute selector. From section 16:

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

Since any attribute selector is equally specific to a class selector, the specificity of any number of lone attribute selectors in a list will never be more than that of just one of them. This allows you to get away with using just one class selector for your override without having to repeat it.

The following works in Safari as a proof-of-concept:

/* 1 type, 2 attributes -> specificity = (0, 2, 1)input:not([type="checkbox"]):not([type="radio"]),   1 type, 1 attribute  -> specificity = (0, 1, 1) */input:not([type="checkbox"], [type="radio"]) {    background-color: blue;}
/* 1 type, 1 class -> specificity = (0, 1, 1) */input.red { background-color: red;}
<input type="checkbox"><input type="radio"><input type="text"><input class="red" type="text">

Stylelint selector-max-specificity: class::placeholder (0,0,1) doesn't but class:focus::placeholder (0,1,1) throws error with 0.2.0 max specificity

Does anyone know why I get the error

Using this calculator you can see that the selector .input-basic__input--active:focus::placeholder has a specificity of 0,2,1, i.e. 2 (pseudo-)classes and 1 (pseudo-)element.

0,2,1 is greater than chosen max specificity of 0,2,0, and the rule correctly reports this.

You should increase your primary option to 0,2,1 if you want to use this selector.

the problem is that when I use wonderful-input::placeholder it doesn't throw me an error

The selector wonderful-input::placeholder has a specificity of 0,0,2, i.e. 2 (pseudo-)elements, which is lower than 0,2,0 so the rule allows it.

can I decrease the specificity on this one?

There is no way to decrease the specificity of the .input-basic__input--active:focus::placeholder selector and have it do the same thing: i.e. style the placeholder pseudo-element of focused elements that have the class input-basic__input--active.

I am trying to reduce CSS specificity in my project by using the stylelint selector-max-specificity

It's an admirable goal. You can also use the other selector-max-* rules, like selector-max-class, selector-max-combinators, selector-max-compound-selectors and so on, to help you do this.

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.



Related Topics



Leave a reply



Submit