Specificity determines which CSS rules apply when multiple rules target the same element. Master it to write predictable, maintainable styles.
Specificity Calculation#
1/* Specificity is calculated as: (inline, ID, class, type) */
2
3/* (0,0,0,1) - Type selector */
4p { color: black; }
5
6/* (0,0,1,0) - Class selector */
7.text { color: blue; }
8
9/* (0,0,1,1) - Class + type */
10p.text { color: green; }
11
12/* (0,1,0,0) - ID selector */
13#header { color: red; }
14
15/* (0,1,1,0) - ID + class */
16#header.active { color: purple; }
17
18/* (0,1,1,1) - ID + class + type */
19div#header.active { color: orange; }
20
21/* (1,0,0,0) - Inline style (highest) */
22/* <div style="color: pink;"> */Selector Weights#
1/* Universal selector: (0,0,0,0) */
2* { margin: 0; }
3
4/* Type selectors: (0,0,0,1) each */
5div { } /* 0,0,0,1 */
6div p { } /* 0,0,0,2 */
7div p span { } /* 0,0,0,3 */
8
9/* Class, attribute, pseudo-class: (0,0,1,0) each */
10.button { } /* 0,0,1,0 */
11[type="text"] { } /* 0,0,1,0 */
12:hover { } /* 0,0,1,0 */
13.button.primary { } /* 0,0,2,0 */
14.button:hover { } /* 0,0,2,0 */
15
16/* ID selectors: (0,1,0,0) each */
17#nav { } /* 0,1,0,0 */
18#nav #logo { } /* 0,2,0,0 */
19
20/* Pseudo-elements: (0,0,0,1) */
21p::first-line { } /* 0,0,0,2 */
22.text::before { } /* 0,0,1,1 */Comparing Specificity#
1/* Higher specificity wins */
2.button { color: blue; } /* 0,0,1,0 */
3button { color: red; } /* 0,0,0,1 */
4/* .button wins - blue */
5
6/* Equal specificity: last one wins */
7.primary { color: blue; } /* 0,0,1,0 */
8.active { color: red; } /* 0,0,1,0 */
9/* .active wins if both classes present - red */
10
11/* ID always beats classes */
12.very.specific.selector { } /* 0,0,3,0 */
13#simple { } /* 0,1,0,0 */
14/* #simple wins */
15
16/* Compare column by column */
17/* 0,1,0,0 beats 0,0,10,0 */
18/* ID wins over any number of classes */:is(), :where(), and :not()#
1/* :is() uses highest specificity inside */
2:is(#id, .class, element) p { }
3/* Specificity: 0,1,0,1 (uses #id) */
4
5/* All selectors get the same specificity */
6:is(.foo, .bar, .baz) { } /* 0,0,1,0 for all */
7
8/* :where() has ZERO specificity */
9:where(#id, .class, element) p { }
10/* Specificity: 0,0,0,1 (only p counts) */
11
12/* Perfect for resets and defaults */
13:where(h1, h2, h3, h4) {
14 margin-top: 1em;
15 /* Easy to override */
16}
17
18/* :not() uses argument specificity */
19:not(.active) { } /* 0,0,1,0 */
20:not(#id) { } /* 0,1,0,0 */
21:not(p) { } /* 0,0,0,1 */
22
23/* Multiple :not() */
24:not(.a):not(.b) { } /* 0,0,2,0 */:has() Specificity#
1/* :has() uses its argument's specificity */
2:has(.child) { } /* 0,0,1,0 */
3:has(#id) { } /* 0,1,0,0 */
4:has(> p) { } /* 0,0,0,1 */
5
6/* Combined with other selectors */
7.parent:has(.child) { } /* 0,0,2,0 */
8article:has(h1):has(img) { } /* 0,0,0,3 */Inline Styles and !important#
1/* Inline styles: (1,0,0,0) */
2/* <div style="color: red;"> */
3
4/* !important overrides everything except... */
5p { color: blue !important; }
6
7/* ...another !important with higher specificity */
8.text { color: red !important; }
9/* .text wins because higher specificity */
10
11/* !important specificity order:
12 1. User agent !important
13 2. User !important
14 3. Author !important (your CSS)
15 4. Author normal
16 5. User normal
17 6. User agent normal */Common Specificity Problems#
1/* Problem: Over-qualified selectors */
2div.button { } /* 0,0,1,1 */
3.button { } /* 0,0,1,0 */
4/* div.button always wins, even if .button is later */
5
6/* Problem: ID selectors */
7#sidebar .widget { } /* 0,1,1,0 */
8.widget.highlighted { } /* 0,0,2,0 */
9/* #sidebar version always wins */
10
11/* Problem: Inline styles */
12.button { color: blue; }
13/* <button style="color: red;"> - inline wins */
14
15/* Problem: !important cascade */
16.a { color: red !important; }
17.b { color: blue !important; }
18/* Last one wins with equal specificity */Specificity Strategies#
1/* Strategy 1: Flat specificity (BEM) */
2.block { }
3.block__element { }
4.block--modifier { }
5/* All (0,0,1,0) - easy to override */
6
7/* Strategy 2: Single class selectors */
8.button { }
9.button-primary { }
10.button-large { }
11/* Modifiers don't stack selectors */
12
13/* Strategy 3: Use :where() for base styles */
14:where(button) {
15 padding: 8px 16px;
16 border: none;
17}
18
19.button {
20 /* Easy override with single class */
21 padding: 12px 24px;
22}
23
24/* Strategy 4: Cascade layers */
25@layer reset, base, components, utilities;
26
27@layer reset {
28 * { margin: 0; }
29}
30
31@layer utilities {
32 .mt-4 { margin-top: 1rem; }
33}Cascade Layers#
1/* Layers have lower priority than unlayered styles */
2@layer base {
3 .button { color: blue; } /* In layer */
4}
5
6.button { color: red; } /* Unlayered - wins! */
7
8/* Layer order matters */
9@layer reset, base, components;
10
11@layer reset {
12 button { color: black; } /* First - lowest priority */
13}
14
15@layer components {
16 .button { color: blue; } /* Last - highest priority */
17}
18
19/* Nested layers */
20@layer framework {
21 @layer base {
22 .button { color: blue; }
23 }
24
25 @layer theme {
26 .button { color: red; } /* Wins within framework */
27 }
28}Debugging Specificity#
1/* Browser DevTools show specificity */
2/* Look for crossed-out styles */
3
4/* Add temporary high-specificity selector to test */
5html body .debug-selector {
6 color: red !important;
7 outline: 2px solid red;
8}
9
10/* Check selector specificity */
11/* Use online calculators or browser DevTools */
12
13/* Common issues to look for:
14 - ID selectors
15 - Inline styles
16 - !important
17 - Deep nesting
18 - Over-qualified selectors */Reducing Specificity#
1/* Before: High specificity */
2#header .nav ul li a.active { color: blue; }
3/* 0,1,4,2 - very hard to override */
4
5/* After: Lower specificity */
6.nav-link.is-active { color: blue; }
7/* 0,0,2,0 - easy to override */
8
9/* Use :where() to reduce */
10:where(#header .nav) a { color: blue; }
11/* 0,0,0,1 - only 'a' counts */
12
13/* Avoid inline styles */
14/* Instead of: <div style="color: red;"> */
15.error-text { color: red; }
16
17/* Replace !important with higher specificity */
18/* Instead of: .text { color: blue !important; } */
19.text.text { color: blue; } /* Doubles class specificity */Architecture Patterns#
1/* ITCSS: Inverted Triangle CSS */
2/* Specificity increases through layers:
3 Settings -> Tools -> Generic -> Elements ->
4 Objects -> Components -> Utilities */
5
6/* Utilities have highest specificity (often !important) */
7.u-hidden { display: none !important; }
8
9/* Components use classes only */
10.card { }
11.card-header { }
12.card-body { }
13
14/* Objects are low specificity */
15.o-container { max-width: 1200px; }
16
17/* BEM: Block Element Modifier */
18.block { }
19.block__element { }
20.block--modifier { }
21/* All single class = (0,0,1,0) */
22
23/* Utility-first (Tailwind) */
24.flex { display: flex; }
25.mt-4 { margin-top: 1rem; }
26/* Single classes, sometimes !important for utilities */Best Practices#
Writing Selectors:
✓ Use single classes
✓ Avoid ID selectors
✓ Keep nesting shallow
✓ Prefer BEM or similar
Managing Specificity:
✓ Use cascade layers
✓ Use :where() for defaults
✓ Keep specificity flat
✓ Document exceptions
Avoiding Problems:
✓ Never use !important (except utilities)
✓ Don't over-qualify selectors
✓ Avoid inline styles
✓ Be consistent with methodology
Debugging:
✓ Use DevTools
✓ Check crossed-out styles
✓ Calculate specificity
✓ Test overrides
Quick Reference#
Specificity (Inline, ID, Class, Type):
Universal (*) 0,0,0,0
Type (div) 0,0,0,1
Pseudo-element (::) 0,0,0,1
Class (.class) 0,0,1,0
Attribute ([attr]) 0,0,1,0
Pseudo-class (:hover) 0,0,1,0
ID (#id) 0,1,0,0
Inline style 1,0,0,0
!important Beats all*
Special:
:where() 0,0,0,0 (zero)
:is() Uses highest inside
:not() Uses argument
:has() Uses argument
Conclusion#
Specificity determines which CSS rules apply when selectors conflict. Keep specificity low and flat using single-class selectors and methodologies like BEM. Use :where() for base styles that should be easily overridable, and leverage cascade layers for architectural control. Avoid ID selectors, deep nesting, and !important to maintain predictable, maintainable stylesheets.