Episode 1 — Fundamentals / 1.6 — CSS Core Fundamentals
1.6.a — CSS Syntax & Selectors
In one sentence: CSS rules are selector + declaration block pairs that tell the browser which elements to style and how — and the selector you choose determines reach, specificity, and maintainability.
Navigation: ← 1.6 Overview · 1.6.b — Specificity & Cascade →
1. Anatomy of a CSS rule
selector {
property: value; /* ← declaration */
property: value;
}
| Part | Role |
|---|---|
| Selector | Targets element(s) in the DOM |
| Declaration block | {} containing one or more declarations |
| Declaration | property: value; — what to change and to what |
2. Where CSS lives
| Method | Syntax | Trade-off |
|---|---|---|
| External stylesheet | <link rel="stylesheet" href="…"> | Best for production — cacheable, reusable |
<style> block | <style>…</style> in <head> | Useful for critical CSS; not cacheable separately |
| Inline style | style="color: red" | Highest specificity (except !important); hard to maintain |
Best practice: external stylesheets for almost everything; inline styles only when dynamically computed via JS.
3. Selector types
Simple selectors
| Selector | Targets | Example |
|---|---|---|
| Element (type) | All elements of that type | p { } — every <p> |
| Class | Elements with that class | .card { } — <div class="card"> |
| ID | The unique element with that ID | #hero { } — <section id="hero"> |
| Universal | Every element | * { } |
| Attribute | Elements with a matching attribute | [type="email"] { } |
Grouping selector
Comma-separate selectors to apply the same styles:
h1, h2, h3 {
font-family: inherit;
}
Combinator selectors
| Combinator | Name | Meaning | Example |
|---|---|---|---|
(space) | Descendant | Any nested child (any depth) | nav a { } |
> | Child | Direct children only | ul > li { } |
+ | Adjacent sibling | Immediately next sibling | h2 + p { } |
~ | General sibling | Any following sibling | h2 ~ p { } |
Pseudo-classes (state / position)
| Pseudo-class | Meaning |
|---|---|
:hover | Mouse is over the element |
:focus | Element has keyboard/programmatic focus |
:focus-visible | Focus visible only when keyboard-driven (preferred for focus styles) |
:active | Element is being activated (clicked/tapped) |
:first-child | First child of its parent |
:last-child | Last child of its parent |
:nth-child(n) | Positional selection (odd, even, 3n+1, etc.) |
:not(selector) | Negation — excludes matches |
:is(selector) | Matches any in the list (forgiving) |
:has(selector) | Parent selector — selects elements that contain a match |
Pseudo-elements (generated content / parts)
| Pseudo-element | Creates |
|---|---|
::before | Content before element's children |
::after | Content after element's children |
::placeholder | Placeholder text styling |
::selection | User text selection highlight |
4. The parent selector: :has()
:has() is the long-awaited parent selector — it selects an element based on what it contains:
/* Style a card differently when it contains an image */
.card:has(img) {
padding: 0;
}
/* Style a label when its sibling input is invalid */
label:has(+ input:invalid) {
color: red;
}
Power and caution: :has() inverts the selector direction — the browser must look down the tree to match. Avoid deeply nested :has() chains on large DOMs for performance reasons.
5. Attribute selectors (detail)
| Syntax | Meaning |
|---|---|
[attr] | Has the attribute (any value) |
[attr="val"] | Exact match |
[attr^="val"] | Starts with |
[attr$="val"] | Ends with |
[attr*="val"] | Contains substring |
[attr~="val"] | Contains word (space-separated list) |
Useful for targeting form states ([disabled], [required]) or link types ([href^="https://"]).
6. Key takeaways
- Classes are the workhorse — reusable, low specificity, composable.
- IDs are unique and high-specificity — prefer classes for styling; save IDs for JS hooks and
aria-labelledby. - Combinators express relationships — descendant, child, sibling.
:has()is the parent selector — selects based on contents.- Pseudo-classes target state; pseudo-elements create virtual content.
Explain-It Challenge
Explain without notes:
- The difference between descendant (
) and child (>) combinators with an example. - When you would use
:has()instead of adding a class with JavaScript. - Why class selectors are generally preferred over ID selectors for styling.
Navigation: ← 1.6 Overview · 1.6.b — Specificity & Cascade →