Why does it matter ?
When writing new styles for a functioning website, it is common for individuals to override CSS styles above the existing ones. For example, if one aims to emphasize the <h2>
heading on the FAQ card below, it is likely necessary to override its style settings to increase the font-weight
and font-size
values.
When a specific HTML element, such as an <h2>
, already possesses predefined styles, it becomes necessary to craft new CSS rules that are sufficiently robust to supersede the current styling in effect. An example of this scenario is the CSS rule .slide_content .slide_caption a h2
(Rule-A
)which is being applied to the upper FAQ card (this can be observed within the style panel situated on the right side of DevTools in Chrome)
Upon adding a rule utilizing .slide__content h2
as the selector (Rule-B
), I adjusted the style to include a font-size of 20px and font-weight of bold. However, it appears that this new rule is not superseding the original one. Subsequently, when implementing an alternative rule with a more potent selector .slide__content .slide__caption a img + h2
while maintaining the same style attributes (Rule-C
), it effectively overrides the initial selector.
Why is the case that:
- Styles in
Rule-B
cannot override styles inRule-A
- Styles in
Rule-C
can override styles inRule-A
This is because:
- Specificity of selector in
Rule-B
is smaller than that ofRule-A
Rule-B
’s selector.slide_content h2
have specificity of(0,0,1,1)
Rule-A
’s selector.slide_content .slide_caption a h2
have specificity of(0,0,2,2)
- Specificity of selector in
Rule-C
is greater than that ofRule-A
Rule-C
’s selector.slide_content .slide_caption a img + h2
have specificity of(0,0,2,3)
Rule-A
’s selector.slide_content .slide_caption a h2
have specificity of(0,0,2,2)
I hope this example has made clear the importance of understanding the concept of “selector specificity”, learning the working mechnism behind the selector specificity will help you write css rules that is both predictable and consistent, and be more efficient in debugging.
Calculating Selector’s Specificty
Basic Rules
A selector’s specificity is calculated as follows:
- (special case) if the style is applied inline of the component:
- add
(1, 0, 0, 0)
to the specificity
- add
- For each id-value (#id) found in the selector of the css rule:
- add
(0, 1, 0, 0)
to the specificity
- add
- For each class (or pseudo-class, or attribute selector) found in the selector of the rule:
- add
(0, 0, 1, 0)
to the specificity
- add
- For each element reference found in the selector of the rule:
- add
(0, 0, 0, 1)
to the specificity
- add
(* some post you will find online may choose to ignore the first digit of the specificity, because when the style is applied inline of the component, they are always taken in the highest priority, it will alwasy override any style writting in the <style>
tag or css files.)
Examples for the above:
inline
<div style=" ...
=>(1, 0, 0, 0)
<img style=" ...
=>(1, 0, 0, 0)
<h1 style=" ...
=>(1, 0, 0, 0)
<span style=" ...
=>(1, 0, 0, 0)
#id
#footer-acknoledgement {...
=>(0, 1, 0, 0)
#paragraph-card-link-1 {...
=>(0, 1, 0, 0)
-#search input {...
=>(0, 1, 0, 1)
-#search .s-box:focus {...
=>(0, 1, 2, 0)
class, peseudo-class, attribute
.container {...
=>(0, 0, 1, 0)
.container:hover{...
=>(0, 0, 2, 0)
.box[data-id="a9"]{...
=>(0, 0, 2, 0)
div:focus-within{...
=>(0, 0, 1, 1)
.box[data-id="a9"]:hover{...
=>(0, 0, 3, 0)
element reference
ul {...
=>(0, 0, 0, 1)
ul ol a {...
=>(0, 0, 0, 3)
#footer ul.container{...
=>(0, 1, 1, 1)
div:focus-within{...
=>(0, 0, 1, 1)
Special Rules
There are some special rules
- The universal selector (*) adds no specificity.
- The pseudo-class
:not
,:is
,:has
adds no specificity, only what’s inside them matter. - The pseudo-class
:where
adds no specificity, even what’s inside it doesn’t matter. - The pseudo-elements (e.g.
:nth-of-type
,:nth-child
) add(0,0,0,1)
to the specificity. (unlike psuedo-class) - The !important at the end of the style adds
(9,0,0,0)
to the specificity. (it will at highest priority)
Example for the above:
html * nav ul li {...
=>(0, 0, 0, 4)
nav ul li:not(.hidden){...
=>(0, 0, 1, 3)
nav ul li:where(.show,.highlight){...
=>(0, 0, 0, 3)
nav ul li:last-of-type{...
=>(0, 0, 0, 4)
nav ul li{ margin:10px !important; ...
=>(9, 0, 0, 3)