Selectors and the Cascade
Selectors target elements; the cascade is the algorithm the browser uses to decide which rule wins when multiple rules apply.
- Write type, class, ID, and descendant selectors
- Explain what "the cascade" means — that multiple rules can apply to the same element and the browser resolves conflicts
- Understand the three factors in the cascade (origin, specificity, source order)
- Recognize when to use class selectors vs ID selectors
A CSS rule is useless without a good selector. The selector is what connects a rule to the element it should style. Get selectors wrong and your styles hit the wrong targets — or nothing at all.
Type selectors
The simplest selector is a bare element name. It targets every element of that type on the page:
p {
color: #333;
}
h1 {
font-size: 2rem;
}
a {
text-decoration: underline;
}Type selectors are broad. p { color: red } turns every paragraph red —
useful for defaults, but not fine-grained enough for most real styling.
Class selectors
A class selector starts with a dot and targets every element that has that
class in its class attribute:
<p class="intro">This is the lead paragraph.</p>
<p>This is a regular paragraph.</p>.intro {
font-size: 1.2rem;
font-weight: 600;
}Only the first paragraph is affected. Classes are the workhorse of CSS: they
are reusable (add the same class to any element), composable (one element can
have multiple classes: class="card featured"), and they convey intent through
their names.
ID selectors
An ID selector starts with # and targets the single element with that id
attribute:
<header id="site-header">…</header>#site-header {
background: #0d1117;
}IDs are unique per page — no two elements should share the same id. The
problem with ID selectors in CSS is their high specificity: they are very hard
to override later. The modern convention is to use IDs as hooks for
JavaScript (document.getElementById) and classes for all styling. Reach
for #id in a stylesheet only when you have a compelling reason.
Descendant and child combinators
Combinators describe a relationship between two selectors.
Descendant (a space between selectors) — matches the right element anywhere inside the left element, at any depth:
article p {
/* every <p> inside an <article>, no matter how deeply nested */
line-height: 1.7;
}Child (>) — matches only direct children, not deeper descendants:
ul > li {
/* only <li> elements that are immediate children of <ul> */
list-style: disc;
}Adjacent sibling (+) — matches an element immediately following another:
h2 + p {
/* the first <p> that comes directly after an <h2> */
margin-top: 0;
}The descendant combinator is the most useful but also the most dangerous —
a rule like div p will target every paragraph inside every div on the
page. Prefer class selectors and save combinators for genuinely structural
relationships.
The cascade
The word "cascade" is the C in CSS. It describes how the browser handles conflicts: what happens when two rules both target the same element and set the same property to different values?
p { color: blue; }
p { color: red; }Both rules match every <p>. The browser needs a way to pick one. The cascade
is the algorithm that does this, and it has three steps — applied in order:
1. Origin and importance
Rules come from different origins: the browser's built-in default styles,
any user-defined styles (rare in practice), and your author stylesheet. Author
styles override user-agent defaults. The !important flag can reverse this
order, but treat it as a last resort — it is covered in the next lesson.
2. Specificity
If two rules come from the same origin, the more specific selector wins. A
class selector (.intro) beats a type selector (p); an ID selector
(#header) beats both. The full details of specificity scoring are covered
in the next lesson — for now, remember: more specific wins.
3. Source order
If two rules tie on origin and specificity, the later rule wins. That is why you write resets and defaults at the top of a stylesheet and overrides further down.
p { color: blue; }
p { color: red; } /* this wins — later in the file */Putting it together
Here is a stylesheet fragment that demonstrates all three selector types and the cascade at work:
/* Type selector: all paragraphs are dark grey */
p {
color: #333;
line-height: 1.6;
}
/* Class selector: lead paragraphs are slightly larger */
.lead {
font-size: 1.15rem;
}
/* Descendant: paragraphs inside articles get extra breathing room */
article p {
margin-bottom: 1.2rem;
}A <p class="lead"> inside an <article> matches all three rules. The
color and line-height come from the type selector. The font-size comes
from the class selector (which beats the type selector when both could apply,
because classes are more specific). The margin-bottom comes from the
descendant combinator rule.
When debugging unexpected styles, open your browser's DevTools and inspect the element. The Styles panel shows every rule that matches, with struck-out declarations indicating which ones were overridden and why. This is the fastest way to understand the cascade in practice.
Where to go next
Next: Specificity — a precise look at how the browser calculates a specificity score for any selector, and what to do when the score system works against you.