Recent chats
ASKGEEZ Q&A GUIDETechnology & devices

How is specificity calculated when several CSS rules match one element?

Quick answer

Compare each matching selector using three columns: IDs, class-like selectors (classes, attributes, and pseudo-classes), then type selectors and pseudo-elements. The first column where the values differ decides the comparison; a higher value there wins, regardless of later-column totals. Do not judge by selector length. First check that origin, importance, and cascade layers do not already decide the result; specificity applies after those considerations. If specificity ties, later cascade rules, including source order when applicable, resolve the result. [S1]

LET’S WORK THROUGH IT

The question, the details, the next step.

Question

Several rules match my element. What exactly do I count?

Geez

Write each selector as a three-part score, in this order: IDs, class-like selectors, type selectors. Count IDs in the first column. Count classes, attribute selectors, and pseudo-classes in the second. Count element names and pseudo-elements in the third. [S1]

Compare those columns from left to right, not by adding them into one total. A larger number in an earlier column outweighs any number in a later one. That is why a selector’s visual length—or the total number of marks you notice—is not a reliable way to choose the winner.

Before comparing scores, make sure the declarations are genuinely at the same stage of the cascade. Origin, importance, and cascade layer are considered before specificity. [S1] The example below assumes all four declarations have the same origin, importance, and layer, and that every selector matches the same element.

Follow-up question

Can you show the calculation with rules that all match?

Geez

Assume the target is a main element with classes card and panel, the attribute data-state="open", and an ancestor with ID app. These rules all match that target:

  • #app .card → IDs: 1; class-like: 1; types: 0; score: (1, 1, 0)
  • .panel[data-state="open"] → IDs: 0; class-like: 2; types: 0; score: (0, 2, 0)
  • main .card → IDs: 0; class-like: 1; types: 1; score: (0, 1, 1)
  • .card → IDs: 0; class-like: 1; types: 0; score: (0, 1, 0)

Compare the first two rules: #app .card wins over .panel[data-state="open"] because its ID column is higher. Its later-column values do not change that. Between the second and third rules, the ID columns tie at zero, so compare the class-like column: two beats one. The final two rules tie in the first two columns; main .card wins because it has one type selector. This comparison assumes the cascade conditions above are equal. [S1]

Follow-up question

What if two selectors have the same score? And does the longest selector win?

Geez

No: length alone does not determine specificity. For example, under the same cascade assumptions, .card and .panel both score (0, 1, 0). When specificity is equal, the cascade continues; source order can resolve the result when applicable. [S1] So if those otherwise-competing declarations tie, check which declaration comes later in the relevant cascade ordering rather than adding up selector characters.

Keep the example’s limits in mind: the score comparison answers only the specificity part. It does not establish which declaration wins when origin, importance, or layers differ, nor does it account for whether a selector actually matches. Check those conditions separately before relying on the comparison.

Follow-up question

Can you give me a repeatable way to compare a real pair of rules?

Geez

Use this short worksheet for each declaration that sets the property you are investigating:

  1. Confirm that both selectors match the same element and set the same property.
  2. Compare origin, importance, and cascade layer first; do not use specificity to skip those checks. [S1]
  3. For each matching selector, record (IDs, class-like, types).
  4. Compare scores left to right. Stop at the first column that differs; the higher value wins that comparison.
  5. If all three values tie, continue checking the cascade, including source order when applicable. [S1]

Copyable notes:

Element/property: [element] / [property]
Assumptions: same origin, importance, and layer? [yes/no/check]
Rule A selector: [selector]   Score: ([IDs], [class-like], [types])
Rule B selector: [selector]   Score: ([IDs], [class-like], [types])
First differing column: [IDs / class-like / types / none]
Next check if needed: [cascade ordering / source order]

This helps explain a comparison without treating specificity as a single total or assuming the longest-looking rule wins.

A practical checklist

  • Confirm both declarations match the element and set the property you are comparing.

  • Check origin, importance, and cascade layer before specificity.

  • Count IDs, class-like selectors, and types in separate columns.

  • Compare from the ID column left to right; stop at the first difference.

  • If all columns tie, continue through the cascade and check source order when applicable.

A few more useful answers

Do attribute selectors count in the same column as classes?

Yes. Attribute selectors belong in the class-like column, alongside classes and pseudo-classes. [S1]

Does one ID lose to several classes?

Not in the specificity comparison: an ID in the first column outweighs any number in a later column. Check origin, importance, and cascade layer before applying that comparison. [S1]

Does a specificity score by itself prove which rule wins?

No. Specificity is considered after origin, importance, and cascade layers; equal specificity also requires further cascade comparison, including source order when applicable. [S1]

Sources

References used for this guide.

  1. [S1] Specificity — MDN
    developer.mozilla.org · Checked

YOUR QUESTION. YOUR DETAILS.

Let’s work through your situation.

Get a tailored answer or help with your next task. Starts at $1, with a quote before you pay.

Ask your own question
← Back to Geez at Work