Code Safari

Chapter 132·Intermediate·9 min read

CSS and the Render Tree

How the browser turns CSS text into the CSSOM, resolves the cascade and specificity into one computed style per element, and merges it with the DOM to build the render tree — the visible-only tree that layout and paint actually work on.

July 24, 2026

By the end of the DOM chapter, the browser had a complete tree of what is on the page — but nothing about how any of it should look. A <h1> in the DOM is just a heading node; it has no colour, no size, no position. Turning that structural tree into something worth drawing needs a second ingredient: your CSS. This chapter follows CSS through the same journey HTML took — text becomes a tree — and then watches the browser fuse the two into the structure every remaining stage actually operates on: the render tree.

CSS becomes the CSSOM

Your stylesheets arrive as text, exactly like your HTML, and the browser parses them into a tree of their own: the CSSOM, the CSS Object Model. Where the DOM models the page's structure, the CSSOM models its style rules — every selector, and the declarations it applies.

The CSSOM is a tree for the same reason the DOM is: style inherits. Set a color on body and, unless something overrides it, every descendant inherits that colour. A tree structure captures that "flows down to children" behaviour naturally. So the browser builds a hierarchy of style information that mirrors how properties cascade down through the page.

The cascade: many rules, one value

Here's the problem CSS has to solve on every single element: more than one rule usually applies to it. A paragraph might be touched by a browser default, a rule for p, a rule for .intro, and an inline style attribute — each possibly setting color. The element can only have one colour. So which wins?

That decision is the cascade — the "C" in CSS — and it resolves conflicts using a clear priority order:

Origin & importance
Specificity
Source order
Winning value
How the cascade picks a winner for each property

Walking that order: first origin and importance — an author !important beats a normal author rule, which beats the browser's defaults. Then specificity — a more specific selector wins: an ID (#hero) beats a class (.title) beats a bare tag (h1). Finally, when specificity ties, source order — the rule declared last wins. Run this contest for every property of every element and you get exactly one computed value per property: the final, resolved answer.

SelectorSpecificity (rough)Beats
h1low
.titlemediumh1
#hero .titlehigh.title
inline style=""highestalmost everything
any !importanttrumps normal rulesthe lot

The end product of all this, for a given element, is its computed style: a complete set of every CSS property resolved to a single value — the real colour, the real font size in pixels, the real display type. That computed style is what the next stages need.

Merging into the render tree

Now the two trees come together. The browser walks the DOM and, for each node, looks up its computed style from the CSSOM, producing a combined structure: the render tree. It's a tree of everything that will actually be drawn, each entry carrying both its content (from the DOM) and its final styles (from the CSSOM).

The word to dwell on is visible. The render tree is not a copy of the DOM — it deliberately leaves things out:

  • display: none — the node is excluded from the render tree entirely. It occupies no space, participates in no layout, and is never painted. As far as rendering is concerned, it isn't there.
  • <head>, <script>, <meta> — non-visual nodes never make it into the render tree; there's nothing to draw.
  • visibility: hidden — a subtle contrast: the node stays in the render tree and still takes up its space in layout; it's simply painted invisibly.

So the render tree is the pipeline's pivot point: the DOM said what, the CSSOM said how, and the render tree is the merged, visible-only, fully-styled answer that the geometry stages consume.

Why CSS blocks the first paint

The overview flagged CSS as render-blocking, and now we can say precisely why. The browser cannot build a correct render tree — and therefore cannot paint anything accurately — until it has processed the CSS that affects the page. If it painted first and styled later, you'd get a jarring flash of unstyled content: a split-second of raw, black-on-white, default-styled HTML before your design snapped into place. To spare users that, the browser holds back the first paint until it has the CSSOM it needs.

This has a direct practical consequence: CSS is on the critical path to seeing anything. A large or slow-loading stylesheet delays first paint for the whole page. It's why techniques like inlining the small amount of "above the fold" CSS, and deferring the rest, exist — they shrink the render-blocking cost so the browser can paint sooner. We'll connect this to real performance metrics in the final chapter.

Where this leaves us

The render tree is the quiet star of the whole pipeline. The DOM gave the browser structure; the CSSOM gave it style, resolved through the cascade into one computed value per property; and the render tree fused them into a visible-only model, ready to be drawn. Every stage from here on works on this tree — never your HTML file, never your CSS file.

But "ready to be drawn" still isn't "drawn." The render tree knows a heading is 32px and bold — it doesn't yet know where on the screen that heading sits, or how wide it is, or what it pushes out of the way. Resolving all of that geometry, and then filling in the actual pixels, is the browser's most expensive work. Next: layout, paint, and composite.

CSS and the Render Tree | Code Safari