CSS Β· Lesson 40

CSS Specificity Β· Who Wins When Rules Conflict?

10 phases  Β·  Build: Stage 1 Β· Setup

πŸ‘‹ Welcome to Lesson 40

Have you ever written CSS and been confused by why your style didn't seem to work? You set a colour for a paragraph, but something else overrode it. You added a class, but the result was still wrong. This happens because of a concept called CSS Specificity.

In this lesson you will learn exactly what specificity is, why it exists, how the browser scores and compares selectors, and how to control which style wins every single time. By the end, you will never be surprised by a style conflict again.

πŸ’‘ Real-world connection: In professional front-end development, understanding specificity is essential for debugging layout issues, maintaining large stylesheets, working with CSS frameworks like Bootstrap, and writing clean, predictable code.


πŸ“š 10 phasesπŸ—οΈ Stage 1 Β· Setup🌐 GitHub Pages
Phase 1 of 10
Lesson Introduction

Have you ever written CSS and been confused by why your style didn't seem to work? You set a colour for a paragraph, but something else overrode it. You added a class, but the result was still wrong. This happens because of a concept called CSS Specificity.

In this lesson you will learn exactly what specificity is, why it exists, how the browser scores and compares selectors, and how to control which style wins every single time. By the end, you will never be surprised by a style conflict again.

πŸ’‘ Real-world connection: In professional front-end development, understanding specificity is essential for debugging layout issues, maintaining large stylesheets, working with CSS frameworks like Bootstrap, and writing clean, predictable code.


✏️ Your Task
Practise what you just learned about Lesson Introduction. Open your editor, type the examples above by hand, modify them, and observe what changes.
Phase 2 of 10
Prerequisite Concepts

Before diving in, let's make sure you understand the building blocks used throughout this lesson.

What Is a CSS Selector?

A selector is the part of a CSS rule that identifies which HTML element(s) you want to style.

css
/* The selector is "p" β€” it targets all <p> elements */
p {
  color: red;
}

The Three Main Selector Types You Need to Know

1. Element selector Β· targets HTML tags by name.

css
p { color: red; }    /* styles every <p> */
h1 { color: blue; }  /* styles every <h1> */

2. Class selector Β· targets elements that have a specific class attribute. Written with a dot (.) before the name.

css
.highlight { color: yellow; }
/* This styles any element with class="highlight" */

