Choosing what to style
A selector is the part of a rule that decides which elements it applies to. Get selectors right and CSS feels easy; get them wrong and styles land in the wrong places or refuse to apply. This lesson covers the selectors you will use 95% of the time.
The three basic selectors
<style>
p { color: #555; } /* type: every paragraph */
.highlight { background: gold; } /* class: every element with class="highlight" */
#intro { font-size: 1.3em; } /* id: the one element with id="intro" */
</style>
<p id="intro">This paragraph is the introduction.</p>
<p>A normal paragraph.</p>
<p class="highlight">A highlighted paragraph.</p>
<h2 class="highlight">Classes work on any element</h2>| Selector | Example | Matches |
|---|---|---|
| Type | p |
Every p element |
| Class | .highlight |
Every element whose class list includes highlight |
| ID | #intro |
The element with id="intro" |
| Universal | * |
Every element |
You can join a type and a class with no space to be more precise: p.highlight matches only paragraphs with that class. An element with class="card featured" is matched by both .card and .featured, and by .card.featured, which needs both classes at once.
Use classes for styling. They are reusable and easy to override. IDs are unique by definition, and their high specificity makes them hard to override later, which is why many teams avoid them in CSS altogether.
Combinators: elements in relation to others
Combinators select elements based on where they are in the HTML tree.
<style>
nav a { color: white; text-decoration: none; } /* any link inside nav */
nav { background: #234; padding: 8px; }
ul.steps > li { font-weight: bold; } /* only direct children */
h2 + p { font-style: italic; } /* the p right after an h2 */
</style>
<nav><a href="#">Home</a> · <a href="#">Courses</a></nav>
<a href="#">A link outside the nav stays blue</a>
<ul class="steps">
<li>Step one
<ul><li>A detail (not bold)</li></ul>
</li>
<li>Step two</li>
</ul>
<h2>Section title</h2>
<p>The first paragraph after the heading is italic.</p>
<p>The second one is not.</p>| Combinator | Example | Meaning |
|---|---|---|
| Space (descendant) | nav a |
a anywhere inside nav |
> (child) |
ul > li |
li directly inside ul, not deeper |
+ (next sibling) |
h2 + p |
the p immediately after an h2 |
~ (later siblings) |
h2 ~ p |
every p after an h2, sharing its parent |
Grouping with commas
When several selectors need the same styles, list them with commas instead of repeating the rule:
h1, h2, h3 {
font-family: Georgia, serif;
color: #222;
}Attribute selectors
Match elements by their attributes and values:
input[type="email"] { border-color: teal; } /* exact value */
a[href^="https"] { font-weight: bold; } /* value starts with */
a[href$=".pdf"]::after { content: " (PDF)"; } /* value ends with */
img:not([alt]) { outline: 4px solid red; } /* images missing alt text */The last line is a handy trick while building a page: it outlines every image that is missing its alt text, so you can fix them. The ^= and $= patterns may remind you of regular expressions; the Regex Tester is useful when you get to real pattern matching in JavaScript.
Pseudo-classes: states and positions
A pseudo-class starts with a colon and matches an element in a particular state or position.
<style>
a:hover { color: tomato; }
button:focus-visible { outline: 3px solid orange; }
li:first-child { color: green; }
li:nth-child(even) { background: #eef; }
input:invalid { border: 2px solid crimson; }
</style>
<p><a href="#">Hover over this link</a></p>
<button>Press Tab to focus me</button>
<ul>
<li>First (green)</li><li>Second</li><li>Third</li><li>Fourth</li>
</ul>
<input type="email" placeholder="Type something that is not an email">Common ones: :hover, :focus-visible (keyboard focus), :active (being pressed), :first-child, :last-child, :nth-child(n) (odd, even or a formula like 3n), :checked, :disabled, :invalid and :not(...).
Always style :focus-visible when you change how links or buttons look. Keyboard users rely on the focus outline to know where they are on the page.
Keep selectors short
A selector such as body div.main ul li a span works, but it is fragile (any change to the HTML breaks it) and very hard to override. Aim for one or two parts, usually a class: .menu-link. Short selectors are easier to read, faster to change and play nicely with the cascade, which is the next lesson.
Common mistakes
- Forgetting the dot or hash:
highlight { ... }looks for an element called<highlight>, which does not exist. - Adding a space by accident:
p .note(paragraph containing a.note) is very different fromp.note(a paragraph with classnote). - Expecting
>to reach deeper levels. It only matches direct children. - Removing focus outlines with
outline: noneand not replacing them.
Exercise
Paste this HTML into an editor and write CSS that: makes all links in the footer grey; makes only the direct li children of .menu bold; gives every second row of the table a light background; and turns any link to a PDF red.
<ul class="menu">
<li>Home</li>
<li>Shop
<ul><li>Shoes</li><li>Bags</li></ul>
</li>
</ul>
<table>
<tr><td>Mon</td><td>10-6</td></tr>
<tr><td>Tue</td><td>10-6</td></tr>
<tr><td>Wed</td><td>Closed</td></tr>
<tr><td>Thu</td><td>10-6</td></tr>
</table>
<footer>
<a href="/privacy">Privacy</a> <a href="/price-list.pdf">Price list</a>
</footer>Show one possible answer
footer a { color: grey; }
.menu > li { font-weight: bold; }
.menu li li { font-weight: normal; } /* nested items inherit bold, so reset them */
tr:nth-child(even) { background: #f2f2f2; }
a[href$=".pdf"] { color: red; }The nested list items look bold at first even though > only matched the outer items. That is inheritance: text inside a bold item is bold too. You will learn exactly how it works in the inheritance lesson.
Key points
- Type selectors (p) match every element of that type; class selectors (.card) match a class; id selectors (#main) match one element.
- A space means 'inside' (nav a); > means 'direct child' (ul > li); a comma groups selectors (h1, h2).
- Attribute selectors match on attributes, such as a[href^="https"].
- Pseudo-classes match states and positions: :hover, :focus-visible, :first-child, :nth-child(2n).
- Prefer classes for styling; keep selectors short so they stay easy to override.