What Is CSS Custom Properties?
CSS Custom Properties, formally defined in the CSS Custom Properties for Cascading Variables Module Level 1 specification (W3C), are developer-defined variables declared with a double-hyphen prefix (--variable-name) that store reusable values like colors, spacing, or font sizes directly in the CSS cascade. Unlike preprocessor variables (Sass, Less), they are live in the DOM, meaning JavaScript can read and update them at runtime without recompilation. They inherit through the document tree, follow specificity rules, and can be scoped to specific elements or globally via the :root pseudo-class.
What Is CSS Custom Properties?
CSS Custom Properties, formally defined in the CSS Custom Properties for Cascading Variables Module Level 1 specification (W3C), are developer-defined variables declared with a double-hyphen prefix (--variable-name) that store reusable values like colors, spacing, or font sizes directly in the CSS cascade. Unlike preprocessor variables (Sass, Less), they are live in the DOM, meaning JavaScript can read and update them at runtime without recompilation. They inherit through the document tree, follow specificity rules, and can be scoped to specific elements or globally via the :root pseudo-class.
How CSS Custom Properties Works
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.
Best Practices for CSS Custom Properties
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 & Canvas Builder
Canvas Builder produces Bootstrap 5 HTML output, and Bootstrap 5 is architected around CSS Custom Properties for its color palette, grid gutters, component spacing, and typography, meaning every site Canvas Builder generates inherits a structured custom property system out of the box. The clean, semantic markup Canvas Builder outputs avoids the inline style overrides and div-soup that make custom property scoping difficult, so developers can apply component-level variable overrides to predictable, well-named selectors. Teams using Canvas Builder as a starting point can extend the generated codebase with a design token layer by simply augmenting the :root declarations, achieving consistent theming across all generated components without modifying the underlying HTML structure.
Try Canvas Builder →