How the browser turns HTML and CSS text into pixels: DOM, CSSOM, render tree, layout, paint, composite.
A page arrives from the server as text, not pixels. A browser can't draw HTML and CSS directly — it runs them through a fixed assembly line called the critical rendering path: parse the HTML into a DOM, parse the CSS into a CSSOM, merge those into a render tree of what's actually visible, compute every box's size and position (layout), fill in the pixels (paint), and stack the layers onto the screen (composite). Understanding this line is what lets you reason about *why* a page appears when it does.
The browser reads the HTML top to bottom and builds the Document Object Model: a tree of nodes, one per tag, nested exactly as the markup nests. This is the structure the browser and JavaScript both work with. Parsing is *incremental* — the tree grows as bytes stream in, so the browser can start work before the whole file has arrived.
<html>
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Hello</h1>
<p>Welcome to the web.</p>
</body>
</html>That markup becomes a tree: html at the root, with head and body children; body holds an h1 and a p, each wrapping a text node. That tree is the DOM.
In parallel, every stylesheet is parsed into the CSS Object Model — a matching tree of style rules that says how each element should look. Styles *cascade*: a rule on body also affects the h1 inside it unless something more specific overrides it, so the browser must read the whole stylesheet before it knows any element's final computed style.
The DOM and CSSOM are merged into the render tree: only the nodes that will actually be drawn, each paired with its computed style. Invisible nodes are left out — head never renders, and anything with display: none is skipped entirely (note that visibility: hidden still takes up space, so it *stays* in the tree).
em units resolve against the viewport.transform or opacity can skip both and run purely on the compositor, which is why those are the smooth properties to animate.A plain <script> tag is parser-blocking: the browser stops building the DOM to download and run the script, because the script might call document.write or read the DOM. Worse, a script that reads styles must wait for the CSSOM, so a stylesheet above a script can stall parsing too. Adding defer (run after parsing, in order) or async (run whenever it arrives) lets the parser keep going — the standard fix for render-blocking scripts.