3. ID selector Β· targets ONE specific element with a matching id attribute. Written with a hash (#) before the name.

css
#main-title { color: green; }
/* This styles the element with id="main-title" */

πŸ’‘ Key rule to remember: An id must be unique on a page Β· only one element should have any given id. Classes can be shared by many elements.

What Is a Style Declaration?

A declaration is the property: value pair inside curly braces.

css
p {
  color: blue;       /* This is one declaration */
  font-size: 18px;   /* This is another declaration */
}

✏️ Your Task
Practise what you just learned about Prerequisite Concepts. Open your editor, type the examples above by hand, modify them, and observe what changes.
Phase 3 of 10
Conceptual Understanding

What Is Specificity?

Specificity is the scoring system that browsers use to decide which CSS rule "wins" when two or more rules target the same element and set the same property.

Think of it like a competition between your CSS rules. Every selector has a score (called its specificity weight). When two rules conflict, the one with the higher score wins, and its style gets applied.

Why Does Specificity Exist?

Imagine you're building a website. You have a general rule that makes all paragraphs grey. Then later you want one special paragraph to be red. Without specificity, the browser wouldn't know which rule to use. Specificity is the tiebreaker.

Analogy: Think of specificity like priority lanes at an airport. Economy passengers (element selectors) use the regular queue. Business class passengers (class selectors) get a faster lane. First-class passengers (ID selectors) go straight through. The more specific you are, the faster you get to the front.

A First Look at the Problem Specificity Solves

html
<!-- HTML -->
<p class="test" id="demo">Hello World!</p>
css
/* Three rules, all targeting the same paragraph */
p       { color: red; }
.test   { color: green; }
#demo   { color: blue; }

Expected Output: The text "Hello World!" appears in blue.

Why blue? Because #demo (an ID selector) has a higher specificity score than .test (a class selector), which has a higher score than p (an element selector). The browser picks blue.

πŸ€” Thinking prompt: What would happen if you removed #demo { color: blue; }? Which colour would win then? Think about it before reading on.


✏️ Your Task
Practise what you just learned about Conceptual Understanding. Open your editor, type the examples above by hand, modify them, and observe what changes.
Phase 4 of 10
The Four Levels of Specificity

CSS organises selectors into four levels, from lowest to highest priority.

Level 1 Β· Universal Selector and :where()

Weight: 0-0-0 (no priority)

The universal selector * matches everything, but it has zero specificity. The :where() pseudo-class also carries zero specificity weight.

css
* { color: grey; }        /* weight: 0-0-0 */
:where(p) { color: grey; } /* weight: 0-0-0 */

These are always overridden by any other selector.


Level 2 Β· Element Selectors and Pseudo-elements

Weight: 0-0-1

Element selectors target HTML tags. Pseudo-elements like ::before and ::after also sit at this level.

css
p      { color: red; }    /* weight: 0-0-1 */
h1     { color: blue; }   /* weight: 0-0-1 */
::before { content: "β†’"; } /* weight: 0-0-1 */

Level 3 Β· Class Selectors, Attribute Selectors, and Pseudo-classes

Weight: 0-1-0

This level includes classes (.myclass), attribute selectors ([type="text"]), and pseudo-classes (:hover, :focus, :nth-child()).

css
.test          { color: green; }   /* weight: 0-1-0 */
[type="text"]  { color: green; }   /* weight: 0-1-0 */
:hover         { color: green; }   /* weight: 0-1-0 */

Level 4 Β· ID Selectors

Weight: 1-0-0

ID selectors are the highest specificity you can normally use in a stylesheet. They always override classes and element selectors.

css
#demo { color: blue; }   /* weight: 1-0-0 */

Level 5 Β· Inline Styles

Weight: Even higher than IDs

Inline styles are written directly on an HTML element using the style attribute. They override all stylesheet rules.

html
<p style="color: pink;">This is pink no matter what.</p>

An inline style beats every other type of CSS selector.


✏️ Your Task
Practise what you just learned about The Four Levels of Specificity. Open your editor, type the examples above by hand, modify them, and observe what changes.
Phase 5 of 10
The Specificity Weight Notation: X-Y-Z

Specificity is measured using a three-number notation: X-Y-Z

PositionCounts...Example
X (first number)Number of ID selectors#header β†’ X = 1
Y (second number)Number of class, attribute, and pseudo-class selectors.nav, [href], :hover β†’ Y = 1 each
Z (third number)Number of element and pseudo-element selectorsp, h1, ::before β†’ Z = 1 each

To compare two selectors, start from the left (X). The selector with the bigger X wins. If X is equal, compare Y. If Y is equal, compare Z. If all three are equal, the rule that appears later in the stylesheet wins (this is the cascade rule you may have learned previously).

Quick Weight Examples

SelectorX (IDs)Y (Classes)Z (Elements)Notation
p0010-0-1
.test0100-1-0
p.test0110-1-1
#demo1001-0-0
p#demo1011-0-1
#demo.test1101-1-0

✏️ Your Task
Practise what you just learned about The Specificity Weight Notation: X-Y-Z. Open your editor, type the examples above by hand, modify them, and observe what changes.
Phase 6 of 10
Simple Standalone Examples

Example 1 Β· Element Selector Alone

html
<html>
<head>
  <style>
    p { color: red; }
  </style>
</head>
<body>
  <p>Hello World!</p>
</body>
</html>

Expected Output: The text "Hello World!" appears in red.

Line-by-line explanation:

  • p Β· This is an element selector targeting all <p> tags.
  • { color: red; } Β· This sets the text colour to red.
  • Specificity weight: 0-0-1
  • Since there is only one rule, it wins by default.

Example 2 Β· Class Selector Overrides Element Selector

html
<html>
<head>
  <style>
    .test { color: green; }  /* weight: 0-1-0 */
    p     { color: red; }    /* weight: 0-0-1 */
  </style>
</head>
<body>
  <p class="test">Hello World!</p>
</body>
</html>

Expected Output: The text "Hello World!" appears in green.

Why green?

  • Both rules target the same <p> element.
  • .test has weight 0-1-0.
  • p has weight 0-0-1.
  • Comparing: 0-1-0 is greater than 0-0-1 (Y column: 1 > 0).
  • Green wins.

πŸ€” Thinking prompt: What if you swap the order of the two rules in the stylesheet? Does the result change? Try it.


Example 3 Β· ID Selector Overrides Everything

html
<html>
<head>
  <style>
    #demo  { color: blue; }   /* weight: 1-0-0 */
    .test  { color: green; }  /* weight: 0-1-0 */
    p      { color: red; }    /* weight: 0-0-1 */
  </style>
</head>
<body>
  <p id="demo" class="test">Hello World!</p>
</body>
</html>

Expected Output: The text "Hello World!" appears in blue.

Why blue?

  • The <p> element matches all three selectors.
  • #demo has the highest weight: 1-0-0.
  • Blue wins, regardless of order in the stylesheet.

Example 4 Β· Combined Selector with Higher Weight

Now here is where it gets interesting. What if we combine selectors? When you write p#demo, you are selecting a <p> element that also has id="demo". This combination adds up the weights.

css
#demo   { color: blue; }    /* weight: 1-0-0 */
p#demo  { color: orange; }  /* weight: 1-0-1  ← WINS */
.test   { color: green; }   /* weight: 0-1-0 */
p.test  { color: yellow; }  /* weight: 0-1-1 */
p       { color: red; }     /* weight: 0-0-1 */
html
<p id="demo" class="test">Hello World!</p>

Expected Output: The text "Hello World!" appears in orange.

Why orange?

  • p#demo has weight 1-0-1 (1 ID + 1 element).
  • #demo alone has weight 1-0-0 (1 ID, no elements).
  • Comparing X: both have X=1. Tie. Compare Z: p#demo has Z=1, #demo has Z=0.
  • 1-0-1 > 1-0-0 β†’ orange wins.

πŸ€” Thinking prompt: Why does p.test (weight 0-1-1) lose to #demo (weight 1-0-0)? Because the X column (IDs) always overrules the Y and Z columns no matter how many classes or elements you have.


Example 5 Β· Equal Specificity: Last Rule Wins

When two selectors have identical weight, the browser uses the one that appears later in the stylesheet. This is called the cascade.

css
p { color: red; }    /* weight: 0-0-1 */
p { color: blue; }   /* weight: 0-0-1 β€” WINS because it comes last */

Expected Output: Blue.


✏️ Your Task
Practise what you just learned about Simple Standalone Examples. Open your editor, type the examples above by hand, modify them, and observe what changes.
Phase 7 of 10
Understanding the Specificity Hierarchy Table

Here is the complete picture from highest to lowest priority:

RankTypeExampleWeight
1st (highest)Inline style<h1 style="color:pink">Higher than all stylesheet selectors
2ndID selector#navbar1-0-0
3rdClass / Attribute / Pseudo-class.test, [href], :hover0-1-0
4thElement / Pseudo-elementh1, p, ::before0-0-1
5th (lowest)Universal / :where()*, :where(p)0-0-0

✏️ Your Task
Practise what you just learned about Understanding the Specificity Hierarchy Table. Open your editor, type the examples above by hand, modify them, and observe what changes.
Phase 8 of 10
More Complex Specificity Calculations

Let's practice reading and calculating weights of more complex selectors.

Worked Calculation 1

css
div p.highlight { color: purple; }

Break it down:

  • div β†’ element β†’ adds 0-0-1
  • p β†’ element β†’ adds 0-0-1
  • .highlight β†’ class β†’ adds 0-1-0
  • Total: 0-1-2

Worked Calculation 2

css
#header nav ul li a:hover { color: orange; }

Break it down:

  • #header β†’ ID β†’ adds 1-0-0
  • nav β†’ element β†’ adds 0-0-1
  • ul β†’ element β†’ adds 0-0-1
  • li β†’ element β†’ adds 0-0-1
  • a β†’ element β†’ adds 0-0-1
  • :hover β†’ pseudo-class β†’ adds 0-1-0
  • Total: 1-1-4

Worked Calculation 3

css
.sidebar .widget h2 { color: teal; }

Break it down:

  • .sidebar β†’ class β†’ adds 0-1-0
  • .widget β†’ class β†’ adds 0-1-0
  • h2 β†’ element β†’ adds 0-0-1
  • Total: 0-2-1

✏️ Your Task
### Worked Calculation 1 `css div p.highlight { color: purple; } ` Break it down: - div β†’ element β†’ adds 0-0-1 - p β†’ element β†’ adds 0-0-1 - .highlight β†’ class β†’ adds 0-1-0 - Total: 0-1-2 Β·
Phase 9 of 10
Guided Practice Exercises
🎯 Your Challenge

Exercise 1 Β· Predict the Winning Colour

Objective: Read the CSS and HTML below, calculate which colour wins, and explain why.

HTML:

✏️ Task
Practise what you just learned about Guided Practice Exercises. Open your editor, type the examples above by hand, modify them, and observe what changes.
html
<p id="intro" class="lead">Welcome to my website.</p>

CSS:

css
p        { color: black; }      /* Rule A */
.lead    { color: navy; }       /* Rule B */
#intro   { color: crimson; }    /* Rule C */
p.lead   { color: teal; }       /* Rule D */

Steps:

  1. Calculate the weight of each rule.
  2. Identify which rule has the highest weight.
  3. State which colour the text will appear.

Weights:

  • Rule A: p β†’ 0-0-1
  • Rule B: .lead β†’ 0-1-0
  • Rule C: #intro β†’ 1-0-0
  • Rule D: p.lead β†’ 0-1-1

Expected Output: Text appears in crimson.

Why? Rule C (#intro) has the highest weight of 1-0-0. The X column (ID count) immediately beats all other rules regardless of their Y or Z values.

Self-check questions:

  • Does changing the order of the CSS rules change the result? (No Β· specificity takes priority over order when weights are different.)
  • What would happen if you removed #intro from the HTML element? Which colour would win then?

Exercise 2 Β· Rank These Selectors from Weakest to Strongest

Given the following selectors, rank them from lowest to highest specificity:

code
a) *
b) li
c) ul li
d) .menu
e) .menu li
f) #nav
g) #nav li
h) #nav .menu li

