Show me what the browser does between HTML and pixels.
Every performance question in the frontend is really a question about which of these stages you triggered and how often.
flowchart LR
H[HTML] --> D[DOM tree]
C[CSS] --> S[CSSOM]
D --> R[Render tree]
S --> R
R --> L[Layout<br/>geometry of every box]
L --> P[Paint<br/>pixels for each layer]
P --> Comp[Composite<br/>layers to the screen]The critical path is that the render tree needs both the DOM and the CSSOM, so CSS is render-blocking by definition — the browser will not paint an unstyled page and then restyle it. That is why stylesheets belong in the head and why a large stylesheet delays first paint even when most of it is unused.
Scripts are worse, because a classic <script> blocks parsing entirely: the
parser stops, fetches, executes, then resumes, and the executing script may
itself read layout, which forces the CSSOM to be complete first. defer keeps
the download parallel and runs the script after parsing in document order;
async runs it the moment it arrives, in no guaranteed order, which suits an
independent analytics tag and breaks anything with a dependency.
The three stages at the end are a hierarchy of cost, and the whole of frontend
animation performance follows from it. Changing geometry runs layout, paint and
composite. Changing a colour skips layout. Changing transform or opacity
skips both layout and paint, so it happens on the compositor and can hit sixty
frames per second on a page too heavy to lay out even once.