Understanding the CSS Specificity Algorithm

Introduction
Have you ever wondered how CSS styles an HTML element? How does CSS choose between multiple conflicting styles and apply it? Well to answer that the CSS follows an Specificity Algorithm to decide which style to apply on which HTML element and what to do in case of conflicts.
CSS specificity determines which property-value pairs from the competing CSS declarations are applied to the element. When multiple rules target the same property ; the cascade origin, specificity and order of appearance determine the appearance.
Cascade Origin
The cascade origin helps resolve conflicts by prioritizing styles from different origins. There are basically three types of origins which are:
User Agent Styles : These are the default browser styles.
Author Styles : The style defined by the developer in his/her CSS file.
User Styles : The styles applied by the user (eg in browser settings).
Cascade Layer
Cascade Layers group related styles, helping developers organize and control priority.
They are important because the order in which layers are defined matters. A declaration in a layer defined later will override one in an earlier layer, even if both have the same specificity.
Specificity
Specificity is calculated using four categories:
Inline Style : This represents the style attribute in the HTML element. Denoted by ‘a’ this has the highest priority.
IDs : The ID given to an HTML elements comes next in priority. It is denoted by ‘b’.
Classes, attributes and pseudo-classes : They are less specific then inline CSS or IDs. They are denoted by ‘c’.
Element selectors and pseudo-elements : They have the least priority. They are denoted by ‘d’.
Specificity is represented as a four-value tuple ( a, b, c, d ), where:
a: Inline styles (always 1 or 0)
b: Number of ID selectors
c: Number of class selectors, attributes, or pseudo-classes
d: Number of type selectors or pseudo-elements
Comparison
When comparing two rules; compare a values first then b, c, and finally d. The rule with the higher value in the first differing category wins.
Some General Rules
Inline styles override everything except !important.
ID selectors are more specific than class selectors.
Class selectors, attributes, and pseudo-classes are more specific than type selectors.
Type Selectors are least specific.
Examples
Here are examples illustrating how inline styles, IDs, and classes influence CSS specificity.
Example 1
<div class="box" id="unique" style="color: green;">Hello, World!</div>
.box {
color: blue; /* Specificity: (0, 0, 1, 0) */
}
#unique {
color: red; /* Specificity: (0, 1, 0, 0) */
}
Result : The text will be green
Reason: Inline styles ( style = “color: green“ ) have the highest specificity (1, 0, 0, 0) and override both the class and ID selectors.
Example 2
<div class="box" id="unique">Hello, World!</div>
.box {
color: blue; /* Specificity: (0, 0, 1, 0) */
}
#unique {
color: red; /* Specificity: (0, 1, 0, 0) */
}
Result: The text will be red.
Reason: The ID selector #unique has higher specificity than the class selector .box
Example 3
<div class="box" style="color: orange;">Hello, World!</div>
.box {
color: blue; /* Specificity: (0, 0, 1, 0) */
}
Result: The text will be orange.
Reason: Inline styles ( style =”color : orange” ) have higher specificity ( 1,0,0,0 ) than the class selector.
Example 4
<div class="box">Hello, World!</div>
.box {
color: blue; /* Specificity: (0, 0, 1, 0) */
}
.box {
color: red; /* Specificity: (0, 0, 1, 0) */
}
Result: The text will be red.
Reason: Both declarations have equal specificity ( 0, 0, 1, 0 ). The later rule in the CSS overrides the earlier one.
Example 5
<div id="unique" class="box" style="color: yellow;">Hello, World!</div>
.box {
color: blue; /* Specificity: (0, 0, 1, 0) */
}
#unique {
color: red; /* Specificity: (0, 1, 0, 0) */
}
Result: The text will be yellow.
Reason: Inline styles have the highest specificity and override both the ID and class selector.
Example 6
<div id="unique" class="box" style="color: orange;">Hello, World!</div>
.box {
color: blue !important; /* Overrides other declarations */
}
#unique {
color: red; /* Specificity: (0, 1, 0, 0) */
}
Result: The text will be blue.
Reason: The !important declaration in .box overrides both the ID and inline styles, even though its specificity is lower.
Best Practices For Avoiding Specificity Wars
To write maintainable and conflict-free CSS, follow these practices:
Use Class Selectors over ID Selectors : Class Selectors are more flexible and reusable than IDs. Since IDs have higher specificity, relying on them can cause conflicts and require unnecessary overrides.
Avoid Over-Nesting : Over-Nesting increases specificity unnecessarily, making it harder to override styles.
Keep Styles Modular Using Cascade Layer as they allow you to group and prioritize styles logically.
Clear documentation and CSS Architecture : A well - documented CSS improves team collaboration and reduces conflicts.
An Advanced Example
Scenario:
A webpage has multiple sources of CSS styles: user agent styles, author styles, and user styles. The CSS also uses cascade layers and contains conflicting property declarations with varying specificity.
<div class="box primary" id="unique-box" style="color: purple;">Advanced Example</div>
/* User agent styles */
div {
color: black; /* Specificity: (0, 0, 0, 1) */
}
/* Author styles - Base Layer */
@layer base {
.box {
color: blue; /* Specificity: (0, 0, 1, 0) */
}
}
/* Author styles - Theme Layer */
@layer theme {
#unique-box {
color: red; /* Specificity: (0, 1, 0, 0) */
}
}
/* User styles (in browser settings) */
@layer user-settings {
.primary {
color: green !important; /* Specificity: (0, 0, 1, 0), but with !important */
}
}
Resolution Breakdown
Inline Styles:
- The style= “color: purple” in the HTML has the highest specificity ( 1,0,0,0 ) and takes precedence.
User-Defined Styles with !important :
- The !important declaration in the user layer (color : green !important) overrides all other rules except inline styles.
Cascade Layers:
- If !important and inline styles were not present, the theme layer would override the base layer because it appears later in the cascade order.
Result:
- The element's color will be purple due to inline styles.
Conclusion
Through this blog and examples you understood how the cascade, specificity, and order of appearance interact is crucial for predictable results in determining styling on an HTML element.