Answers with weights:

SelectorWeightRank
*0-0-0Weakest
li0-0-12nd
ul li0-0-23rd
.menu0-1-04th
.menu li0-1-15th
#nav1-0-06th
#nav li1-0-17th
#nav .menu li1-1-1Strongest

πŸ€” Thinking prompt: Notice that ul li (0-0-2) is stronger than .menu (0-1-0)? Wait… actually that's wrong! Let's check: 0-0-2 vs 0-1-0. Compare Y column: 0 vs 1. .menu wins. Always compare left to right!


Exercise 3 Β· Fix the Specificity Problem

Scenario: A web developer is building a navigation bar. She expects the active link to appear orange, but it keeps appearing blue. Help her fix the issue without changing the HTML.

HTML:

html
<nav id="main-nav">
  <a href="#" class="active">Home</a>
</nav>

CSS (broken):

css
#main-nav a   { color: blue; }     /* weight: 1-0-1 */
.active       { color: orange; }   /* weight: 0-1-0 */

Problem: .active (0-1-0) loses to #main-nav a (1-0-1) because 1-0-1 > 0-1-0.

Fix option 1 Β· increase the specificity of the orange rule:

css
#main-nav a      { color: blue; }       /* weight: 1-0-1 */
#main-nav .active { color: orange; }   /* weight: 1-1-0  ← WINS */

