CSS Selectors

Published Updated

A CSS selector is the part of a rule that decides which elements on the page get styled. Get the selector right and the rest of CSS calms down a lot. Most of the styling bugs you will chase early on come from a selector matching too much, too little, or losing a fight with another rule you forgot was there.

Selectors Target Elements by Pattern

Every rule starts with a selector, then a block of declarations the browser applies to whatever the selector matches. You can match by tag name, by a shared class, or by a unique id:

p {
  line-height: 1.5;
}

.card {
  padding: 1rem;
}

#site-header {
  position: sticky;
}

The class selector is the one you will use most of the time, because a class can sit on as many elements as you want. Keep your selectors readable and you will usually be able to predict what they hit before you even reload the page.

A type selector like p reaches every paragraph in the document, which makes it right for a genuine document-wide default and wrong the moment one paragraph needs to look different. An id selector sits at the other extreme, matching only the one element that carries it, since an id is supposed to be unique on the page. Classes live in between, giving you a repeatable hook for however many elements share that look.

Combinators Describe Relationships

Selectors get more useful when you combine them to describe where an element lives in the document. A space between two selectors means "any descendant," and the > symbol means "direct child only":

.menu a {
  color: var(--color-link);
}

.menu > li {
  margin-block: 0.25rem;
}

That first rule reaches every link inside .menu, however deeply nested. The second rule only touches list items that are immediate children of .menu, which is exactly what you want when nesting could otherwise leak your styles somewhere surprising.

Combinators add no specificity of their own, only the selectors on either side of the symbol count toward the score. So .menu > li and .menu li carry identical weight even though the child combinator is far more precise about what it hits. Picking the tighter combinator is about correctness, not about winning a specificity fight.

Specificity Decides the Winner

When two rules style the same element and set the same property, the browser scores each selector and the higher score wins. An id counts for more than a class, and a class counts for more than a tag:

#promo .button {
  background: rebeccapurple;
}

.button {
  background: gray;
}

The button inside #promo ends up purple even though the gray rule comes later in the file, because the id pushes the first selector's score higher. When you understand how that number gets built, you stop reaching for !important to force a result.

That score is not one number, it is three tiers compared in order: ids first, then classes and pseudo-classes, then tags. A selector only needs to win the leftmost tier to beat a rival, no matter how many classes the losing selector piles on afterward.

The Gotcha: Fixing a Selector Bug by Adding More Selectors

When a rule will not apply the way you expect, the instinct is to bolt on another class, another !important, or a longer chain of ids to force a win. That usually buries the real problem instead of fixing it. The actual issue is almost always that an earlier, more specific rule already matches the element, or that your selector does not match what you think it matches. Check what is actually winning before adding anything new, since the real fix is normally one small change rather than an escalating specificity war.

Topics

TopicWhat it coversGuide
Selector basicsType, class, and id selectors and when to use eachSelector Basics
CombinatorsDescendant, child, and sibling relationshipsCombinators
Pseudo-classesState and position selectors like :hover and :nth-child()Pseudo-classes
SpecificityHow the cascade scores selectors and resolves conflictsSpecificity
  • CSS guide for the full language overview
  • HTML guide so your selectors have real structure to target

Frequently Asked Questions

Are CSS selectors case-sensitive?

Element names are not, so DIV and div both match. Class and id names are case-sensitive in HTML documents, which is why myBox and mybox are different selectors and a capitalisation slip produces a rule that silently matches nothing.

Do complex selectors slow a page down?

Almost never at the scale of a normal page. Browsers match selectors right to left and are heavily optimised for it. Readability and predictable specificity are far better reasons to simplify a selector than performance is.

Can a CSS selector match an element based on its text?

No. Selectors match structure, attributes, and state, never the text a node contains. Matching on text needs a class added by the author or by JavaScript. This is the most common thing people expect selectors to do and they cannot.

What is the difference between a selector and a rule?

The selector is the pattern before the braces that decides which elements are targeted. The rule is the whole block: the selector plus the declarations inside it. One rule can carry several selectors separated by commas.

Can you match an element on more than one attribute at once?

Yes. Attribute selectors chain, so writing two in a row requires both to match on the same element. They also support matching on a prefix, suffix, or substring, which is how you target every link to a particular domain or every file of one type.

Sources

  1. [1]
    Specificity
    (developer.mozilla.org)
  2. [2]
    Class selectors
    (developer.mozilla.org)
  3. [3]
    Descendant combinator
    (developer.mozilla.org)