CSSOM: CSS Object Model
The CSSOM is a tree of CSS rules built by the browser.
CSSOM: CSS Object Model
While HTML gives a page its content structure through the DOM, CSS gives it visual style through the CSSOM. These two trees are built separately by the browser but must be combined before anything can be painted on screen. Understanding the CSSOM helps you write more performant stylesheets and debug rendering issues more effectively.
What Is the CSSOM
The CSS Object Model (CSSOM) is a tree representation of all the CSS style rules that apply to a web page. When the browser encounters a stylesheet, whether in a <link> tag, a <style> tag, or as an inline style attribute, it parses the CSS and builds the CSSOM. Like the DOM, the CSSOM is a live structure that JavaScript can read and modify at runtime.
Each node in the CSSOM represents a CSS rule or a set of rules, along with the computed values that result from applying the cascade, inheritance, and specificity resolution. The CSSOM does not mirror the DOM structure directly. Instead it stores style rules organised by selector, and the browser uses it to determine what styles apply to each DOM node when building the render tree.
How the Browser Builds the CSSOM
CSSOM construction is a blocking process. The browser cannot proceed with rendering until every stylesheet has been fully downloaded and parsed. This is intentional. Rendering a page before all styles are known would cause a flash of unstyled content, where elements briefly appear without their intended styles before jumping into their final appearance.
- The browser encounters a stylesheet reference, either an external
<link>tag, an inline<style>block, or a CSS@importrule - If the stylesheet is external, the browser downloads it. Rendering is blocked until the download completes.
- The browser parses all CSS rules in the stylesheet from top to bottom
- It resolves the cascade by applying specificity rules, the order of declarations, and the origin of each rule to determine which styles win when conflicts arise
- It resolves inheritance so that properties like
font-sizeandcolorflow down from parent elements to children unless overridden - It converts all values into computed values, resolving relative units like
emandreminto absolute pixel values - The final CSSOM tree is complete and ready to be combined with the DOM
DOM + CSSOM = Render Tree
The browser cannot paint anything until both the DOM and CSSOM are fully built. Once both are ready, the browser combines them into the Render Tree. The render tree is not simply a merge of the two structures. It contains only the elements that will actually be displayed on screen, paired with their final computed styles.
Elements that are hidden using display: none are excluded from the render tree entirely. Elements with visibility: hidden remain in the render tree because they still occupy space in the layout, but they are not visually painted. The <head> element and its children, including <script> and <meta> tags, are also excluded because they produce no visual output.
| Structure | Built From | Contains |
|---|---|---|
| DOM | HTML parsing | All nodes including head, scripts, hidden elements, and text nodes |
| CSSOM | CSS parsing | All style rules with cascaded and computed values resolved |
| Render Tree | DOM and CSSOM combined | Only visible elements with their final computed styles attached |
CSS Is Render-Blocking
CSS stylesheets are render-blocking by default. The browser will not paint anything on screen until it has downloaded and parsed all CSS referenced in the document. This is a deliberate design choice to prevent users from seeing a flash of unstyled content, but it means that slow or large stylesheets directly delay the time to first paint.
The render-blocking behaviour also affects JavaScript. The browser will not execute a script that appears after a stylesheet until that stylesheet has been fully parsed. This is because the script might query computed styles through window.getComputedStyle, and the browser needs the CSSOM to be complete before it can answer those queries accurately.
To reduce the impact of render-blocking CSS, consider the following approaches:
- Place all stylesheets in the
<head>so the browser discovers and begins downloading them as early as possible during HTML parsing - Minimise the total number and size of stylesheets by removing unused rules and combining files where practical
- Use media query attributes on link tags to mark non-critical stylesheets as non-render-blocking. For example,
<link rel="stylesheet" href="print.css" media="print">will not block rendering on screen - Inline the critical CSS needed to render above-the-fold content directly in a
<style>tag in the<head>, and load the full stylesheet separately - Use
<link rel="preload" as="style">to instruct the browser to fetch important stylesheets at high priority before the parser would normally discover them
Cascade, Specificity, and Inheritance
The CSSOM is not a simple collection of CSS rules. Before storing styles, the browser must resolve three layered mechanisms that determine which rules actually apply to each element.
- Cascade: When multiple rules target the same element and property, the cascade determines which one wins. Rules from author stylesheets override user agent defaults. More recently declared rules override earlier ones at equal specificity.
- Specificity: A measure of how precisely a selector targets an element. Inline styles have the highest specificity, followed by ID selectors, then class and attribute selectors, then element selectors. A more specific rule overrides a less specific one regardless of order.
- Inheritance: Some CSS properties such as
font-family,color, andline-heightare inherited by child elements from their parents. Others such asmargin,padding, andborderare not inherited. The CSSOM resolves inherited values so that every element has an explicit computed value for every property.
Accessing the CSSOM in JavaScript
JavaScript can both read from and write to the CSSOM at runtime. Reading computed styles is a common operation for animations, measurements, and dynamic layout adjustments. Writing to the CSSOM through style changes can trigger reflow and repaint, so it should be done carefully to avoid performance issues.
// Get the final computed styles for an element
const styles = window.getComputedStyle(element);
console.log(styles.color);
console.log(styles.fontSize);
// Read all loaded stylesheets and their rules
for (const sheet of document.styleSheets) {
console.log(sheet.href, sheet.cssRules);
}
// Modify a CSS custom property on the root element
document.documentElement.style.setProperty('--primary-color', '#0ea5e9');
// Directly set an inline style (triggers repaint or reflow)
element.style.backgroundColor = '#f0f0f0';
Reading layout properties such as offsetHeight, getBoundingClientRect, or scrollTop after making style changes forces the browser to flush pending style recalculations and perform a synchronous reflow. This is known as a forced synchronous layout or layout thrashing when it happens repeatedly in a loop.
Frequently Asked Questions
- Why does modifying CSS through JavaScript sometimes feel slow?
Any CSS change that affects the geometry of an element, such as altering its width, height, margin, or position, triggers a full reflow. The browser must recalculate the layout of the affected element and all elements that depend on it, then repaint. Changing only visual properties that do not affect geometry, such as color or background, triggers a cheaper repaint without reflow. The cheapest operations are those handled entirely by the compositor, such as CSStransformandopacitychanges. - What is the difference between computed style and specified style?
Specified style is what you write in your CSS file, for examplefont-size: 1.5em. Computed style is the final resolved value after the cascade, inheritance, and all unit conversions have been applied, for examplefont-size: 24px. Thewindow.getComputedStyle()method returns computed values, which are always in absolute units and reflect the actual values the browser is using to render the element. - Does JavaScript need to wait for the CSSOM before executing?
Yes, when a script tag appears after a stylesheet in the document. The browser blocks JavaScript execution until the CSSOM is fully built because scripts may query computed styles, and the browser needs complete style information to answer those queries correctly. This means that slow CSS downloads do not just delay the first paint directly. They also delay any JavaScript that follows, which can further delay rendering if that JavaScript modifies the DOM. - What happens if a CSS file fails to load?
If an external stylesheet fails to load due to a network error or a wrong URL, the browser will still proceed with rendering once the request times out or returns an error. The CSSOM will be built without that stylesheet's rules, and any elements that relied on those styles will fall back to browser defaults or other matching rules. The page will render but may look broken. Unlike a failed script, a failed stylesheet does not throw a JavaScript error by default. - What is a CSS custom property and how does it interact with the CSSOM?
CSS custom properties, also called CSS variables, are properties defined with a double-dash prefix such as--primary-color: #0ea5e9and referenced withvar(--primary-color). They are part of the CSSOM like any other property. You can read and update them through JavaScript usinggetComputedStyleandsetProperty. Changing a custom property triggers a style recalculation for every element that references it, which makes them a powerful but potentially expensive tool for dynamic theming.
Conclusion
The CSSOM is the style counterpart to the DOM. Together they form the Render Tree, which drives everything the user sees on screen. CSS is render-blocking by design, which makes stylesheet size, loading strategy, and specificity management all directly relevant to first-paint performance. Understanding how the browser builds the CSSOM, how cascade and inheritance are resolved, and how JavaScript interacts with computed styles gives you a clearer picture of why pages render the way they do and where to focus when optimising. Continue with critical rendering path, browser rendering, and DOM to complete your understanding of how browsers turn code into pixels.
