CSS Specificity Calculator

Demystify the CSS cascade and resolve selector conflicts with surgical precision. Inspect exact ID, Class, and Element weight scores for complex nested selectors, and discover how to maintain clean, scalable stylesheets without resorting to !important.

100% Free & Client-SideID / Class / Element TiersPseudo-Class & Element ParsingComma-Separated SupportCascade Layer Guide
HiFi ToolKit Logo

CSS Specificity Calculator

For comma-separated selectors, the highest specificity is shown.
0
IDs
0
Classes, Attributes, Pseudo-classes
0
Elements, Pseudo-elements
Total Specificity: 0,0,0

The Definitive Guide to CSS Specificity and Cascade Architecture

Master the scoring rules that govern how web browsers apply conflicting styles, learn why !important poisons codebases, and harness modern tools like :where() and @layer.

What is CSS Specificity and Why Does It Matter?

Every web developer has experienced the frustration of writing a CSS rule, refreshing the browser, and seeing absolutely nothing change. You inspect the element in Chrome DevTools only to discover your rule is crossed out with a strikethrough because another rule overruled it.

This phenomenon is governed by CSS Specificity. Specificity is the weight assigned to a given CSS selector by the browser engine. When multiple selectors target the exact same HTML element and declare conflicting properties (such as color: red; versus color: blue;), the selector with the highest specificity score always wins.

Without a solid grasp of specificity, developers often reach for the nuclear option: slapping!important onto declarations. This temporarily forces the style to apply, but it sets off a vicious cycle of "specificity wars" that turns stylesheets into unmaintainable spaghetti code.

The 4-Tier Specificity Score Matrix

Specificity is calculated as a 4-part tuple represented as (A, B, C, D):

Column A (1, 0, 0, 0)
Inline Styles

Styles written directly in HTML markup via style="...". Outweighs all external CSS selectors.

Column B (0, 1, 0, 0)
ID Selectors

Selectors referencing element IDs (e.g. #nav-bar, #header). Immensely powerful.

Column C (0, 0, 1, 0)
Classes & Attributes

Classes (.btn), attributes ([type="submit"]), and pseudo-classes (:hover).

Column D (0, 0, 0, 1)
Elements & Pseudo

HTML tags (div, p, h1) and pseudo-elements (::before, ::after).

The Left-to-Right Comparison Rule (Debunking the Base-10 Myth)

A dangerous misconception among beginners is that specificity works like standard decimal numbers (where 10 classes = 100 points, equaling one ID). This is completely false!

Specificity is compared column by column from left to right:

/* Selector 1: One ID */
#main-nav { color: green; }              /* Score: (0, 1, 0, 0) -> WINS! */

/* Selector 2: Eleven chained classes */
.page .header .nav .menu .item .link .active .primary .large .bold .glow {
  color: red;                             /* Score: (0, 0, 11, 0) -> LOSES! */
}

Because Column B has 1 in Selector 1 and 0 in Selector 2, Selector 1 wins immediately. The browser never even glances at Column C!

Modern CSS Superpowers: :where() and Cascade Layers (@layer)

In modern web architecture (CSS specifications 2022-2026), two incredible features eliminate specificity headaches:

1. The :where() Zero-Specificity Hack

Wrapping any selector inside :where() gives it a specificity of (0, 0, 0, 0). This is universally used by CSS reset libraries (like Tailwind and Normalize) so component styles can override defaults effortlessly:

/* Targets elements with ZERO specificity */
:where(header nav ul li a) {
  text-decoration: none;
}

/* Overrides the above effortlessly */
a {
  text-decoration: underline;
}

2. CSS Cascade Layers (@layer)

Cascade layers allow you to explicitly declare the precedence order of your styles. Styles in a higher layer (e.g. components) always beat styles in a lower layer (e.g. reset), regardless of selector specificity:

@layer reset, base, components, utilities;

@layer components {
  .btn { background: blue; } /* WINS over reset #id */
}

@layer reset {
  #app .btn { background: red; }
}

Selector Specificity Reference Table

Selector ExampleInlineIDsClasses / Attr / PseudoElements / PseudoScore Tuple
* (Universal)0000(0, 0, 0, 0)
div, p0001(0, 0, 0, 1)
.card, [disabled], :hover0010(0, 0, 1, 0)
div.card:hover::before0022(0, 0, 2, 2)
#main-header0100(0, 1, 0, 0)
nav#nav ul.links li a:hover0123(0, 1, 2, 3)
style="..."1000(1, 0, 0, 0)

Frequently Asked Questions (FAQs)

CSS Specificity is the algorithm web browsers use to determine which CSS rule applies to an element when multiple competing rules match. Specificity is calculated as a 3-part or 4-part score (Inline, ID, Class/Attribute/Pseudo-class, Element/Pseudo-element). When conflicts occur, the rule with the higher score in the highest-weighted category wins, regardless of rule order.

The classic specificity hierarchy breaks down into four tiers: Inline styles have the highest weight (1,0,0,0). ID selectors (#header) carry (0,1,0,0). Classes (.btn), attribute selectors ([type='text']), and pseudo-classes (:hover) carry (0,0,1,0). Type/element selectors (div, p) and pseudo-elements (::after) carry (0,0,0,1). Combinators (+, >, ~) and the universal selector (*) contribute (0,0,0,0).

No! This is a widespread myth. Specificity is not evaluated in base-10 arithmetic. An ID selector (0,1,0,0) will always defeat an element with 10, 20, or even 100 chained class names (0,0,100,0). The comparison works from left to right: column 1 (ID) is checked first, and only if IDs are tied does the browser compare column 2 (classes).

The modern CSS :where() pseudo-class takes a selector list and evaluates with exactly zero specificity (0,0,0,0). For instance, :where(nav#menu .list a:hover) still targets the specific DOM path, but its specificity score is completely stripped to zero, making it trivially easy to override in downstream stylesheets without !important.

While :where() always sets specificity to (0,0,0,0), :is() takes on the specificity of its most specific selector argument. For example, :is(h1, #hero-title) will assign the specificity of #hero-title (0,1,0,0) to both headings.

The !important declaration overrides the standard specificity cascade by placing the property into a separate 'author important' origin. Once an !important rule is written, overriding it requires writing another rule with higher specificity that also contains !important, leading to fragile specificity wars and unmaintainable code.

Modern CSS cascade layers (@layer) fundamentally change cascade resolution. Layer order takes precedence over selector specificity! A low-specificity selector in a higher-priority layer will beat a high-specificity selector in a lower-priority layer, allowing developers to safely reset component styles.

Yes. If two conflicting CSS rules targeting the same element have the exact same specificity score (e.g., two rules with (0,0,1,0)), the browser applies the tie-breaker rule: the rule declared latest in the stylesheet wins.

Explore Related Tools

Hand-picked utilities and calculators related to this tool.

CSS Tools