View All CSS Tutorials

CSS Specificity Explained

Learn how CSS specificity decides which selectors win, why some rules override others, and how to avoid hard-to-maintain styles.

Infographic explaining CSS specificity, including type selectors, classes, IDs, inline styles, and rule conflicts.

Introduction

CSS specificity is one of the main reasons one CSS rule can override another.

For example:

CSS
p {  color: black;} .intro {  color: navy;}

HTML:

HTML
<p class=”intro”>Welcome to the page.</p>

Both rules apply to the paragraph.

The paragraph is a <p> element, so the p selector applies.

The paragraph also has class="intro", so the .intro selector applies.

The browser must decide which colour to use.

In this case, the paragraph becomes navy because .intro is more specific than p.

Specificity is the system CSS uses to compare selectors when more than one rule targets the same element and property.

Understanding specificity helps explain why some selectors override others and why some CSS rules appear crossed out in browser developer tools.

What Is CSS Specificity?

Specificity is the weight or strength of a CSS selector.

When two or more declarations apply to the same element and the same property, the browser compares their specificity.

Example:

CSS
p {  color: black;} .highlight {  color: blue;}

HTML:

HTML
<p class=”highlight”>Important paragraph.</p>

Both rules set color.

The .highlight selector is more specific than the p selector.

So the paragraph becomes blue.

The more specific selector wins unless another part of the cascade changes the result.

Why Specificity Matters

Specificity matters because CSS often has several rules affecting the same element.

For example:

