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 _
.