How to calculate CSS specificity

Apr 16, 2021css-conceptscss

According to how CSS works, browsers determine which CSS declarations win over the others by cascading in following descending order of priority:

  • Origin and importance: Based on combination of declaration origins (user agent, user, author, transition, animation) and !important annotation. The precedence of the various origins is, in descending order:
1. Transition
2. User agent + `!important`
3. User + `!important`
4. Author + `!important`
5. Animation
6. Author
7. User
8. User agent
  • Specificity: A weight that is applied to a given CSS declaration, determined by the number of each selector type in the matching selector, highest specificity wins.
  • Order of appearance: The last declaration in document wins in case of equal specificity.

Specificity is needed when there’s still conflict after comparing origin and importance. When multiple declarations have equal specificity, the last declaration found in the CSS is applied to the element.

Inheritance is part of defaulting which happens after cascading, so styles for a directly targeted element will always take precedence over inherited styles, regardless of the specificity of the inherited rule.

How to compare specificity

The resulting specificity is not a number but a tuple of numbers like (a,b,c). Specificity tuples are compared by comparing corresponding elements of two tuples using following algorithm:

  • Pad the shorter tuple with zero (0) to the left until both tuples equal in length: (1,0,0,0,0) vs (1,1,1) => (1,0,0,0,0) vs (0,0,1,1,1)
  • Compare the n-th items of both tuple (starting with the zero-th index from left to right). If both are equal, repeat this step with the next item.
  • For two unequal items, the item that is “less than” makes the tuple, that contains it, also “less than” the other tuple.
  • If all items are equal, both tuples are equal.
(1,0,0,0,0) > (1,0,0,0)
(1,0,0,0) > (1,3,3)
(1,0,0) > (0,1,0)
(0,12,1) > (0,1,0)
(0,0,2) > (0,0,1)

How to calculate specificity

(1,0,0,0,0)

The !important value appended a CSS property value is an automatic win with specificity of (1,0,0,0,0). It overrides even inline styles from the markup. The only way an !important value can be overridden is with another !important rule declared later in the CSS and with equal or great specificity value otherwise.

Using !important considers a bad practice and should be avoided because it makes debugging more difficult by breaking the natural cascading in your stylesheets.

(1,0,0,0)

Inline styles added to an element (e.g., style=“font-weight: bold;” ) always overwrite any styles in external stylesheets, and thus can be thought of as having the specificity (1,0,0,0).

Both inline styles and !important are considered very bad practice, but sometimes you need the latter to override the former.

(a,b,c)

  • 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)
  • Selectors inside the negation pseudo-class :not(x) are counted like any other, but the negation itself does not count as a pseudo-class.
  • Universal selector (*), combinators (+, >, ~, ' ', ||) have no effect on specificity.
  • Repeated occurrences of the same simple selector are allowed and do increase specificity.
*               /* a=0 b=0 c=0 -> specificity = (0,0,0) */
li              /* a=0 b=0 c=1 -> specificity = (0,0,1) */
ul li           /* a=0 b=0 c=2 -> specificity = (0,0,2) */
ul ol+li        /* a=0 b=0 c=3 -> specificity = (0,0,3) */
h1 + *[rel=up]  /* a=0 b=1 c=1 -> specificity = (0,1,1) */
ul ol li.red    /* a=0 b=1 c=3 -> specificity = (0,1,3) */
li.red.level    /* a=0 b=2 c=1 -> specificity = (0,2,1) */
#x34y           /* a=1 b=0 c=0 -> specificity = (1,0,0) */
#s12:not(foo)   /* a=1 b=0 c=1 -> specificity = (1,0,1) */