CSS Selector Specificity: The Art of Cascade and Weight â
The Concept of Cascade â
"Cascading" in CSS (Cascading Style Sheets) is its core mechanism. Imagine multiple waterfalls pouring down from different heights, eventually converging into a single pool â this is the cascade principle of CSS. When multiple rules apply to the same element, the browser needs to decide which rule ultimately takes effect.
This decision process is based on three key factors:
- Origin: Where the style comes from (browser defaults, author styles, user styles)
- Specificity: How specific the selector is
- Order: Position in the code
Specificity Calculation Rules â
Specificity is the core of the CSS priority system. Each selector has a specificity value, typically represented as a four-digit number: (a, b, c, d)
Calculation Rules â
(a, b, c, d)- a: Inline styles (1000)
- b: Number of ID selectors (100)
- c: Number of class selectors, attribute selectors, pseudo-classes (10)
- d: Number of element selectors, pseudo-elements (1)
Let's look at specific examples:
/* (0, 0, 0, 1) - Weight: 1 */
p {
color: black;
}
/* (0, 0, 1, 0) - Weight: 10 */
.text {
color: blue;
}
/* (0, 0, 1, 1) - Weight: 11 */
p.text {
color: green;
}
/* (0, 1, 0, 0) - Weight: 100 */
#header {
color: red;
}
/* (0, 1, 1, 1) - Weight: 111 */
#header p.text {
color: purple;
}
/* (1, 0, 0, 0) - Weight: 1000 */
/* Inline style */
<p style="color: orange;">Text</p>Complex Selector Calculations â
/* (0, 0, 1, 2) = 12 */
/* 1 class + 2 elements */
div p.highlight {
color: blue;
}
/* (0, 0, 2, 1) = 21 */
/* 2 classes + 1 element */
.container .text.bold {
color: red;
}
/* (0, 1, 1, 2) = 112 */
/* 1 ID + 1 class + 2 elements */
#main div.content p {
color: green;
}
/* (0, 0, 3, 0) = 30 */
/* 3 classes */
.nav.primary.active {
color: purple;
}
/* (0, 0, 2, 0) = 20 */
/* 1 attribute selector + 1 pseudo-class */
input[type="text"]:focus {
border-color: blue;
}Special Rules â
/* Universal selector * has weight 0 */
* {
margin: 0; /* (0, 0, 0, 0) */
}
/* Combinators (>, +, ~, space) don't add weight */
div > p {
/* (0, 0, 0, 2) */
color: blue;
}
.parent > .child {
/* (0, 0, 2, 0) */
color: red;
}
/* :not() itself doesn't count, but content inside does */
div:not(.special) {
/* (0, 0, 1, 1) - 1 element + 1 class */
color: green;
}
/* :is() :where() difference */
:is(#header, .nav) p {
/* (0, 1, 0, 1) - Takes highest weight of ID */
color: blue;
}
:where(#header, .nav) p {
/* (0, 0, 0, 1) - :where() weight is always 0 */
color: red;
}Priority Rules â
When specificity is the same, later rules override earlier ones:
/* Both have same weight (0, 0, 1, 0) = 10 */
.text {
color: blue;
}
.text {
color: red; /* This takes effect */
}Origin Priority â
Priority order from highest to lowest:
1. User agent important declarations (browser !important)
2. User important declarations (user-defined !important)
3. Author important declarations (developer !important)
4. Author normal declarations (developer normal styles)
5. User normal declarations (user-defined normal styles)
6. User agent normal declarations (browser default styles)In actual development, we mainly focus on author style priority:
/* Normal declaration */
p {
color: blue;
}
/* !important declaration - highest priority */
p {
color: red !important; /* This takes effect */
}
/* Even with higher specificity, cannot override !important */
#content .text p {
color: green; /* Doesn't take effect */
}Proper Use of !important â
!important is the "nuclear weapon" in the priority system and should be used carefully:
/* Not recommended: Abusing !important */
.button {
background: blue !important;
color: white !important;
padding: 10px !important;
}
/* Recommended: Only use when necessary */
.utility-class {
display: none !important; /* Ensure utility class always takes effect */
}
/* Override third-party library styles */
.third-party-widget .custom-override {
color: red !important;
}Reasonable Use Cases:
- Override inline styles
- Override third-party library styles
- Create utility classes that must always take effect
- Avoid risks when refactoring legacy code
Cases to Avoid:
- As the first choice for resolving priority conflicts
- In regular component styles
- Multiple consecutive
!importantoverriding each other
Inheritance Mechanism â
Some CSS properties inherit from parent to child elements, but inherited properties have no specificity (even lower than 0):
/* Inheritable properties */
body {
color: #333; /* Inherited */
font-family: Arial; /* Inherited */
line-height: 1.6; /* Inherited */
}
/* Non-inheritable properties */
div {
border: 1px solid #ddd; /* Not inherited */
padding: 20px; /* Not inherited */
margin: 10px; /* Not inherited */
}
/* Even with 0 specificity, overrides inherited value */
p {
color: blue; /* (0,0,0,1) overrides inherited color */
}
/* Force inheritance */
.child {
color: inherit; /* Explicitly inherit parent's color */
border: inherit; /* Explicitly inherit parent's border */
}
/* Restore initial value */
.reset {
all: initial; /* Reset all properties to initial value */
}
/* Restore to unset state */
.unset {
all: unset; /* Inheritable properties inherit, non-inheritable use initial value */
}Common Inheritable Properties:
- Text-related:
color,font-*,line-height,text-align,text-indent - List-related:
list-style-* - Others:
cursor,visibility
Common Non-inheritable Properties:
- Box model:
width,height,margin,padding,border - Positioning:
position,top,left - Layout:
display,float - Background:
background-*
Practical Case Studies â
Case 1: Resolving Style Conflicts â
<div id="container" class="wrapper">
<p class="text highlight">Hello World</p>
</div>/* Analyze weights */
p {
color: black; /* (0, 0, 0, 1) = 1 */
}
.text {
color: blue; /* (0, 0, 1, 0) = 10 */
}
.wrapper p {
color: green; /* (0, 0, 1, 1) = 11 */
}
#container p {
color: red; /* (0, 1, 0, 1) = 101 */
}
.wrapper .text {
color: purple; /* (0, 0, 2, 0) = 20 */
}
#container .text.highlight {
color: orange; /* (0, 1, 2, 0) = 120 - Finally takes effect */
}Case 2: Reducing Specificity â
Using :where() to reduce weight:
/* High weight base style - hard to override */
#app .button {
background: blue; /* (0, 1, 1, 0) = 110 */
}
/* Optimized: Use :where() to reduce weight */
:where(#app) .button {
background: blue; /* (0, 0, 1, 0) = 10 */
}
/* Now easy to override */
.button.primary {
background: red; /* (0, 0, 2, 0) = 20 - Can override */
}Case 3: Modular Style Architecture â
/* Base styles - low weight */
.btn {
padding: 10px 20px;
border-radius: 4px;
}
/* Variant styles - medium weight */
.btn.btn-primary {
background: blue;
color: white;
}
.btn.btn-secondary {
background: gray;
color: white;
}
/* State styles - high weight, use :is() for flexibility */
.btn:is(:hover, :focus, :active) {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
/* Special scenarios */
.sidebar .btn {
width: 100%;
}Case 4: Handling Third-party Library Conflicts â
/* Bootstrap's styles */
.btn-primary {
background-color: #007bff; /* Bootstrap default blue */
}
/* Method 1: Increase specificity */
.my-app .btn-primary {
background-color: #e74c3c; /* Custom red */
}
/* Method 2: Use !important (not recommended, but sometimes necessary) */
.btn-primary {
background-color: #e74c3c !important;
}
/* Method 3: Use custom class to override */
.btn-custom {
background-color: #e74c3c;
}Debugging Techniques â
Chrome DevTools â
/* In browser developer tools: */
/*
1. Crossed-out styles = overridden by higher priority
2. Gray styles = not applicable to this element
3. Check "Computed" tab to see final effective values
*/Specificity Calculator â
// Simple specificity calculation function
function calculateSpecificity(selector) {
const ids = (selector.match(/#/g) || []).length;
const classes = (selector.match(/\./g) || []).length;
const attrs = (selector.match(/\[/g) || []).length;
const pseudoClasses =
(selector.match(/:/g) || []).length - (selector.match(/::/g) || []).length;
const elements = selector.split(/[#.\[\s>+~:]/).length - 1;
return {
ids: ids * 100,
classes: (classes + attrs + pseudoClasses) * 10,
elements: elements * 1,
total: ids * 100 + (classes + attrs + pseudoClasses) * 10 + elements,
};
}
// Usage
console.log(calculateSpecificity("#header .nav li.active"));
// { ids: 100, classes: 20, elements: 2, total: 122 }Best Practices â
1. Keep Low Specificity â
/* Not recommended: Overly high specificity */
#page #content .sidebar .widget .title h3 {
color: blue;
}
/* Recommended: Concise selectors */
.widget-title {
color: blue;
}2. Use BEM Naming Convention â
/* BEM (Block Element Modifier) reduces specificity conflicts */
.card {
} /* Block */
.card__title {
} /* Element */
.card__title--large {
} /* Modifier */
.card--featured {
} /* Modifier */3. Avoid ID Selectors â
/* Not recommended */
#header {
}
/* Recommended */
.header {
}4. Layered Style Architecture â
/* Layer 1: Reset and base (weight: 1-10) */
* {
box-sizing: border-box;
}
body {
font-family: Arial;
}
/* Layer 2: Layout (weight: 10-20) */
.container {
max-width: 1200px;
}
.grid {
display: grid;
}
/* Layer 3: Components (weight: 10-30) */
.button {
padding: 10px;
}
.card {
border-radius: 8px;
}
/* Layer 4: Utility classes (weight: 10-20) */
.text-center {
text-align: center;
}
.mt-4 {
margin-top: 1rem;
}
/* Layer 5: States (weight: 20-40) */
.is-active {
background: blue;
}
.has-error {
border-color: red;
}5. Document Weight Strategy â
/**
* Weight rules:
* 0-10: Base styles and resets
* 10-30: Component styles
* 30-50: States and variants
* 100+: Avoid using, only for overriding third-party libraries
* !important: Only for utility classes
*/Summary â
Understanding CSS specificity is key to writing maintainable stylesheets. By properly controlling selector specificity, you can create flexible, easily extensible style systems and avoid falling into specificity wars.
Key Points:
- Cascade Mechanism: CSS determines final styles through origin, specificity, and order
- Specificity Calculation: (a, b, c, d) system
- Inline styles: 1000
- ID selectors: 100
- Class/attribute/pseudo-class: 10
- Element/pseudo-element: 1
- Special Selectors:
*weight is 0, combinators don't add weight,:not()doesn't count but content does,:where()weight is 0 - !important: Use carefully, only for utility classes or overriding third-party libraries
- Inheritance Mechanism: Some properties inherit, inherited values have lowest priority
- Debugging Techniques: Browser developer tools, specificity calculators
- Best Practices: Keep low specificity, use BEM, avoid IDs, layered architecture, document strategy