CSS
body {  color: #222222;} p {  color: #333333;} .intro {  color: navy;}

HTML:

HTML
<p class=”intro”>This is the introduction.</p>

The paragraph may receive colour information from several places.

The body sets a general inherited colour.

The p rule sets a paragraph colour.

The .intro rule sets a class-specific colour.

The browser uses the cascade and specificity to decide the final value.

In this case, .intro is the most specific direct selector, so the text becomes navy.

Specificity Works Per Property

Specificity is compared per CSS property.

Example:

CSS
p {  color: black;  font-size: 18px;} .intro {  color: navy;}

HTML:

HTML
<p class=”intro”>Welcome.</p>

The paragraph gets:

TEXT
color from .introfont-size from p

The .intro selector overrides only the color property because that is the property both rules define.

The p rule still provides font-size because .intro does not set a competing font size.

Specificity does not make an entire rule disappear.

It decides individual property conflicts.

A Simple Specificity Order

A simplified order from weaker to stronger is:

TEXT
type selectorclass selectorID selectorinline style

Examples:

CSS
p {  color: black;}
CSS
.intro {  color: navy;}
CSS
#main-intro {  color: green;}
HTML
<p style=”color: red;”>

In general:

TEXT
class selectors override type selectorsID selectors override class selectorsinline styles override normal stylesheet rules

This simplified model explains many common CSS conflicts.

Type Selector Specificity

A type selector targets an HTML element by tag name.

Examples:

CSS
p {  color: black;}
CSS
h1 {  font-size: 2rem;}
CSS
button {  padding: 10px 16px;}

Type selectors have low specificity.

They are useful for broad base styles.

For example:

CSS
body {  font-family: Arial, sans-serif;} p {  line-height: 1.6;}

Because type selectors are low specificity, they are usually easy to override later with classes.

Class Selector Specificity

A class selector targets elements with a specific class.

Example:

CSS
.card {  padding: 24px;}

HTML:

HTML
<article class=”card”>  <h2>CSS Selectors</h2></article>

Class selectors are more specific than type selectors.

Example:

CSS
p {  color: black;} .intro {  color: navy;}

HTML:

HTML
<p class=”intro”>Welcome.</p>

The paragraph becomes navy.

The .intro selector is stronger than the p selector.

Classes are usually the most useful selectors for reusable styling.

ID Selector Specificity

An ID selector targets an element with a specific id.

Example:

CSS
#main-title {  color: green;}

HTML:

HTML
<h1 id=”main-title”>Welcome</h1>

ID selectors are more specific than class selectors.

Example:

CSS
.page-title {  color: navy;} #main-title {  color: green;}

HTML:

HTML
<h1 id=”main-title” class=”page-title”>Welcome</h1>

The heading becomes green because #main-title is more specific than .page-title.

IDs are powerful.

Use them carefully in CSS because they can make styles harder to override.

Inline Style Specificity

Inline styles are written directly in the HTML with the style attribute.

Example:

HTML
<p style=”color: red;”>This paragraph is red.</p>

Inline styles are usually stronger than normal stylesheet rules.

Example:

HTML
<p class=”intro” style=”color: red;”>Welcome.</p>

CSS:

CSS
.intro {  color: navy;}

The paragraph becomes red.

The inline style wins over the class selector.

Inline styles can be useful in rare cases, but they usually make CSS harder to maintain.

For normal website styling, use stylesheets and classes instead.

Specificity as a Score

Specificity is often explained as a scoring system.

A common simplified format is:

TEXT
IDs – classes/attributes/pseudo-classes – types/pseudo-elements

For example:

CSS
p

has:

TEXT
0 IDs0 classes1 type

So it can be thought of as:

TEXT
0-0-1

This selector:

CSS
.intro

has:

TEXT
0 IDs1 class0 types

So it is:

TEXT
0-1-0

This selector:

CSS
#main-intro

has:

TEXT
1 ID0 classes0 types

So it is:

TEXT
1-0-0

A higher value in the leftmost column wins.

Specificity Score Examples

This selector:

CSS
p

Specificity:

TEXT
0-0-1

This selector:

CSS
.card

Specificity:

TEXT
0-1-0

This selector:

CSS
.card p

Specificity:

TEXT
0-1-1

This selector:

CSS
#main-content p

Specificity:

TEXT
1-0-1

The ID selector column is stronger than the class column.

That means #main-content p is much harder to override than .card p.

Comparing Specificity

Example:

CSS
p {  color: black;} .card p {  color: navy;}

HTML:

HTML
<article class=”card”>  <p>This paragraph is inside a card.</p></article>

The paragraph becomes navy.

Why?

The selector p has specificity:

TEXT
0-0-1

The selector .card p has specificity:

TEXT
0-1-1

Because .card p includes a class selector, it is more specific.

Specificity Beats Source Order When Selectors Differ

Source order means later rules can win when specificity is equal.

But a more specific selector can beat a later less-specific selector.

Example:

CSS
.intro {  color: navy;} p {  color: black;}

HTML:

HTML
<p class=”intro”>Welcome.</p>

The paragraph becomes navy.

Even though the p rule comes later, .intro is more specific.

Source order does not automatically beat specificity.

The later rule wins only when the competing declarations have equal specificity or when other cascade factors allow it.

When Source Order Wins

If specificity is equal, the later rule wins.

Example:

CSS
.message {  color: green;} .message {  color: blue;}

HTML:

HTML
<p class=”message”>Your settings were saved.</p>

Both rules use the same selector.

They have equal specificity.

The second rule comes later, so the message becomes blue.

Specificity decides first.

If specificity is tied, order decides.

Specificity with Multiple Classes

Multiple classes in a selector increase specificity.

Example:

CSS
.button {  background-color: gray;} .button.primary {  background-color: navy;}

HTML:

HTML
<button class=”button primary”>Save</button>

The button becomes navy.

The selector .button.primary has two class selectors.

It is more specific than .button.

This means .button.primary can override .button even if .button appears later in some cases.

Specificity with Type and Class Selectors

A class selector combined with a type selector is more specific than the class alone.

Example:

CSS
.note {  color: navy;} p.note {  color: green;}

HTML:

HTML
<p class=”note”>This is a note.</p>

The paragraph becomes green.

The selector p.note contains:

TEXT
one type selectorone class selector

It is more specific than .note.

However, adding type selectors to classes is not always necessary.

Use them only when you need that extra targeting.

Specificity with Descendant Selectors

Descendant selectors add up the specificity of their parts.

Example:

CSS
.card p {  color: navy;}

This selector includes:

TEXT
.card = one classp = one type

So it is stronger than:

CSS
p {  color: black;}

This is useful when styling paragraphs only inside cards.

However, long descendant selectors can become too specific and fragile.

Example:

CSS
body main .container article.card p.text {  color: navy;}

This may be harder to override and maintain.

Attribute Selectors and Specificity

Attribute selectors count like class selectors.

Example:

CSS
input[type=”email”] {  border-color: blue;}

This is more specific than:

CSS
input {  border-color: gray;}

HTML:

HTML
<input type=”email”>

The email input gets a blue border.

The selector input[type="email"] includes:

TEXT
one type selectorone attribute selector

Attribute selectors are useful and reasonably specific.

They are not as strong as ID selectors.

Pseudo-Classes and Specificity

Pseudo-classes count like class selectors.

Examples:

CSS
a:hover {  text-decoration: underline;}
CSS
li:first-child {  font-weight: bold;}
CSS
input:checked {  outline: 2px solid green;}

Each pseudo-class contributes specificity like a class.

For example, a:hover is more specific than a.

That is why hover styles can override normal link styles when the element is hovered.

Pseudo-Elements and Specificity

Pseudo-elements count like type selectors.

Examples:

CSS
p::first-line {  font-weight: bold;}
CSS
.note::before {  content: “Note: “;}

Pseudo-elements are generally low specificity.

They target parts of elements rather than element states.

They are usually not a major source of specificity problems, but they still contribute to the selector’s specificity.

Universal Selector Specificity

The universal selector has no specificity.

Example:

CSS
* {  box-sizing: border-box;}

The * selector matches every element, but it does not add specificity.

That makes it useful for broad base rules.

For example:

CSS
*,*::before,*::after {  box-sizing: border-box;}

This creates a base rule that is easy to override if needed.

Specificity with :is()

The :is() selector function takes the specificity of the most specific selector inside it.

Example:

CSS
:is(h1, .title, #main-title) {  color: navy;}

The most specific selector inside :is() is:

CSS
#main-title

This can make the whole rule more specific than expected.

Be careful when mixing type, class, and ID selectors inside :is().

If you want low specificity grouping, :where() may be better.

Specificity with :where()

The :where() selector function always has zero specificity.

Example:

CSS
:where(h1, h2, p) {  margin-top: 0;}

This rule matches headings and paragraphs, but the :where() part adds no specificity.

That makes it useful for base styles and resets.

For example:

CSS
:where(h1, h2, h3, p, ul, ol) {  margin-top: 0;}

Later class selectors can easily override these low-specificity defaults.

Specificity with :not()

The :not() selector function takes the specificity of the selector inside it.

Example:

CSS
.card:not(.featured) {  border: 1px solid #dddddd;}

This includes:

TEXT
.card.featured inside :not()

Both contribute to specificity.

Be careful with selectors such as:

CSS
.card:not(#featured-card) {  border: 1px solid #dddddd;}

The ID inside :not() makes the selector more specific.

That may make it harder to override later.

Specificity with :has()

The :has() selector function also takes specificity from its argument.

Example:

CSS
.card:has(img) {  border-top: 4px solid navy;}

This includes the .card class and the img type selector inside :has().

Another example:

CSS
.card:has(.featured-image) {  border-top: 4px solid navy;}

The class inside :has() contributes to specificity.

:has() is powerful, but it can make selectors more complex.

Use it when it improves clarity.

Inline Styles and Specificity

Inline styles are stronger than normal stylesheet selectors.

Example:

HTML
<p class=”intro” style=”color: red;”>Welcome.</p>

CSS:

CSS
.intro {  color: navy;}

The paragraph becomes red.

The inline style wins.

To override inline styles from a stylesheet, authors sometimes use !important.

However, this should be avoided where possible.

If you control the HTML, removing the inline style is usually cleaner.

!important and Specificity

The !important flag changes normal cascade behaviour.

Example:

CSS
p {  color: red !important;} .intro {  color: navy;}

HTML:

HTML
<p class=”intro”>Welcome.</p>

The paragraph may become red because the p declaration is marked !important.

Even though .intro is more specific, the important declaration has higher priority.

Use !important sparingly.

It can make CSS difficult to override and debug.

Competing !important Rules

If two declarations are both marked !important, specificity matters again.

Example:

CSS
p {  color: red !important;} .intro {  color: navy !important;}

HTML:

HTML
<p class=”intro”>Welcome.</p>

Both declarations are important.

The .intro selector is more specific than p.

So the paragraph becomes navy.

If specificity is tied between two important declarations, source order can decide.

Why High Specificity Causes Problems

High-specificity selectors can make CSS hard to maintain.

Example:

CSS
body main .content article.card p.description {  color: navy;}

This selector is very specific.

To override it, you may need an even more specific selector.

That can lead to a specificity battle.

Example:

CSS
body main .content article.card.featured p.description {  color: green;}

This makes the stylesheet increasingly complex.

It is usually better to use clear class selectors.

Avoiding Specificity Battles

A specificity battle happens when you keep making selectors stronger to override earlier selectors.

Less ideal:

CSS
.card p {  color: navy;} main .card p {  color: green;} body main .card p {  color: red;}

Better:

CSS
.card-text {  color: navy;} .card-text-featured {  color: green;}

HTML:

HTML
<p class=”card-text card-text-featured”>Featured card text.</p>

Clear classes reduce the need for increasingly specific selectors.

Use Classes for Predictable Specificity

Classes are usually the best tool for predictable styling.

Example:

HTML
<button class=”button button-primary”>Save</button><button class=”button button-secondary”>Cancel</button>

CSS:

CSS
.button {  padding: 10px 16px;} .button-primary {  background-color: navy;  color: white;} .button-secondary {  background-color: white;  color: navy;}

This creates predictable, reusable styles.

The base class gives shared styling.

Modifier classes provide variations.

This is easier to maintain than relying on long descendant selectors or IDs.

Keep Specificity Low Where Possible

Low-specificity CSS is easier to override.

For example:

CSS
.card {  padding: 24px;}

is easier to work with than:

CSS
main .content section.article-list article.card {  padding: 24px;}

The first selector clearly styles cards.

The second selector depends on a very specific HTML structure.

If the structure changes, the selector may break.

If another style needs to override it, the selector may be unnecessarily difficult to beat.

Use Source Order Intentionally

When selectors have equal specificity, source order decides.

This can be useful.

Example:

CSS
.button {  background-color: gray;} .button-primary {  background-color: navy;}

HTML:

HTML
<button class=”button button-primary”>Save</button>

Both selectors are class selectors.

They have equal specificity.

Because .button-primary comes later, it overrides the background colour.

This is a common and clean pattern:

TEXT
base styles firstvariation styles later

A Complete Example

HTML:

HTML
<!DOCTYPE html><html lang=”en”><head>  <meta charset=”UTF-8″>  <title>CSS Specificity Example</title>  <link rel=”stylesheet” href=”styles.css”></head><body>   <main class=”container”>    <h1 id=”page-title” class=”title”>CSS Specificity</h1>     <p class=”intro”>      Specificity explains why some selectors override others.    </p>     <article class=”card featured-card”>      <h2 class=”card-title”>Featured Lesson</h2>      <p class=”card-text”>        More specific selectors can override less specific selectors.      </p>      <a class=”button button-primary” href=”lesson.html”>Read more</a>    </article>  </main> </body></html>

CSS:

CSS
/* Type selector: low specificity */h1 {  color: black;} /* Class selector: stronger than h1 */.title {  color: navy;} /* ID selector: stronger than .title */#page-title {  font-size: 2.5rem;} /* Base paragraph style */p {  color: #333333;} /* Class selector overrides p colour */.intro {  color: navy;} /* Component base */.card {  padding: 24px;  border: 1px solid #dddddd;} /* Variation comes later */.featured-card {  border-color: navy;} /* Clear class for card text */.card-text {  line-height: 1.6;} /* Button base */.button {  padding: 10px 16px;  background-color: gray;  color: white;} /* Button variation */.button-primary {  background-color: navy;}

This example shows type, class, ID, and source order working together.

How the Complete Example Works

This rule targets all <h1> elements:

CSS
h1 {  color: black;}

This rule targets elements with class="title":

CSS
.title {  color: navy;}

The title becomes navy because .title is more specific than h1.

This rule targets the unique element with id="page-title":

CSS
#page-title {  font-size: 2.5rem;}

The ID selector applies the font size.

This rule targets all paragraphs:

CSS
p {  color: #333333;}

This rule targets the intro paragraph:

CSS
.intro {  color: navy;}

The intro paragraph becomes navy because .intro is more specific than p.

This rule gives all buttons a base background:

CSS
.button {  background-color: gray;}

This later rule changes primary buttons:

CSS
.button-primary {  background-color: navy;}

Because both selectors have equal specificity and .button-primary comes later, the primary button becomes navy.

Debugging Specificity Problems

Browser developer tools are the best way to debug specificity.

Inspect the element and check the Styles panel.

Look for:

TEXT
which rules applywhich declarations are crossed outwhich selector is more specificwhich rule appears laterwhether an inline style is presentwhether !important is being used

A crossed-out declaration usually means the browser read the rule, but another rule won.

For example:

CSS
p {  color: black;}

may be crossed out because:

CSS
.intro {  color: navy;}

is more specific.

Developer tools show this directly.

Common Mistake: Thinking the Last Rule Always Wins

The last rule does not always win.

Example:

CSS
.intro {  color: navy;} p {  color: black;}

HTML:

HTML
<p class=”intro”>Welcome.</p>

The paragraph becomes navy.

Even though the p rule comes later, .intro is more specific.

The later rule wins only when specificity is equal or when higher cascade rules allow it.

Common Mistake: Using IDs for Everyday Styling

This works:

CSS
#site-nav {  background-color: navy;}

But it can be harder to override than a class:

CSS
.site-nav {  background-color: navy;}

Use IDs when uniqueness is important, such as page anchors or JavaScript hooks.

Use classes for most styling.

Classes are easier to reuse and easier to override.

Common Mistake: Making Selectors Too Long

Less maintainable:

CSS
body main .container section.article-list article.card p.card-text {  color: navy;}

Better:

CSS
.card-text {  color: navy;}

Long selectors can create high specificity and depend too much on exact HTML structure.

Short, meaningful class selectors are usually easier to maintain.

Common Mistake: Adding !important Instead of Fixing Specificity

Less ideal:

CSS
.card-text {  color: navy !important;}

Better:

CSS
.card-text-featured {  color: navy;}

or place the override later:

CSS
.card-text {  color: #333333;} .card-text-featured {  color: navy;}

!important can be useful in limited situations, but it should not be the default solution.

Common Mistake: Forgetting That Attribute Selectors Count Like Classes

This selector:

CSS
input[type=”email”] {  border-color: blue;}

is more specific than:

CSS
input {  border-color: gray;}

The attribute selector adds specificity.

If your general input style is being overridden, check whether an attribute selector is involved.

Common Mistake: Confusing Inheritance with Specificity

Inheritance is not the same as specificity.

Example:

CSS
body {  color: black;} p {  color: navy;}

The paragraph becomes navy.

The paragraph may inherit black from body, but the p rule directly applies navy.

Directly applied styles beat inherited styles.

Specificity compares selectors that directly apply to the element.

Inheritance provides values when no direct value is set.

Common Mistake: Over-Specifying Component Styles

Less flexible:

CSS
main .card .card-title {  color: navy;}

Often better:

CSS
.card-title {  color: navy;}

or:

CSS
.card .card-title {  color: navy;}

Use only as much specificity as the component needs.

Over-specific selectors make future changes harder.

Best Practices

Use type selectors for broad base styles.

Use classes for reusable component styles.

Use IDs sparingly in CSS.

Avoid inline styles for normal styling.

Avoid !important unless there is a clear reason.

Keep selectors as short as practical.

Avoid deep descendant selector chains.

Use modifier classes for variations.

Place base styles before variation styles.

Use source order intentionally when specificity is equal.

Use :where() for low-specificity base rules when appropriate.

Use browser developer tools to inspect which selector is winning.

Summary

CSS specificity explains why some selectors override others.

A type selector has low specificity:

CSS
p {  color: black;}

A class selector is more specific:

CSS
.intro {  color: navy;}

An ID selector is more specific again:

CSS
#main-intro {  color: green;}

Inline styles are usually stronger than normal stylesheet selectors:

HTML
<p style=”color: red;”>

The simplified order is:

TEXT
type selector < class selector < ID selector < inline style

If selectors have equal specificity, the later rule usually wins.

If one selector is more specific, it can override a later but weaker selector.

The main idea is simple:

TEXT
Specificity is the weight of a selector.The heavier selector usually wins.

Once you understand specificity, it becomes much easier to predict, organise, and debug CSS.