Topic 02

Selectors

CSSAccessibility

A CSS rule is only as useful as its selector, the part before the braces that says which elements the rule applies to. Pick too broadly and a style lands where you did not want it; pick too narrowly and you write the same rule five times. A handful of selector types cover almost everything a site needs, and choosing among them well keeps a stylesheet short and readable.

Selectors work like the address on a letter. "To everyone in the building" reaches every resident. "To everyone in flat group B" reaches a named group. "To the person in flat 4" reaches exactly one. CSS has a selector for each of those, and a few ways to be more precise, such as "everyone in flat group B who lives on the top floor".

Type, Class and Id

The three basic selectors match elements by their name, by a class, or by an id. Here is one rule of each kind, and the HTML they would match.

Fragment
p {
  color: darkslategray;
}

.card {
  background-color: lightyellow;
}

#top {
  text-transform: uppercase;
}
Fragment
<h1 id="top">Recipes</h1>
<div class="card">
  <p>Tomato soup</p>
</div>
<div class="card">
  <p>Bread</p>
</div>

A type selector is an element's name on its own, and it matches every element of that type: here, both paragraphs turn dark slate grey. A class selector is a dot followed by a class name, and it matches every element whose class attribute contains that name: both cards get a pale yellow background. An id selector is a hash sign followed by an id, and it matches the one element with that id: the heading "Recipes" appears in capital letters.

Classes are the everyday tool. You choose the name, any number of elements can share it, and one element can carry several classes separated by spaces. A good class name says what a thing is, such as card or site-nav, never what it looks like, because the look will change and the name should not have to.

Ids are different. An id must be unique on the page, which makes it right for the jobs that need exactly one target: the in-page links from Chapter 2, and later, finding an element from JavaScript. For styling, ids cause a problem that topic 3 explains, so this book styles with classes.

Combining Selectors

Selectors can be joined to be more precise. A space between two selectors means "inside": a descendant selector such as nav a matches every link that sits anywhere inside a nav element, however deeply nested, and no other link.

Fragment
nav a {
  text-decoration: none;
}

ul > li {
  margin-bottom: 0.5rem;
}

h1, h2 {
  font-family: Georgia, serif;
}

The first rule removes the underline from the links in the menu, while links in the text keep theirs. The second uses a greater-than sign, the child selector: it matches a list item only when it sits directly inside a ul, not one nested deeper. It adds a little space under each item; topic 4 explains the unit. The third is a list: a comma between selectors means "and also", so both kinds of heading get the Georgia font. Try the first rule on your site's menu and watch only the three menu links lose their underline.

Selecting by Attribute

Square brackets match elements by their attributes. The most useful form for a beginner picks links by where they point. The selector a[href^="https"] reads "a link whose address begins with https", the caret before the equals sign meaning "begins with". Since your own pages use relative paths, that selector picks out only the links that leave your site, so a rule could mark them in italics to warn visitors they are heading elsewhere.

States: Hover, Focus and Position

A colon after a selector adds a condition about the element's state or position, called a pseudo-class. Three are worth knowing now.

Fragment
a:hover {
  color: darkred;
}

a:focus-visible {
  outline: 3px solid firebrick;
  outline-offset: 2px;
}

li:first-child {
  font-weight: bold;
}

The first rule turns a link dark red while the mouse pointer is over it, and back when it leaves. The third makes the first item of every list bold, because :first-child matches an element that is the first child of its parent.

The second rule needs more explanation, because it is the first accessibility habit in CSS. Many people use a keyboard instead of a mouse, and they move through a page's links and buttons with the Tab key. The element that will respond to the keyboard has focus, and browsers mark it with a visible ring so the person can see where they are. The :focus-visible pseudo-class matches an element that has focus when the browser judges the ring should show, which for links and buttons means when someone is using the keyboard rather than clicking. This rule replaces the browser's ring with a thicker red outline, drawn two pixels outside the link. Press Tab on a page with this rule and each link in turn gets the red outline. In Safari on a Mac, Tab skips links until you turn on the option to press Tab to highlight each item, in Safari's Advanced settings.

Restyle the focus ring as much as you like. Never remove it without putting something as visible in its place, or keyboard users are left navigating a page where they cannot see their own position.

Selecting by What an Element Contains

For most of CSS's history, a selector could look down into an element but never up. The :has() pseudo-class changes that: it matches an element by what it contains. The selector .card:has(img) matches only cards that have an image somewhere inside them.

Fragment
.card:has(img) {
  border: 2px solid gray;
}

With the two cards from earlier, neither has an image, so neither changes. Add an img element inside the first card and that card alone gets a grey line two pixels wide around its edge, while the second stays as it was. This pseudo-class is the newest feature in this chapter, widely available only since 2026, and it replaces tricks that once needed JavaScript.

Five selectors and what they match
SelectorMatchesTypical use
pEvery paragraphBase styles for one kind of element
.cardEvery element with the class cardThe everyday tool for styling components
#topThe one element with the id topIn-page links and scripts, not styling
nav aLinks anywhere inside a navStyling links by the region they sit in
a:hoverA link while the pointer is over itFeedback that something can be clicked
Common Confusions
  • ".card and card are the same selector." Without the dot, the selector looks for an element named card, which does not exist, so the rule matches nothing and nothing warns you.
  • "Removing the focus outline looks cleaner." It leaves keyboard users unable to see where they are on the page. Restyle the ring with :focus-visible; never delete it.
  • "Ids are stronger, so they are better." That strength is exactly the problem. An id rule overrides class rules in ways that are hard to undo, as topic 3 shows.
Why It Matters
  • Class names that say what a thing is keep a stylesheet readable a year later, by you or by anyone else.
  • A visible focus style costs one rule and makes the whole site usable for everyone who navigates by keyboard.

Knowledge Check

A page has a nav holding three links, and a paragraph holding one more. Which links does nav a match?

  • All four links, because each one is an a element
  • Only the three links that sit inside the nav
  • The nav element itself, and not the links
  • Only the link inside the paragraph, outside the nav

Why should you never remove the focus outline without a replacement?

  • Search engines rank pages lower when it is missing
  • The browser shows an error if the outline is removed
  • Mouse users would no longer know what to click next
  • Keyboard users would no longer see where they are

You want the same look on many elements across the site. Which selector type fits best?

  • A class, which any number of elements can share
  • An id, since it gives each element a unique name
  • A type selector for every element of that kind
  • An attribute selector that matches each address

What does .card:has(img) match?

  • Every image that sits anywhere inside a card
  • Every card, whether or not it has an image
  • Only the cards that contain an image
  • Cards that should have an image added

You got correct