How CSS Works
Before you write a single line of CSS it's worth understanding what actually happens when the browser reads your stylesheet. This isn't dry theory — knowing this model helps you debug mysterious problems, understand why some styles seem to be ignored, and write CSS that does exactly what you intend.
The anatomy of a CSS rule
Every CSS rule has two parts: a selector that identifies which HTML elements to target, and a declaration block that contains one or more property–value pairs.
/* This is a CSS rule */
h1 { /* ← selector: targets every <h1> element */
color: navy; /* ← declaration: property "color", value "navy" */
font-size: 2rem;/* ← another declaration */
}
/* Anatomy labels:
selector { property: value; property: value; }
└─ declaration block ─────────────┘ */How the browser parses CSS
When you load a page, the browser does the following in roughly this order:
1. Parse the HTML → build the DOM (Document Object Model) A tree of every element on the page 2. Load linked resources Images, fonts, and — crucially — CSS files 3. Parse the CSS → build the CSSOM (CSS Object Model) Another tree, but for style rules 4. Combine DOM + CSSOM → the Render Tree Only elements that will actually be visible are included (display:none elements are excluded) 5. Layout (Reflow) Calculate the exact size and position of each element 6. Paint Fill in the pixels — colours, borders, shadows, text 7. Composite Layer the painted tiles together (for GPU acceleration) Then: display the finished frame to the screen
Selectors and the matching process
The browser reads selectors right to left. For a selector like nav ul li a, it first finds all <a> elements on the page, then checks if each is inside an <li>, then checks if that <li> is inside a <ul>, then checks if that <ul> is inside a <nav>. This direction matters for performance: a very broad rightmost element like a matches thousands of elements immediately, then most are filtered out. Keeping the rightmost part specific is why unnecessarily deep selectors are slow.
The cascade in one sentence
Once the browser has found all the rules that match an element, it needs to decide which ones win. The cascade is the algorithm that resolves these conflicts. In brief:
Origin — browser default styles lose to author styles (your CSS), which lose to user-defined styles.
Specificity — a more specific selector (e.g.
#header p) beats a less specific one (e.g.p), regardless of order.Order — when specificity is equal, whichever rule appears later in the stylesheet wins.
Importance —
!importantoverrides everything in the current origin (use sparingly).
This guide has a full section on the cascade — it's one of the most important concepts in CSS.
Inheritance
Some CSS properties inherit — they automatically pass their value down from a parent element to its children. Others don't. Knowing which is which saves a lot of repetition.
Inherits by default | Does NOT inherit by default |
|---|---|
|
|
|
|
|
|
|
|
|
|
body {
font-family: system-ui, sans-serif;
color: #333;
}
/* Every element on the page — headings, paragraphs, links — inherits
these values without you having to re-declare them. This is how CSS
lets you set a "base" style once. */How values are computed
When you write font-size: 1.5em, the browser doesn't store "1.5em" — it converts every value to an absolute computed value at render time. That 1.5em becomes something like 24px based on the parent's font size. This resolved value is what you see in DevTools under "Computed" styles. Understanding the difference between specified values (what you wrote), computed values (what the cascade resolves to), and used values (what the browser actually lays out with) helps enormously when debugging why a style doesn't look like you expect.