Why one CSS rule beats another
How the browser decides which style wins when two selectors target the same element, what the three-number triple means, how the modern pseudo-classes change the count, and a walkthrough of fixing the most common specificity fight.

The problem specificity solves
You write a rule. It does nothing. You inspect the element and find a strikethrough line in the styles panel. Another rule is winning. The question is always the same: why?
The answer is almost always specificity. When two rules both match an element, the browser does not pick the one that appears later in the file. It picks the one with the more specific selector. Later only wins when the two selectors are equally specific. That is the most common source of confusion in CSS, and it is the question this calculator settles.
How the triple works
Specificity is a set of three numbers, written as ids, classes, elements. Each selector adds to those buckets. An id selector like #nav adds one to the first number. A class like .menu, an attribute like [type=text], or a pseudo-class like :hover adds one to the second number. A type selector like div or a pseudo-element like ::before adds one to the third.
A compound selector adds to more than one bucket at once. The selector #nav .menu a has one id, one class, and one element, giving it a triple of 1-1-1. The selector .sidebar ul li a has three elements and one class, so it scores 0-1-3.
Compare the triples from left to right. The first number that differs decides the winner. A selector with one id beats one with nine classes, because the id column is compared first and one is greater than zero. If the ids tie, classes are compared. If those tie too, elements break it. Only when all three numbers match does the position in the file matter.
Pasting selectors and reading the ranking
Type or paste selectors into the left panel, one per line. You can also paste comma-separated selectors, or entire rule blocks including the braces and properties. Anything before an opening brace is read as a selector. The right panel ranks them from highest specificity to lowest, with the triple shown beside each one.
Press Try an example to load a small set that covers the common cases. The ranking makes it obvious which selector would win if they all targeted the same element. Press Copy to take the ranked list, or Download to save it as a file.
A real fight and how to fix it
Suppose you have a navigation bar styled with #nav a and you add a class called .active to highlight the current page. You write .active with a bold font weight, but the link stays normal. The calculator shows why. The selector #nav a has a triple of 1-0-1. The selector .active has 0-1-0. The id in the first selector outranks the class in the second, no matter how many classes you add.
The fix is to bring the losing selector up to the same level. Writing #nav a.active gives it a triple of 1-1-1, which beats 1-0-1 and applies the bold. A better fix, when you control the stylesheet, is to lower the winning selector. Replacing #nav with a class like .nav drops both rules into the class column, and the fight becomes easy to manage.
Paste both selectors into the calculator to see the triples side by side. The ranking tells you at a glance which one the browser will choose, and the gap between the numbers shows how far apart they are. A gap of one class is a small adjustment. A gap that crosses from the class column into the id column means rethinking the selector entirely.
The modern pseudo-classes
Four pseudo-classes from the current CSS specification change how the count works. They look like functions and they take selectors as arguments.
The selectors :not(), :is() and :has() each count as the most specific argument inside them. If you write :not(.foo), the :not itself adds nothing, but .foo adds one class. If you write :is(#bar, .foo), the count uses #bar because it is the most specific of the two arguments. The specificity of :has() works the same way.
Then there is :where(). Everything inside :where() counts zero, regardless of what is in it. That is the escape hatch. Design systems wrap their defaults in :where() so that any plain selector a developer writes can override them. The calculator handles all four correctly.
What does not count at all
The universal selector * adds nothing. Combinators, the space for descendant, the > for child, the + for adjacent sibling, and the ~ for general sibling, add nothing. They describe the relationship between selectors but carry no weight of their own. A selector like body > main > section > div > p looks long but scores only 0-0-5, because each part is just an element.
Inline styles, written as style on the element itself, outrank every selector. There is no triple for them because no selector can reach that level. Only the important flag beats an inline style, and important itself is not part of the specificity calculation. It is a separate mechanism that overrides the whole system. When important is the only thing that works, the honest fix is usually somewhere in the specificity table.
Order matters less than people think
A common belief is that CSS works top to bottom and later rules always win. That is only true between selectors of equal specificity. A rule at the top of a file with a higher triple beats a rule at the bottom with a lower one. The file order is the tiebreaker, not the ranking.
This is where the calculator earns its keep. When you paste two selectors and see that one outranks the other, you know that moving the rules around will not help. The only fix is to change one of the selectors. Rearranging the stylesheet is the most common time wasted on a specificity problem.
Practical habits that prevent fights
Keep selectors as flat as you can. A class on its own has a triple of 0-1-0 and is easy to override. A chain of three nested selectors with an id is hard to beat without adding another id.
Use ids for anchoring and JavaScript hooks, not for styling. Once an id enters a selector, every rule that competes with it needs an id too, and the specificity arms race begins.
When you inherit a codebase full of high-specificity selectors, :where() can help you add new defaults that stay deliberately low. Wrap your base styles in :where() and they will yield to anything already in the project.
If you maintain a library or a component set, keep your selectors in the class column and avoid ids entirely. Other developers will need to override your styles, and the easier you make that, the fewer complaints land in your inbox. A well-designed library never forces its users to match an id.
- Paste both the winning and the losing selector to see the gap.
- Lower the winner before you raise the loser.
- Treat important as a sign that the stylesheet needs work, not as a solution.
Mistakes that waste the most time
Adding classes to a losing selector when the winner has an id. You can stack ten classes and still lose, because the id column is checked first and it is already ahead. The only way across that gap is to either add an id on the losing side or remove the id from the winning side.
Assuming a deeper selector is more specific. The selector div div div div has a triple of 0-0-4. The selector .card has 0-1-0. The class wins despite being shorter, because classes outrank elements. Depth and specificity are different things.
Reaching for important as a first response. It works, but it starts a second arms race on top of the first one. A stylesheet where every other rule is marked important is harder to maintain than one where the specificities are kept deliberately low.
Nothing leaves your browser
The calculation runs entirely on your device. Your selectors are parsed in the browser, the triples are counted there, and the ranked list is built there. Nothing is sent to a server. There is no upload and no account. Close the tab and the selectors are gone.