Now #main-nav .active has weight 1-1-0, which beats #main-nav a at 1-0-1 because the Y column (1 vs 0) tips the balance when X is tied.

Fix option 2 Β· even more specific:

css
#main-nav a.active { color: orange; }  /* weight: 1-1-1  ← also wins */

Expected Output (after fix): The "Home" link appears in orange.


Phase 10 of 10
Common Beginner Mistakes

Mistake 1 Β· Thinking Order Always Matters

Wrong thinking: "My rule comes after the other one, so it should win."

Correction: Order only matters when specificity weights are equal. If they are different, the higher weight always wins regardless of order.

css
#demo  { color: blue; }    /* weight: 1-0-0 */
p      { color: red; }     /* weight: 0-0-1 β€” comes after but LOSES */

Output: Blue. The ID beats the element selector no matter the order.


Mistake 2 Β· Adding More Classes Does Not Beat an ID

Wrong thinking: "I have three classes on my selector, so it must beat an ID."

css
.a.b.c { color: green; }   /* weight: 0-3-0 */
#demo  { color: blue; }    /* weight: 1-0-0 */

Correction: No number of class selectors can ever beat a single ID selector. The X column (ID count) is always more powerful than the Y column (class count). 0-3-0 loses to 1-0-0.


Mistake 3 Β· Forgetting That Combined Selectors Add Up

