Back to Blog
CSSSpecificitySelectorsBest Practices

CSS Specificity Guide

Understand CSS specificity to write maintainable stylesheets and avoid !important.

B
Bootspring Team
Engineering
August 24, 2018
7 min read

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.

Share this article

Help spread the word about Bootspring