What is CSS Specificity? Selectors, Specificity, and the Cascade Explained
CSS specificity is the algorithm browsers use to pick which CSS rule wins when two declarations target the same element and property. This guide explains selectors, specificity calculation, the cascade, and inheritance — with practical examples.
On this page
- CSS Selectors
- Type, Class, and ID Selectors
- Attribute Selectors
- Combinators
- Pseudo-Classes and Pseudo-Elements
- The Cascade
- Cascade Origin and Layers
- The !important Flag
- How Specificity Is Calculated
- The Cascade Order Within Specificity
- Specificity Examples
- :is(), :not(), and Nesting
- Inheritance
- How the Three Interact
- FAQ
- What is the difference between specificity and the cascade?
- Why should I avoid !important?
- Does specificity count pseudo-classes and pseudo-elements the same way?
- Can I use all: unset safely on any element?
- How do I debug a CSS rule that is not applying?
- Verdict
When two CSS rules target the same element and the same property, only one can win. The browser uses the cascade algorithm — which evaluates origin, importance, specificity, and source order — to decide which declaration applies. Specificity is the selector-weight part of that algorithm: the more specific your selector, the less likely another rule will override it.
The MDN specificity guide is explicit: browsers evaluate specificity after determining cascade origin and importance, and it is compared only between selectors from the one cascade origin and layer that has precedence. This guide walks through every part of the system: how selectors work, how specificity is calculated, how the cascade resolves conflicts, and how inheritance fits in.
CSS Selectors
A CSS selector is the pattern that tells the browser which HTML elements a rule applies to. The MDN basic selectors lesson describes it as the first part of a CSS rule — a pattern of elements and terms that identifies the subject of the selector. Selectors range from trivially simple to precisely targeted.
Type, Class, and ID Selectors
A type selector (also called tag-name or element selector) targets an HTML tag directly:
p { color: blue; }
A class selector starts with a dot (.) and targets every element with that class. Multiple classes can be chained without a space — p.highlight targets only <p class="highlight"> — and an element can carry several classes, each independently styleable. The MDN lesson gives the example of a .notebox with additional .warning or .danger classes applied to the same element for layered styling.
An ID selector starts with # and targets exactly one element per page, since id must be unique in a document. Using the same ID twice produces invalid HTML. The MDN lesson warns: "Using the same ID multiple times in a document may appear to work for styling purposes, but don't do this. It results in invalid code and will cause strange behavior in many places."
Attribute Selectors
Attribute selectors target elements based on the presence or value of an attribute. The MDN attribute selectors lesson covers two groups.
Presence and value selectors:
| Selector | Example | Description |
|---|---|---|
[attr] |
a[title] |
Elements with that attribute |
[attr=value] |
a[href="https://example.com"] |
Exact value match |
[attr~=value] |
p[class~="special"] |
Value in space-separated list |
[attr|=value] |
div[lang|="zh"] |
Exact value or value-prefixed with hyphen |
Substring (pattern) selectors:
| Selector | Example | Description |
|---|---|---|
[attr^=value] |
li[class^="box-"] |
Value starts with |
[attr$=value] |
li[class$="-box"] |
Value ends with |
[attr*=value] |
li[class*="box"] |
Value contains anywhere |
Combinators
Combinators combine two selectors to express a relationship in the DOM tree. The MDN combinators lesson defines four:
- Descendant (space):
article p— any<p>inside<article>, at any depth. - Child (
>):article > p—<p>that is a direct child of<article>only. - Next-sibling (
+):h1 + p— the<p>immediately after an<h1>. - Subsequent-sibling (
~):h1 ~ p— every<p>that follows an<h1>at the same level.
Pseudo-Classes and Pseudo-Elements
Pseudo-classes start with a single colon and target elements in a particular state — :hover, :focus, :first-child. The MDN lesson describes them as acting "as if you had applied a class to some part of your document."
Pseudo-elements start with a double colon and act "as if you had added a whole new element to the DOM." ::before and ::after are used with the content property to insert generated content. ::first-line styles the first line of an element reliably regardless of how the browser wraps text.
The Cascade
The cascade resolves conflicts when multiple CSS declarations compete for the same element and property. The MDN handling conflicts lesson lists the three factors — listed here in order of increasing importance:
- Source order — later rules override earlier ones when everything else is equal.
- Specificity — more specific selectors win over less specific ones.
- Importance — declarations marked
!importantoverride normal declarations.
Cascade Origin and Layers
Before specificity matters, the cascade origin determines which source of styles takes priority. The MDN lesson gives this complete precedence order (later over earlier):
- User agent stylesheets (browser default styles)
- Normal user stylesheets (user-defined preferences)
- Normal author stylesheets (your CSS)
- Important author stylesheets (
!importantin your CSS) - Important user stylesheets
- Important user agent stylesheets
The reversal for !important means users with accessibility needs (for example, larger text) can override author styles. The MDN specificity reference notes that origin and importance are evaluated before specificity — meaning an !important declaration from the author wins over a non-!important ID selector from the same author.
Inline styles (the style attribute in HTML) take precedence over all normal styles, but not over !important. The MDN specificity reference gives inline styles a specificity of 1-0-0-0 (ID column only).
The !important Flag
Marking a declaration with !important flips its priority in the cascade:
.override {
border: none !important; /* overrides an ID selector's border */
}
The MDN lesson advises caution: "we strongly recommend that you never use !important unless you absolutely have to. The !important flag changes the way the cascade normally works, so it can make debugging CSS problems really hard to work out, especially in a large stylesheet." One legitimate use is working within a CMS where you cannot edit the core stylesheet.
How Specificity Is Calculated
Specificity is a three-column value written as ID – CLASS – TYPE (or sometimes presented as three numbers). The MDN specificity reference defines each column:
- ID — counts ID selectors (
#example). Each adds1-0-0. - CLASS — counts class selectors (
.myClass), attribute selectors ([type="radio"]), and pseudo-classes (:hover,:nth-of-type(3n)). Each adds0-1-0. - TYPE — counts type selectors (
p,h1,td) and pseudo-elements (::before,::first-line). Each adds0-0-1.
The universal selector (*) and :where() add 0-0-0 — they match elements but do not affect specificity. Combinators (>, +, ~, space) add no specificity weight either; only the selectors they connect count.
The Cascade Order Within Specificity
The three columns are compared column by column from left to right. The MDN specificity reference gives this hierarchy:
- The ID column is compared first. A selector with one ID always beats any number of classes or types — regardless of how many classes or elements the other selector has.
- If the ID count is equal, the CLASS column is compared.
- If the CLASS count is also equal, the TYPE column is compared.
- If all three columns are equal, source order breaks the tie — the later rule wins.
The MDN lesson illustrates this with a well-known rule: "a million class selectors combined would not be able to overwrite the specificity of one ID selector."
Specificity Examples
| Selector | IDs | Classes | Types | Specificity |
|---|---|---|---|---|
h1 |
0 | 0 | 1 | 0-0-1 |
p + p::first-letter |
0 | 0 | 3 | 0-0-3 |
li > a[href*="en-US"] > .inline-warning |
0 | 2 | 2 | 0-2-2 |
#identifier |
1 | 0 | 0 | 1-0-0 |
li:nth-child(2):hover |
0 | 2 | 1 | 0-2-1 |
:not(.error) |
0 | 1 | 0 | 0-1-0 |
The MDN lesson provides an in-depth example: #outer #inner a (specificity 2-0-1) beats #outer a (1-0-1) on background-color, because the extra ID wins even though both have the same TYPE count.
:is(), :not(), and Nesting
The :is(), :not(), and :has() pseudo-classes do not add specificity themselves — but their parameters do. The MDN specificity reference explains that the weight used is the highest specificity among the parameters. This lets you game specificity:
:is(p, #override) { /* specificity: 1-0-0 — from the ID */ }
:not(#fake) { /* specificity: 1-0-0 */ }
CSS nesting (&) behaves the same as :is() for specificity purposes.
Inheritance
When a CSS property is set on a parent element, its descendants inherit that value by default — unless a more specific rule overrides it. The MDN lesson uses color and font-family as the canonical example: setting them on <body> applies them to every element inside it.
Some properties do not inherit. The MDN lesson gives width, margin, and padding as examples: "If you set a width of 50% on an element, all of its descendants do not get a width of 50% of their parent's width. If this was the case, CSS would be very frustrating to use!" Every MDN CSS property reference page lists whether the property is inherited under "Formal definition."
Five universal values control inheritance for any property:
inherit— use the parent's value (effectively "turn on" inheritance for this property).initial— use the property's initial value (the CSS specification default).revert— use the user agent's default, not the spec's initial value.revert-layer— revert to the value from the previous cascade layer.unset— act likeinheritfor inherited properties,initialfor non-inherited ones.
The shorthand all: unset resets every property on an element to its natural value, which the MDN lesson calls "a convenient way to undo changes made to styles so that you can get back to a known starting point."
How the Three Interact
The MDN lesson describes the full resolution order: first the cascade origin and importance determine which layer has precedence; then specificity resolves conflicts within that layer; then source order breaks remaining ties. Only after all of that does inheritance apply — and only for properties that received no cascade value.
The practical debugging sequence the MDN lesson recommends: ask "is this cascade, specificity, or inheritance?" The answer is usually specificity. Inspect the element in browser DevTools, look at the applied rules, note each one's origin and specificity, and remove rules until the conflict resolves.
FAQ
What is the difference between specificity and the cascade?
The cascade is the full algorithm that decides which CSS declaration wins — it evaluates origin (user agent, user, author), importance (!important), specificity, and source order. Specificity is one factor within the cascade, applied only after origin and importance. The MDN specificity reference puts it clearly: specificity is "relevant and compared only between selectors from the one cascade origin and layer that has precedence."
Why should I avoid !important?
The !important flag flips the cascade's priority order, making higher-specificity declarations lose to lower-specificity ones that carry the flag. The MDN lesson warns this "changes the way the cascade normally works, so it can make debugging CSS problems really hard to work out, especially in a large stylesheet." Only reach for it in CMS environments where you cannot edit the core stylesheet and need to override an inline style or a framework default.
Does specificity count pseudo-classes and pseudo-elements the same way?
Pseudo-classes (:hover, :first-child, :not()) add to the CLASS column (0-1-0). Pseudo-elements (::before, ::first-line) add to the TYPE column (0-0-1). Both are counted — but the :not() pseudo-class is an exception: it counts only the specificity of its arguments, not the pseudo-class itself. So :not(.error) is worth 0-1-0, and :not(#unique) is worth 1-0-0.
Can I use all: unset safely on any element?
all: unset resets every property to its natural value (inherit for inherited properties, initial for non-inherited). This is safe and useful for restoring an element to its browser default state. It removes any author styles you had applied, but it does not remove inline styles or !important declarations. The MDN lesson describes it as "a convenient way to undo changes" for debugging or resetting component styles.
How do I debug a CSS rule that is not applying?
The MDN lesson's recommended sequence: open browser DevTools, select the element, find the property in the Styles panel — DevTools shows each rule's origin (author/user-agent), specificity (displayed as a weight), and source file. If a rule is crossed out, something with higher specificity, importance, or cascade origin is overriding it. If nothing is crossed out, inheritance is likely the cause — the element may be getting its value from an ancestor. Removing rules one by one until the style appears is the fastest way to narrow it down.
Verdict
CSS specificity is a three-column weight (ID – CLASS – TYPE) that resolves selector conflicts within the same cascade origin and layer. IDs always beat classes, classes always beat element selectors, and source order breaks ties when the weight is equal. The !important flag overrides this entire system and should be a last resort. Understanding specificity — combined with knowledge of inheritance and cascade origin — is what separates CSS that behaves predictably from CSS that surprises you.
Sources
- MDN — Basic CSS selectors (developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Basic_selectors
- CC-BY-SA 2.5+)
- MDN — Attribute selectors (developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Attribute_selectors
- CC-BY-SA 2.5+)
- MDN — Combinators (developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Combinators
- CC-BY-SA 2.5+)
- MDN — Pseudo-classes and pseudo-elements (developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Pseudo_classes_and_elements
- CC-BY-SA 2.5+)
- MDN — Handling conflicts in CSS: cascade
- specificity
- and inheritance (developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Handling_conflicts
- CC-BY-SA 2.5+)
- MDN — Specificity (developer.mozilla.org/en-US/docs/Web/CSS/Guides/Cascade/Specificity
- CC-BY-SA 2.5+)
Share this guide
Related guides
- What is web accessibility (a11y)? WCAG principles and practical techniques
Web accessibility (a11y) means designing sites that anyone can use, regardless of ability. This guide explains the four WCAG principles — perceivable, operable, understandable, robust — and practical techniques: semantic HTML, keyboard navigation, alt text, and color contrast.
- Flexbox vs Grid: which CSS layout should you choose?
Flexbox and CSS Grid are two layout systems built into the browser. Flexbox is one-dimensional (a row or a column); Grid is two-dimensional (rows and columns at once). Learn how each one works, when to reach for it, and how to combine them.
- What is responsive web design? How it works (media queries, fluid grids, images)
Responsive web design (RWD) makes pages render well on every screen size. Learn how media queries, fluid grids, fluid images, and the viewport meta tag work together.