In the last article, you saw the shape of a CSS rule: a selector, then a block of declarations. The declarations are the easy part — color: blue; does exactly what it says. The selector is where the real skill lives, because it decides which elements get the styles. Target the wrong elements and your page turns into a mess; target the right ones and everything falls neatly into place.
This article is all about selectors. We’ll cover the three you’ll reach for most, how to combine them, the special “pseudo-class” selectors like :hover, and finally how CSS decides which rule wins when several of them apply to the same element.
Element selectors
The simplest selector targets elements by their tag name. You write the tag name, and the rule applies to every element of that type:
p {
color: #333;
}
h1 {
font-size: 32px;
}
The first rule styles every <p> on the page; the second styles every <h1>. This is perfect for site-wide defaults — “all paragraphs should be dark grey,” “all headings should be this size.” But it’s a blunt instrument: it sweeps up every element of that type at once, with no way to single one out. For that, you need classes and ids.
Class selectors
A class selector targets elements that have a specific class attribute. You write a dot (.) followed by the class name. Recall from the HTML attributes article that a class is a label you can put on any element, and many elements can share it.
In your HTML:
<p class="intro">This is the introduction.</p>
<p>This is a normal paragraph.</p>
In your CSS:
.intro {
font-weight: bold;
font-size: 20px;
}
The .intro selector only targets elements with class="intro" — so the first paragraph gets bold, larger text, while the second is left untouched. This is the everyday workhorse of CSS. You’ll create classes for buttons, cards, headers, alerts — anything you want to style as a group.
Classes are your main styling tool
In real projects, the vast majority of your styling happens through class selectors. They’re flexible (any element can have any class), reusable (give ten buttons the class button and style them all in one rule), and they keep your intent clear. So when you’re deciding how to target something, reach for a class first — element selectors for broad defaults, classes for everything specific.
ID selectors
An id selector targets the single element with a specific id attribute. You write a hash (#) followed by the id name:
<div id="header">...</div>
#header {
background-color: #00838f;
}
Because an id must be unique on the page (only one element can have it), an id selector always targets exactly one element. That sounds handy, but in practice most developers prefer classes for styling, even for one-off elements. Why? Classes are more flexible, and id selectors have a side effect on the “cascade” (which we’ll get to in a moment) that can make your styles harder to manage.
Prefer classes over ids for styling
You can style with id selectors, but the common advice is to use classes for styling and save ids for other jobs — like linking to a section (href="#contact") or letting JavaScript grab a specific element. For styling, a class can do everything an id can, with fewer headaches. It’s not a hard rule, but get into the habit of styling with classes and you’ll sidestep a whole category of tricky problems down the road.
Combining and grouping selectors
Selectors get more powerful when you combine them.
Group selectors with a comma to apply the same styles to several at once:
h1, h2, h3 {
font-family: Georgia, serif;
}
That one rule styles all three heading levels. Without the commas, you’d have to repeat the declarations three times.
Combine selectors to be more specific. Putting selectors next to each other with a space means “an element inside another element”:
.card p {
color: grey;
}
This targets any <p> that sits inside an element with the class card — and leaves paragraphs elsewhere alone. This “descendant” combination is extremely common: it lets you style elements based on where they sit in the page.
You can also stick selectors together with no space to mean “an element that matches both”:
p.intro {
font-style: italic;
}
This targets only <p> elements that also have the class intro — not other elements with that class, and not other paragraphs.
Pseudo-classes: styling based on state
Some of the most useful selectors target elements based on their state — what the user is doing to them right now. These are called pseudo-classes, and they’re written with a colon (:).
The classic one is :hover, which applies while the user’s mouse is over an element:
a:hover {
color: red;
}
Now links turn red the moment you hover over them — the kind of small interactive touch that makes a page feel alive. Here are a few common pseudo-classes:
| Pseudo-class | Applies when… |
|---|---|
:hover |
The mouse is over the element |
:focus |
The element is focused (e.g. a clicked-into input) |
:active |
The element is being clicked |
:first-child |
The element is the first child of its parent |
:last-child |
The element is the last child of its parent |
These let you respond to interaction and position without a single line of JavaScript — it’s pure CSS.
The cascade: which rule wins?
Here’s the question that trips up every beginner sooner or later: what happens when two rules try to style the same element in different ways? For example:
p {
color: black;
}
.intro {
color: blue;
}
If a paragraph has class="intro", both rules apply — one says black, one says blue. So which one wins? This is exactly what the “Cascading” in CSS refers to, and it comes down to specificity: more specific selectors beat less specific ones.
The rough order, from weakest to strongest:
- Element selectors (
p,h1) — least specific. - Class selectors (
.intro,:hover) — more specific. - ID selectors (
#header) — most specific.
So in the example above, .intro (a class) beats p (an element), and the paragraph turns blue. A class is more specific than a plain tag, so it wins.
When two rules tie, the last one wins
If two selectors have the exact same specificity, the one written later in the CSS wins — so order matters once specificity is tied. This is why a style sometimes seems to “not work”: another rule of equal or higher specificity is overriding it. When a style refuses to apply, the cause is almost always the cascade — something more specific, or something later, is winning. Get this one concept and you’ll save yourself hours of confusion.
There’s also !important, a way to force a declaration to win no matter the specificity. Avoid it. It’s a sledgehammer that makes your styles hard to manage, and reaching for it is usually a sign that something in your selectors needs rethinking instead.
Wrapping up
Selectors are the heart of CSS, and now you can target elements precisely:
- Element selectors (
p) style every element of a type — good for broad defaults. - Class selectors (
.intro) style any element with that class — your main, everyday tool. - ID selectors (
#header) style one unique element — but prefer classes for styling. - Group selectors with commas, combine them with spaces (descendant) or no space (matches both) to be precise.
- Pseudo-classes like
:hoverand:focusstyle elements based on state — interactivity with no JavaScript. - The cascade decides which rule wins, based on specificity (id > class > element), with later rules breaking ties.
Master selectors and you can drop your styles exactly where you want them — which is most of the battle in CSS. Next, we’ll get into one of the most fun parts of styling: colors and backgrounds — the many ways to specify color, and how to fill elements with solid color, gradients, and images.