CSS Specificity Calculation - Real Case Studies

Walk through real CSS specificity calculations: nested classes, :is() with IDs, :where() defaults, !important conflicts, and how the cascade resolves them step by step.

Concepts

Detailed Explanation

Three Worked Examples

Specificity is just arithmetic on a triple (IDs, classes, types). The hard part is spotting which selectors contribute. Here are three real conflicts and their resolutions.

Case 1 — Card Title Color

.card .title { color: #333; }              /* (0, 2, 0) */
article h2.title { color: #000; }          /* (0, 1, 2) */

For <article><h2 class="title"> inside a .card, both rules match. Compare left-to-right:

  • IDs: 0 vs 0 → tie
  • Classes: 2 vs 1 → .card .title wins

The title is #333.

Case 2 — :is() Drags in an ID

.btn { padding: 8px 16px; }                            /* (0, 1, 0) */
:is(#sidebar, .panel) .btn { padding: 4px 8px; }       /* (1, 2, 0) */

The second rule has (1, 2, 0) because :is() borrows the highest specificity from its argument list — and #sidebar is in there. Even buttons inside .panel (no ID nearby) get the (1, 2, 0) score, which can blow up your other overrides. Refactor to :where(#sidebar, .panel) .btn if you want the lower score.

Case 3 — !important vs Inline

<p style="color: red;">Hello</p>
p { color: blue !important; }

!important in a stylesheet beats inline styles. Order of precedence (highest wins):

  1. !important on user-agent (UA) styles
  2. !important on user styles
  3. !important on author styles ← wins here
  4. Author inline style (style="...")
  5. Author selector-based styles
  6. User styles
  7. UA styles

So the paragraph renders blue.

Tooling

Browser DevTools shows specificity scores on every matched rule (Chrome's Computed pane, Firefox's Inspector). When a rule "should" win but doesn't, count the IDs/classes/types on each competing selector and compare left-to-right.

Mental Shortcut

If you ever see specificity above (0, 3, 0), pause and ask whether you really need it. Most production CSS lives at (0, 1, x). Anything higher is usually a sign of an architectural smell or an overly enthusiastic :is().

Use Case

Walk through specificity calculations when debugging "why isn't my style applying?" issues. Common cases: design-system overrides being beaten by component CSS, :is() unexpectedly raising specificity, and !important arms races between vendor stylesheets and your own. Knowing the rules turns guesswork into a 30-second check.

Try It — CSS Selector Reference

Open full tool