Section · 01
Anatomy of a CSS rule
a:hover { /* selector — which elements this applies to */
color: red; /* declaration: property + value */
}One file of CSS is a long list of these rules. The browser walks every element, finds every rule whose selector matches, combines them, and paints. The whole game is writing selectors that match what you want without matching what you don’t.
Section · 02
The five basic selector types
/* Element — match all <p> */
p { color: gray; }
/* Class — match elements with class="btn" */
.btn { background: red; }
/* ID — match the single element with id="hero" */
#hero { padding: 40px; }
/* Attribute — match elements with a given attribute (value optional) */
input[type="email"] { border-color: green; }
a[target="_blank"] { color: orange; }
/* Universal — match every element */
* { box-sizing: border-box; }The right tool depends on scope. Element selectors are broad — they hit every <p>on the page. Classes are reusable — multiple elements can share one. IDs are unique — only one element should have a given ID per page. In modern codebases you’ll mostly use classes.
Section · 03
Combinators — relationships between elements
/* Descendant (space) — any nav inside a header */
header nav { padding: 8px; }
/* Child (>) — only direct children */
ul > li { color: red; }
/* Adjacent sibling (+) — the next sibling that immediately follows */
h2 + p { font-weight: bold; }
/* General sibling (~) — any following sibling */
h2 ~ p { color: gray; }You’ll use descendant (just a space) more than the others combined. The rest exist for specific cases — like styling the paragraph immediately after a heading without giving it a class.
Section · 04
Pseudo-classes — state and structure
Pseudo-classes start with a colon. They match elements in a particular state or position.
a:hover — when the mouse is over the link
a:focus — when keyboard-focused
button:disabled — when disabled
input:checked — when a checkbox/radio is checked
li:first-child — the first <li> in its parent
li:last-child — the last
li:nth-child(2n) — every even <li> (1n+1 = every, 2n = even, etc.)
:not(.special) — anything that doesn't have the .special classThese are how you style hover effects, focus states, even-row striping, and form validation feedback — all without writing JavaScript.
Section · 05
Specificity — why your style isn't applying
When two rules target the same element, the more specific one wins. Specificity is calculated as three numbers (IDs, classes, elements):
Selector IDs Classes Elements
* 0 0 0
p 0 0 1
p.btn 0 1 1
.btn 0 1 0
.btn.large 0 2 0
#hero 1 0 0
header nav a.active 0 1 3Compare from left to right. Any rule with an ID beats every rule without one. Among class-based rules, more classes win. Inline styles (style="..." attribute) beat stylesheet rules. !important beats all of them — and is a code smell. If you find yourself reaching for it, your selectors are wrong.
Section · 06
The cascade — when specificity ties
When two rules have equal specificity, the one that comes laterin the CSS wins. That’s the C in CSS.
.btn { background: red; }
.btn { background: blue; } /* wins — same specificity, later */Source order matters at the file level too. If you load vendor.css before app.css, your app styles can override the vendor styles. Flip the order and you can’t.
Inheritance
Some properties cascade through children automatically. color, font-family, and line-height inherit. border, padding, margin do not. That’s why setting font on the body “just works” for the whole page.
Section · 07
Debugging selectors
Three steps that fix 95% of “why isn’t my CSS working” problems:
1. Open DevTools (right-click → Inspect)
2. Click the element. Look at the Styles panel.
3. The browser shows you which rules matched, in order of specificity,
with crossed-out properties where something later overrode them.If you see your rule crossed out, something else won — either higher specificity or later in the file. If you don’t see your rule at all, your selector doesn’t match. Fix the selector, then re-check.
Next lesson: the box model and how layout actually works once you can target the right elements.