HTML, CSS & the Browser

CSS Selector

Definition

Selectors come in a small number of shapes that combine freely. A bare name matches a tag: p matches every paragraph. A leading dot matches a class, .card; a leading hash matches an id, #header. Square brackets match an attribute, [type="email"]. A colon introduces a pseudo-class for a state or position — :hover, :focus, :first-child, :nth-child(2n) — and two colons a pseudo-element such as ::before. Putting selectors side by side narrows the match: .card.featured is an element with both classes, while .card .title is a title anywhere inside a card. A comma means "or", so h1, h2, h3 applies one rule to all three.

Why It Matters

Reading a selector correctly is how you tell whether a rule is even meant for the element in front of you. The difference between .card.featured and .card .featured is one space, and it changes the meaning from one element with two classes to a descendant of a card — a rule that silently matches nothing looks exactly like a rule that is being overridden. Selectors also decide what a change costs later: a rule written as nav ul li a breaks the day someone removes the ul, while a rule on a class survives the markup being rearranged. In a stylesheet you did not write, the selector tells you how the author expected the HTML to be shaped.

How It Works

The browser matches selectors from right to left. For .sidebar a it first collects every link in the document, then keeps the ones that have a sidebar ancestor, which is why very long descendant chains are slower than a single class. Each matching rule contributes its declarations to the element, and conflicts are settled by specificity, then by source order. The combinators are worth knowing apart: a space means any descendant, > means a direct child only, + the next sibling, and ~ any later sibling. To check a selector without guessing, open developer tools, run document.querySelectorAll with the same string in the console, and count what comes back — zero results means the selector, not the styling, is the problem.

Real-World Example

An event organiser publishes a schedule page at devfest-oct.99helpers.site where the speaker names refuse to go bold. The rule reads .session .speaker-name { font-weight: 700; } but the markup has class="speaker_name" with an underscore. Running the selector in the console returns an empty list, which settles it in seconds; matching the two spellings makes all fourteen names bold. Nothing about the declaration was ever wrong.

Common Mistakes

  • Adding or dropping a space between two class selectors — .card.featured and .card .featured are different rules, and one of them usually matches nothing
  • Reaching for an id selector to make a rule stick — it wins today, but its specificity is high enough that every later rule has to escalate to beat it
  • Writing a long descendant chain such as body div.main ul li span — it is brittle, hard to override and breaks the moment the markup is restructured

Related Terms

Put a file online in seconds

Drop in a document, an image, a page or a whole static website and share the link — free, with no build step and no server to set up.

Host a file free →