CSS Specificity + Selectors Cheat Sheet
CSS can feel unpredictable when styles don’t apply the way you expect, but most of the time there’s a logical reason behind it. Understanding how selectors work — and how specificity determines which rule wins — turns CSS from something frustrating into something controlled and intentional. This guide breaks down the core selector types, explains how specificity is calculated, and shows practical examples so you can write cleaner styles and debug issues with confidence instead of guesswork.
CSS specificity determines which rule “wins” when multiple selectors target the same element. It works like a scoring system. The higher the score, the more powerful the selector. Inline styles have the highest specificity, followed by IDs, then classes/attributes/pseudo-classes, and finally type selectors (like div, p, h1).
Think of specificity like a hierarchy:
- Inline styles → strongest
- ID selectors → very strong
- Class selectors / attribute selectors / pseudo-classes → medium
- Type selectors → weakest
p { color: blue; } .text { color: green; } #main { color: red; }
If a <p> inside #main also has
class="text", the text will be red. The ID selector beats
the class and the type selector because it has higher specificity.
Attribute selectors allow you to target elements based on attributes instead of classes or IDs. These are incredibly useful when styling forms or dynamic elements.
Basic syntax looks like this:
input[type="text"] { border: 2px solid blue; }
This selects only text inputs, not checkboxes or radio buttons. Attribute selectors can also match partial values:
a[href^="https"] { color: green; }
The ^= means “starts with.” So this targets links that
begin with https.
[attr$="value"]→ ends with[attr*="value"]→ contains[attr~="value"]→ contains whole word
Attribute selectors have the same specificity level as class selectors. That means they beat type selectors but lose to IDs.
Pseudo-classes target elements in a specific state. They don't select based on structure — they select based on behavior or condition.
The most common example is :hover:
button:hover { background-color: black; color: white; }
This applies only when the mouse is hovering over the button.
Another critical one is :focus, especially for
accessibility:
input:focus { outline: 3px solid orange; }
This activates when a user tabs into an input field. It's essential for keyboard users.
Pseudo-classes have the same specificity weight as classes. So:
button:hover
has the same specificity level as:
.button
This is why order in your stylesheet matters when specificity is equal.
Combining selectors increases specificity and allows you to target elements more precisely.
For example:
nav a { color: blue; }
This targets all <a> elements inside
<nav>.
Now compare that to:
nav a.active { color: red; }
This selector is more specific because it includes a class. It targets
only links inside nav that also have the class
active.
You can also combine IDs and classes:
#navbar .nav-link { font-weight: bold; }
This targets elements with class nav-link inside the
element with id navbar.
Each additional class, attribute, or pseudo-class increases specificity. But stacking too many can make debugging harder — so keep selectors intentional, not complicated.
When a style isn't applying, it's almost always one of these reasons:
- A more specific selector is overriding it.
- The rule appears earlier in the stylesheet.
- The selector is incorrect.
- The element doesn't actually match the selector.
Example:
p { color: blue; } .article p { color: green; }
If your <p> is inside .article, it
will be green. The second selector is more specific.
Another common issue is order:
.button { background: red; } .button { background: blue; }
The button will be blue because the second rule appears later.
When debugging:
- Inspect the element in DevTools.
- Check which rule is crossed out.
- Look at specificity scores.
- Confirm the element actually matches the selector.
Once you understand specificity and selector types, CSS stops feeling random. It becomes predictable. And that’s when layout bugs start to feel solvable instead of mysterious.