What Is CSS :root and Why Use It for Variables?

This article provides a straightforward guide to the CSS :root pseudo-class and explains why it has become the standard location for declaring global CSS variables. You will learn what the :root selector represents in the Document Object Model (DOM), how it compares to the <html> selector in terms of specificity, and the primary technical and structural benefits of using it to manage global design tokens across your stylesheets.


What Is the :root Pseudo-Class?

The :root pseudo-class in CSS matches the root element of the document tree. In standard HTML documents, the root element is always the <html> tag. However, :root acts as a generic selector capable of targeting the root of any markup format, including SVG and XML files.

While :root and html both target the exact same node in an HTML document, they differ in CSS specificity:

  • html is an element (type) selector with a specificity score of (0, 0, 1).
  • :root is a pseudo-class selector with a specificity score of (0, 1, 0).

Because pseudo-classes carry higher specificity than basic tag selectors, rules declared on :root take precedence over rules declared on html.


Why :root Is Preferred for Global CSS Variables

CSS custom properties (variables) follow the standard CSS cascade and inheritance model. Declaring variables inside :root is the universally accepted best practice for defining global styles for several distinct reasons:

1. True Global Scope and Inheritance

Because :root corresponds to the very top of the DOM tree, any custom properties defined within it automatically cascade down to every element on the page, from the <body> element down to deeply nested child components.

:root {
  --primary-color: #0066cc;
  --font-base: 16px;
  --spacing-unit: 8px;
}

Any element in the document can consume these properties via the var() function without needing separate declarations:

button {
  background-color: var(--primary-color);
  padding: var(--spacing-unit);
}

2. Clear Semantic Intent

Declaring variables within :root immediately signals to other developers that the properties inside are design tokens intended for global application. It visually separates high-level theme configuration (colors, spacing scales, typography) from component-level styling and local variable overrides.

3. Predictable Overriding Behavior

Because :root has a specificity value of (0, 1, 0), it is strong enough to reliably establish defaults, but light enough to be easily overridden at the component or modifier level when scoping variables locally:

/* Global definition */
:root {
  --card-bg: #ffffff;
}

/* Local override for dark theme cards */
.card-dark {
  --card-bg: #1a1a1a;
}

.card {
  background-color: var(--card-bg);
}

4. Broad Document Compatibility

Unlike the html selector, :root is agnostic to the host language. If you are styling an embedded SVG graphic, for instance, there is no <html> element; the root element is <svg>. The :root selector handles both HTML and SVG uniformly, making it the safer and more portable choice for modular CSS.