CSS specificity is the algorithm browsers use to determine which CSS rules apply when multiple rules target the same element. Understanding specificity is crucial for writing maintainable CSS and avoiding style conflicts.
In this tutorial, we'll explore CSS specificity rules, how specificity is calculated, common specificity issues, and best practices for managing specificity in your stylesheets.
Understanding Specificity
Specificity is a score that determines which CSS rule should be applied when multiple rules target the same element with conflicting properties.
Specificity Basics
/* Higher specificity wins */
#header {
color: blue; /* Higher specificity */
}
.header {
color: red; /* Lower specificity */
}
/* Result: #header wins (blue color) */
/* Same specificity: last rule wins */
.header {
color: red;
}
.header {
color: blue; /* Same specificity, wins */
}
Specificity Hierarchy
/* Specificity levels (highest to lowest):
1. Inline styles
2. ID selectors
3. Class selectors, attribute selectors, pseudo-classes
4. Element selectors, pseudo-elements
5. Universal selector
*/
/* 1. Inline styles (highest) */
<div style="color: red;">Text</div>
/* specificity: 1,0,0,0 */
/* 2. ID selectors */
#unique {
color: blue;
}
/* specificity: 0,1,0,0 */
/* 3. Class selectors */
.class {
color: green;
}
/* specificity: 0,0,1,0 */
/* 4. Element selectors */
div {
color: purple;
}
/* specificity: 0,0,0,1 */
/* 5. Universal selector (lowest) */
* {
color: orange;
}
/* specificity: 0,0,0,0 */
Calculating Specificity
Specificity is calculated as a four-part value: (inline, ID, class, element).
Specificity Calculation Examples
/* Element selector */
div { /* specificity: 0,0,0,1 */ }
/* Class selector */
.class { /* specificity: 0,0,1,0 */ }
/* ID selector */
#id { /* specificity: 0,1,0,0 */ }
/* Combined selectors */
div.class { /* specificity: 0,0,1,1 */ }
#id .class { /* specificity: 0,1,1,0 */ }
#id div.class { /* specificity: 0,1,1,1 */ }
/* Multiple classes */
.class1.class2 { /* specificity: 0,0,2,0 */ }
.class1.class2.class3 { /* specificity: 0,0,3,0 */ }
/* Attribute selectors */
[type="text"] { /* specificity: 0,0,1,0 */ }
input[type="text"] { /* specificity: 0,0,1,1 */ }
/* Pseudo-classes */
:hover { /* specificity: 0,0,1,0 */ }
li:first-child { /* specificity: 0,0,1,1 */ }
/* Pseudo-elements */
::before { /* specificity: 0,0,0,1 */ }
p::first-line { /* specificity: 0,0,0,2 */ }
Complex Selector Specificity
/* Complex specificity calculations */
/* Example 1 */
#nav ul.menu li a:hover {
/* specificity: 0,1,3,1 */
/* 1 ID, 3 classes/pseudo-classes, 1 element */
}
/* Example 2 */
.header .nav .menu .item a {
/* specificity: 0,0,4,1 */
/* 4 classes, 1 element */
}
/* Example 3 */
body div#main.content p.intro span.highlight {
/* specificity: 0,1,2,3 */
/* 1 ID, 2 classes, 3 elements */
}
/* Example 4 */
form#contact input[type="email"]:required:focus {
/* specificity: 0,1,3,1 */
/* 1 ID, 3 classes/pseudo-classes, 1 element */
}
Specificity Rules
Understanding the rules that govern specificity calculations.
Rule 1: More Specific Wins
/* Higher specificity wins regardless of order */
#header {
color: blue; /* Wins - higher specificity */
}
.header {
color: red; /* Loses - lower specificity */
}
/* Even if declared later */
.header {
color: red;
}
#header {
color: blue; /* Still wins */
}
Rule 2: Same Specificity: Source Order
/* Same specificity: last declaration wins */
.button {
color: red;
}
.button {
color: blue; /* Wins - same specificity, later */
}
/* In different stylesheets */
/* stylesheet1.css */
.button { color: red; }
/* stylesheet2.css (loaded after) */
.button { color: blue; /* Wins */ }
Rule 3: !important Overrides Specificity
/* !important overrides normal specificity */
.button {
color: red !important; /* Wins despite lower specificity */
}
#unique-button {
color: blue; /* Loses to !important */
}
/* !important vs !important: specificity matters again */
.button {
color: red !important;
}
#unique-button {
color: blue !important; /* Wins - higher specificity */
}
Common Specificity Issues
Understanding common specificity problems helps you avoid them in your CSS.
Specificity Wars
/* Problem: Escalating specificity */
.header {
color: blue;
}
.page .header {
color: red;
}
.content .page .header {
color: green;
}
.main .content .page .header {
color: purple;
}
/* Solution: Use more specific classes or IDs */
.header-primary {
color: blue;
}
.header-secondary {
color: red;
}
Overly Specific Selectors
/* Problem: Too specific */
body div#main.container section.content div.wrapper p.text span.highlight {
color: red;
/* specificity: 0,1,5,5 */
/* Hard to override */
}
/* Solution: Use meaningful classes */
.highlight {
color: red;
/* specificity: 0,0,1,0 */
/* Easy to override */
}
Unexpected Specificity
/* Problem: Unexpected specificity wins */
.form input[type="text"] {
border: 1px solid #ccc;
/* specificity: 0,0,2,1 */
}
.text-input {
border: 2px solid blue;
/* specificity: 0,0,1,0 */
/* Loses to more specific selector */
}
/* Solution: Increase specificity or use !important */
.form .text-input {
border: 2px solid blue;
/* specificity: 0,0,2,0 */
/* Now wins */
}
Managing Specificity
Strategies for managing specificity effectively in your stylesheets.
CSS Methodologies
/* BEM Methodology */
.card { /* Block */ }
.card__header { /* Element */ }
.card__title { /* Element */ }
.card--featured { /* Modifier */ }
/* Consistent specificity */
.card { /* specificity: 0,0,1,0 */ }
.card__header { /* specificity: 0,0,1,0 */ }
.card--featured { /* specificity: 0,0,1,0 */ }
/* OOCSS Approach */
.button { /* Base class */ }
.button-primary { /* Extension */ }
.button-large { /* Extension */ }
/* Consistent specificity levels */
.button { /* specificity: 0,0,1,0 */ }
.button-primary { /* specificity: 0,0,1,0 */ }
.button-large { /* specificity: 0,0,1,0 */ }
Flat Specificity
/* Keep specificity low and consistent */
.bad {
.container .sidebar .widget .title {
font-size: 18px;
/* specificity: 0,0,4,0 */
}
}
.good {
.widget-title {
font-size: 18px;
/* specificity: 0,0,1,0 */
}
}
/* Use utility classes for overrides */
.text-large {
font-size: 18px !important;
/* specificity: 0,0,1,0 + !important */
}
Specificity Strategies
/* Strategy 1: ID sparingly */
#only-for-js-hooks {
/* Use IDs only for JavaScript hooks */
/* Not for styling */
}
/* Strategy 2: Class-based styling */
.component {
/* Use classes for styling */
}
/* Strategy 3: Specificity mapping */
/* Level 1: Base styles */
.element { /* specificity: 0,0,0,1 */ }
/* Level 2: Components */
.component { /* specificity: 0,0,1,0 */ }
/* Level 3: Modifiers */
.component.modifier { /* specificity: 0,0,2,0 */ }
/* Level 4: States */
.component.is-active { /* specificity: 0,0,2,0 */ }
/* Level 5: Overrides */
.override { /* specificity: 0,0,1,0 + !important */ }
Specificity and CSS Architecture
How specificity fits into different CSS architectures and methodologies.
ITCSS Architecture
/* ITCSS (Inverted Triangle CSS) */
/* 1. Settings - No specificity */
:root {
--color-primary: blue;
}
/* 2. Tools - No specificity */
@function spacing($multiplier) {
@return $multiplier * 1rem;
}
/* 3. Generic - Low specificity */
* {
box-sizing: border-box;
}
/* 4. Elements - Low specificity */
html {
font-size: 16px;
}
body {
margin: 0;
}
/* 5. Objects - Medium specificity */
.o-media {
display: block;
}
/* 6. Components - Medium specificity */
.c-card {
padding: 1rem;
}
/* 7. Trumps - High specificity */
.u-mt-0 {
margin-top: 0 !important;
}
Atomic CSS
/* Atomic CSS - Flat specificity */
.bg-blue { background-color: blue; }
.text-white { color: white; }
.p-4 { padding: 1rem; }
.m-2 { margin: 0.5rem; }
/* All have same specificity: 0,0,1,0 */
/* No specificity conflicts */
/* Easy to override with !important if needed */
Functional CSS
/* Functional CSS - Predictable specificity */
.flex { display: flex; }
.flex-col { flex-direction: column; }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; }
/* Consistent low specificity */
/* Composable without conflicts */
Specificity Debugging
Tools and techniques for debugging specificity issues.
Browser DevTools
/* In DevTools:
1. Select element
2. Check Styles panel
3. See specificity indicators
4. Crossed out = overridden
5. Check Computed panel for final values
*/
/* Debug classes */
.specificity-debug {
/* Add debugging info */
}
.specificity-debug::before {
content: "Specificity: " attr(data-specificity);
position: absolute;
top: -20px;
left: 0;
background: yellow;
padding: 2px 5px;
font-size: 10px;
}
Specificity Calculators
/* Manual specificity calculation */
/* Selector: #nav ul.menu li a:hover */
/* Breakdown:
- #nav: 0,1,0,0 (ID)
- ul: 0,0,0,1 (element)
- .menu: 0,0,1,0 (class)
- li: 0,0,0,1 (element)
- a: 0,0,0,1 (element)
- :hover: 0,0,1,0 (pseudo-class)
/* Total: 0,1,2,3 */
/* Online tools:
- CSS Specificity Calculator
- Specificity Battle
- CSS Specificity Graph
*/
Common Debugging Scenarios
/* Scenario 1: Style not applying */
.problem {
.button { color: red; }
#submit { color: blue; }
}
/* Check: #submit has higher specificity */
/* Scenario 2: Unexpected style */
.problem {
* { color: black !important; }
.highlight { color: yellow; }
}
/* Check: !important overrides everything */
/* Scenario 3: Inconsistent styling */
.problem {
.nav a { color: blue; }
.sidebar .nav a { color: red; }
}
/* Check: More specific selector wins */
Specificity Best Practices
Following best practices helps maintain manageable CSS.
Keep Specificity Low
/* Bad: High specificity */
.header .nav .menu .item .link {
color: blue;
/* specificity: 0,0,4,1 */
}
/* Good: Low specificity */
.nav-link {
color: blue;
/* specificity: 0,0,1,0 */
}
/* Use BEM for consistency */
.nav__link {
color: blue;
/* specificity: 0,0,1,0 */
}
Use IDs Sparingly
/* Bad: IDs for styling */
#header { color: blue; }
#nav { background: red; }
#content { padding: 20px; }
/* Good: Classes for styling */
.header { color: blue; }
.nav { background: red; }
.content { padding: 20px; }
/* IDs for JavaScript only */
#js-header { /* JavaScript hook */ }
#js-nav { /* JavaScript hook */ }
Document Specificity Strategy
/*
* Specificity Strategy:
*
* Base elements: 0,0,0,1
* Utility classes: 0,0,1,0
* Component classes: 0,0,1,0
* Component modifiers: 0,0,2,0
* Component states: 0,0,2,0
* Layout classes: 0,0,1,0
* Overrides: 0,0,1,0 + !important
*/
/* Follow the documented strategy */
.component {
/* Base: 0,0,1,0 */
}
.component--modifier {
/* Modifier: 0,0,2,0 */
}
.component.is-state {
/* State: 0,0,2,0 */
}
Advanced Specificity Concepts
Advanced concepts for mastering CSS specificity.
Specificity and Performance
/* Performance considerations */
/* Efficient: Low specificity */
.button {
color: blue;
}
/* Less efficient: High specificity */
.header .nav .menu .item .button {
color: blue;
}
/* Very inefficient: Universal selector */
* .button {
color: blue;
}
/* Optimize for both readability and performance */
.nav-button {
color: blue;
}
Specificity in CSS-in-JS
// CSS-in-JS often handles specificity automatically
const Button = styled.button`
color: blue;
/* Generates unique class with low specificity */
`;
const SpecificButton = styled.button`
color: red !important;
/* Uses !important for overrides */
`;
Specificity and Shadow DOM
/* Shadow DOM encapsulation */
:host {
/* Host selector */
}
:host(.active) {
/* Host with class */
}
::slotted(.content) {
/* Slotted content */
}
/* Shadow DOM has its own specificity scope */
/* Doesn't conflict with outside styles */
Summary
Key Takeaways
- Specificity determines which CSS rule applies when conflicts occur
- Calculated as (inline, ID, class, element)
- Higher specificity wins regardless of source order
- !important overrides normal specificity rules
- Keep specificity low and consistent
Best Practices
- Use classes for styling, IDs for JavaScript
- Follow CSS methodologies for consistency
- Avoid overly specific selectors
- Use !important sparingly and purposefully
- Document your specificity strategy
Common Pitfalls
- Escalating specificity wars
- Overusing IDs for styling
- Not understanding specificity hierarchy
- Relying too heavily on !important
- Creating overly complex selectors