Skip to content
Glossary

What Is CSS Custom Properties (Variables)?

CSS custom properties, commonly called CSS variables, are entities defined by CSS authors that contain specific values to be reused throughout a document. Declared with a double-dash prefix (--variable-name) and accessed via the var() function, CSS variables enable dynamic theming, consistent design tokens, and easier maintenance of colour systems, spacing scales, and typography settings across a stylesheet.

Declaring and using CSS variables

Variables are declared in a selector scope, most commonly :root for global scope: :root { --primary: #1abc9c; --spacing-md: 1.5rem; --font-body: 'Inter', sans-serif; }. They're accessed with var(): background-color: var(--primary). Variables inherit through the DOM, you can redefine them on a child element to override values within that subtree.

How CSS custom properties work

CSS Custom Properties are declared using a double-hyphen prefix inside a selector block, such as :root { --primary-color: #3b82f6; }, and consumed using the var() function: color: var(--primary-color, #000);. The second argument inside var() is a fallback value, which the browser uses when the property is undefined or invalid at that scope. This fallback mechanism is part of the specification and is critical for building resilient component systems where not every context guarantees a variable is defined. Inheritance is a core behavior: a custom property defined on a parent element is accessible to all its descendants in the DOM tree, behaving exactly like inherited CSS properties. This means you can define a component-level variable on a card component's root element, override it in a specific variant, and every child element within that component picks up the correct value without any additional selectors. This scoping behavior is fundamentally different from Sass variables, which resolve at compile time and produce static output. Custom properties participate fully in the CSS cascade, meaning they can be overridden by media queries, pseudo-classes, and specificity just like any other CSS declaration. A common pattern is redefining variables inside a @media (prefers-color-scheme: dark) block on :root to implement dark mode without touching any component-level selectors. The browser recomputes all dependent properties automatically when a custom property changes, whether triggered by a media query or a JavaScript assignment via element.style.setProperty('--primary-color', '#ef4444'). The computed value of a custom property is always a string; the browser substitutes the variable's value into the declaration before computing types. This means var(--spacing) can be used in calc() expressions like calc(var(--spacing) * 2), making mathematical relationships between design tokens expressible in pure CSS. Browser support is universal across all modern browsers as of the CSS Custom Properties Level 1 spec, and no polyfill is needed for production use today.

CSS variables vs Sass variables

CSS custom properties are runtime variables, they can be changed by JavaScript, vary based on media queries, and work in the browser without a build step. Sass variables ($primary: #1abc9c) are compile-time constants, they produce a fixed value in the output CSS and cannot be changed after compilation. Use CSS variables for themes, dynamic values, and anything that needs to change at runtime. Use Sass variables for build-time configuration.

CSS variables in Bootstrap 5

Bootstrap 5.2+ uses CSS custom properties extensively, all colours, spacing, and component-level values are exposed as variables on :root. This makes Bootstrap themes easier to customise: override --bs-primary, --bs-font-sans-serif, or component-specific variables without touching Bootstrap's source Sass files. Canvas Builder generated HTML can be themed by overriding Bootstrap CSS variables.

CSS custom properties best practices

Define all global design tokens (colors, typography scales, spacing units, border radii, box shadows) as custom properties on :root so every component in a project reads from a single source of truth, making global rebranding a matter of changing variable values rather than hunting through stylesheets. Always provide fallback values inside var() for any property that may be conditionally defined, especially in component libraries consumed by third parties. Use semantic naming conventions like --color-brand-primary instead of --blue-500 so the variable name describes its purpose rather than its current value, allowing the underlying value to change without the name becoming misleading. Scope component-specific overrides to the component's own selector rather than :root to avoid polluting the global namespace; for example, define --card-padding on .card rather than globally, and use CSS inheritance to let child elements pick it up without explicit declarations.

CSS Custom Properties (Variables) and Canvas Builder

Canvas Builder generates Bootstrap 5 HTML that uses CSS custom properties for colours and component values, making generated pages easy to re-theme by overriding a handful of CSS variables.

Start building free

Frequently asked questions

Are CSS variables supported in all browsers?

Yes, CSS custom properties have universal browser support (Chrome 49+, Firefox 31+, Safari 9.1+, Edge 15+). As of 2026, browser support is effectively 100% for any relevant browser.

Can JavaScript change CSS variables?

Yes, document.documentElement.style.setProperty('--primary', '#ff0000') updates a CSS variable at runtime. This is a powerful pattern for dynamic theming, user-selectable colour schemes, and dark mode toggling.

Can CSS Custom Properties be animated with CSS transitions or @keyframes?

CSS Custom Properties themselves are not interpolated by the browser during animations because their computed value is always a string, not a typed number or color. However, you can animate properties that consume a custom property by using @property (Houdini's CSS Properties and Values API), which lets you register a custom property with a specific syntax type (like <color> or <length>) so the browser knows how to interpolate it. Without @property registration, a variable swap during an animation will produce an instant jump rather than a smooth transition.