Wrong thinking: "p#demo is the same as #demo."

css
#demo   { color: blue; }    /* weight: 1-0-0 */
p#demo  { color: orange; }  /* weight: 1-0-1 β€” WINS */

Correction: p#demo adds the weight of both p (an element) and #demo (an ID), giving a total of 1-0-1, which is more than #demo alone at 1-0-0.


Mistake 4 Β· Thinking Inline Styles Can Be Overridden Without !important

html
<p style="color: purple;">I am always purple.</p>
css
#demo  { color: blue; }   /* Cannot override the inline style */

Correction: Inline styles override all stylesheet selectors. The only way to beat an inline style in your CSS is to use !important (which is covered in the next lesson).


Mistake 5 Β· Confusing :where() and Regular Pseudo-classes

Regular pseudo-classes like :hover or :focus have weight 0-1-0. But the special :where() function has weight 0-0-0 Β· it intentionally carries no specificity so it can be easily overridden. These are not the same.

css
p:hover  { color: blue; }    /* weight: 0-1-1 */
:where(p) { color: red; }   /* weight: 0-0-0 β€” always loses */

✏️ Your Task
Practise what you just learned about Common Beginner Mistakes. Open your editor, type the examples above by hand, modify them, and observe what changes.
πŸ—οΈ Build It β€” Mini Project
Stage 1 Β· Setup

In this project you will build a small webpage that demonstrates all four levels of specificity working together. Each level will change the appearance of a block of text in a controlled, predictable way.

Stage 1 Β· Setup

Create your HTML structure:

starter.html
<!DOCTYPE html>
<html>
<head>
  <title>Specificity Demo</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div id="page-wrapper" class="container">
    <h1>Welcome to My Page</h1>
    <p>Default paragraph β€” styled by element selector.</p>
    <p class="featured">Featured paragraph β€” styled by class selector.</p>
    <p id="hero-text" class="featured">Hero paragraph β€” styled by ID selector.</p>
    <p style="color: purple; font-weight: bold;">
      Inline-styled paragraph β€” cannot be overridden by the stylesheet.
    </p>
  </div>
</body>
</html>

Lesson 40 complete! πŸŽ‰

You covered: