πŸ† US-Registered Digital Marketing Agency Trusted by 200+ brands Β· USA Β· UK Β· Canada Β· AUS
DEVELOPER TOOL

CSS Specificity Calculator β€” which selector wins?

Paste any CSS selector to see its exact specificity score, or compare two selectors to find out which one the browser will actually apply.

Paste a full CSS selector, e.g. .card > h2::before
Selector A specificity (0-0-0-0)
0-0-0-0
 
0
Inline style
0
ID selectors
0
Class/attr/pseudo-class
0
Elements/pseudo-elements
Tip: add a Selector B above to see which one wins a direct conflict.
A vs B COMPARISON
Enter both selectors and click Calculate Specificity.
Advertisement

The CSS specificity calculator on this page parses any selector you paste in and breaks it down into the four categories the CSS specification actually uses to decide which rule wins: inline styles, ID selectors, classes/attributes/pseudo-classes, and elements/pseudo-elements. Instead of counting characters yourself, you get an instant 0-0-0-0 style score plus a plain-English breakdown of exactly what contributed to it.

Specificity fights are one of the most common sources of "why won't my CSS apply" bugs, and at Arb Digital we've spent enough hours tracing a stubborn override back to a stray ID selector three files deep that we built this calculator to save that time for anyone else.

What This CSS Specificity Calculator Does

You paste a selector β€” anything from a single class like .button to a long chain like #sidebar .widget:not(.hidden) ul li a::after β€” and the tool walks through every token in it, categorizing each one into the correct specificity bucket. It then reports a four-part score, commonly written as 0-0-0-0 (inline, IDs, classes, elements), along with a written explanation of which specific parts of your selector counted and why. If you add a second selector, the tool also tells you which of the two would win if both targeted the same element with conflicting declarations.

How to Use the Specificity Calculator

  1. Paste Selector A into the first field β€” copy it straight from your stylesheet, including combinators like >, spaces, and pseudo-classes.
  2. Optionally paste Selector B β€” the rule you suspect is conflicting with Selector A.
  3. Click "Calculate Specificity." The tool tokenizes both selectors and scores them.
  4. Read the four-part score for Selector A in the result panel β€” inline count, ID count, class/attribute/pseudo-class count, and element/pseudo-element count.
  5. Check the comparison panel to see which selector wins, and by which category the tie (if any) was broken.
  6. Adjust your real CSS based on the explanation β€” often the fix is adding one class rather than reaching for an ID or !important.

How Specificity Is Calculated

CSS specificity is a weighted score with four tiers, most commonly notated as a-b-c-d: a counts inline styles (a style attribute, which always outranks any selector in an external or embedded stylesheet); b counts ID selectors (#example); c counts classes (.example), attribute selectors ([type="text"]), and pseudo-classes (:hover, :focus, :nth-child()); and d counts type/element selectors (div, a) and pseudo-elements (::before, ::after). Universal selectors (*), combinators (>, +, ~, and descendant spaces), and the negation pseudo-class's own weight don't add anything by themselves β€” but whatever is written inside :not() and :is() does count at the weight of its most specific argument, while :where() is a special case that always contributes zero, regardless of what's inside it. The browser compares these four numbers left to right: a higher inline count always wins outright, then a higher ID count, then classes, then elements β€” it never "carries over" like counting in a different number base. The authoritative technical reference for this algorithm is MDN's Specificity documentation.

Advertisement

The :not(), :is(), and :where() Gotchas

These three functional pseudo-classes trip up even experienced developers because they look similar but behave very differently for specificity purposes. :not(.foo) and :is(.foo, #bar) both take on the specificity of their single most specific argument β€” so :is(.foo, #bar) is scored as if you'd written #bar, because an ID outweighs a class. This means wrapping selectors in :is() for convenience can silently make your rule far more specific than it looks. :where() was introduced specifically to solve that problem: no matter what you put inside its parentheses, :where() always contributes zero specificity, which makes it the tool of choice when you want to group selectors for organization without accidentally inflating your specificity score.

Reading a Specificity Score in Practice

A score of 0-1-0-1 beats 0-0-3-0 because the comparison stops at the first differing column β€” one ID always beats any number of classes, no matter how many. This is exactly why "just add another class" sometimes doesn't work to override an ID-based rule, and why the common advice is to avoid ID selectors in stylesheets entirely, reserving IDs for JavaScript hooks and anchor links instead. It's also why chaining classes (.card.featured.highlighted) can eventually out-specify a simpler rule even without touching IDs β€” each repeated class token still adds one to the class column.

Why Specificity Wars Cause Fragile CSS

Teams that don't have a shared specificity strategy often end up in an "arms race": one developer adds an ID to force an override, the next developer can't beat that ID so they reach for !important, and a few months later half the stylesheet is !important declarations that are nearly impossible to safely change. A specificity calculator like this one is genuinely useful preventative tooling here β€” before you add another layer to a rule, check what the resulting score actually is, and consider whether restructuring the selector (or using a single, well-scoped class) solves the conflict without escalating the fight.

  • Prefer classes over IDs for styling β€” reserve IDs for scripting and fragment links.
  • Keep selector chains short; a three-level chain is usually a sign the underlying HTML or component structure could be simplified.
  • Use :where() when you need to group selectors without raising specificity.
  • Treat !important as an emergency escape hatch, not a first response to a losing selector.
Fighting CSS specificity across a whole client site?

Arb Digital builds clean, maintainable front-end code from the ground up so you never have to untangle a specificity war again. Take a look at our web design work or keep browsing our free tools below.

Our Web Design Services All Free Tools

How Browser Extensions and DevTools Show Specificity

Most modern browser DevTools panels display computed styles with the winning rule highlighted and losing rules struck through, but they rarely show you the raw specificity numbers side by side the way this calculator does β€” you have to infer the reasoning yourself by comparing selectors visually. That's fine once you've internalized the four-tier system, but it's genuinely easy to misjudge at a glance, especially when one selector uses an attribute selector (which many developers forget counts the same as a class) and the other uses a chain of three plain elements. Keeping a tool like this bookmarked for the moments when a style "should" apply but doesn't saves the trial-and-error cycle of adding !important, refreshing, removing it, and trying a different selector instead.

Specificity in Component-Based CSS (BEM, Utility Classes, CSS-in-JS)

Different popular CSS methodologies handle specificity in deliberately different ways, and understanding the underlying score explains why. BEM (Block-Element-Modifier) intentionally keeps every selector to a single class whenever possible, so specificity stays flat and predictable across an entire codebase β€” no selector should ever meaningfully outrank another, which avoids the arms-race problem entirely. Utility-class frameworks like Tailwind CSS lean on this same principle even harder: nearly every rule is a single class selector with identical specificity, so the actual "winner" in a conflict is almost always decided by property mutual-exclusivity and source order rather than specificity differences. CSS-in-JS libraries, by contrast, often generate long, unique, auto-hashed class names specifically so that specificity stays low and uniform while still guaranteeing no two components accidentally collide. In every one of these approaches, the underlying goal is the same: keep the class-selector column doing all the work, and avoid ever needing an ID selector or an !important escape hatch to win a fight.

A Worked Example: Debugging a Real Override Failure

Say you've written .card .title { color: navy; } but a client stylesheet elsewhere declares #page-content h2 { color: red; }, and your text stubbornly stays red. Run both through the calculator: .card .title scores 0-0-2-0 (two classes), while #page-content h2 scores 0-1-0-1 (one ID plus one element). Because the ID column is compared before the class column, 0-1-0-1 beats 0-0-2-0 outright, regardless of how many more classes you stack onto your own selector. The practical fix isn't to escalate with your own ID or an !important β€” it's usually to add a class directly to the element you're targeting and write a single-class selector that's more specific in context, or, better yet, raise the conversation with whoever owns that ID-based rule about removing IDs from the shared stylesheet's styling layer altogether.

Common Mistakes to Avoid

  • Assuming selector length equals specificity. A long descendant chain of elements (div div div div span) can still lose to a single class, because element selectors are the lowest-weighted tier.
  • Forgetting inline styles always win. A style="color:red" attribute beats any external stylesheet rule regardless of how many IDs it has β€” the only things that can override inline styles are another inline style or !important.
  • Not accounting for source order on ties. When two selectors have identical specificity, the one that appears later in the cascade (or later in the same file) wins β€” the calculator shows you the score, but source order still breaks true ties.
  • Overusing :is() without checking its weight. Wrapping a selector list in :is() silently inherits the highest specificity in that list, which can surprise you later.
  • Reaching for !important as a first fix. It solves the immediate problem but makes every future override harder, since !important rules can only reliably be beaten by other !important rules.

Related Free Tools From Arb Digital

Once your selector wins the cascade, make sure the value it sets is right β€” try the CSS Clamp Generator for fluid sizing, the CSS Grid Generator and CSS Flexbox Generator for layout, and the CSS Minifier to ship a smaller stylesheet once it's debugged. If you're moving markup between formats, the HTML to Markdown Converter is handy too. See everything in our free online tools hub.

Frequently Asked Questions

What is CSS specificity?

Specificity is the algorithm browsers use to decide which of several conflicting CSS rules applies to an element, based on the types of selectors used β€” inline styles, IDs, classes/attributes/pseudo-classes, and elements/pseudo-elements, compared in that order.

Does :not() add to specificity?

The :not() pseudo-class itself adds nothing, but whatever selector is written inside its parentheses is counted at its normal weight - so :not(#id) still adds the specificity of an ID selector.

Why does :where() always score zero?

:where() was specifically designed as a zero-specificity version of :is(), so authors can group selectors for organizational convenience without any risk of accidentally increasing specificity and breaking an override elsewhere.

Does the order of CSS rules in my file matter?

Yes, but only as a tiebreaker. If two selectors have identical specificity, whichever rule appears later in the cascade (later in the same file, or in a later-loaded stylesheet) wins. If specificity differs, order doesn't matter at all.

Is an ID selector always stronger than any number of classes?

Yes. Specificity is compared column by column, not summed into one number, so a single ID selector always outranks any quantity of class, attribute, or pseudo-class selectors in the next column, no matter how many are chained together.

Should I ever use !important?

Sparingly. It overrides normal specificity comparisons entirely, which makes future overrides hard to reason about. It's best reserved for narrow, well-documented exceptions rather than a routine way to win specificity conflicts.

This tool runs entirely in your browser β€” no selectors or code are sent to any server.

Advertisement

πŸ‘‹ Hey! Want to grow your business? Ask me anything β€” a free marketing proposal is on the table!