What Are CSS Custom Properties?
CSS custom properties are entities defined by CSS authors that represent specific values to be reused throughout a document. While historically referred to as "CSS variables," the official specification uses the term "custom properties" to emphasize their unique behavior around the cascade and inheritance.
The key characteristics that distinguish CSS custom properties from preprocessor variables include their participation in the CSS cascade, their ability to be modified at runtime through JavaScript, and their support for inheritance from parent to child elements. These features make them far more powerful than traditional CSS preprocessor variables.
Custom properties are defined using two dashes as a prefix (--), followed by the property name. This convention was chosen to prevent conflicts with current and future CSS syntax. For a broader understanding of how CSS properties work, including cascading and specificity, see our guide on styling basics.
1/* Define a custom property */2.element {3 --primary-color: #3b82f6;4 --spacing-unit: 1rem;5 --border-radius: 0.5rem;6 --font-stack: system-ui, -apple-system, sans-serif;7}8 9/* Reference with var() */10.button {11 background-color: var(--primary-color);12 padding: var(--spacing-unit) calc(var(--spacing-unit) * 2);13 border-radius: var(--border-radius);14 font-family: var(--font-stack);15}Declaring Custom Properties
Basic Syntax with the -- Prefix
The most common way to declare a custom property is by using two dashes as a prefix for the property name, followed by a colon and a valid CSS value. This syntax is simple and requires no additional tooling or at-rules.
The selector in which you declare a custom property determines its scope. Properties defined within a specific ruleset are only available to that ruleset and its descendants.
Defining Global Custom Properties with :root
For values that need to be available throughout your entire document, the :root pseudo-class provides a natural place to declare global custom properties. Understanding scope is essential for writing maintainable CSS, as it relates closely to how CSS data values are stored and accessed throughout a stylesheet.
1/* Global design tokens */2:root {3 --color-primary: #3b82f6;4 --color-secondary: #6366f1;5 --color-success: #22c55e;6 --color-warning: #f59e0b;7 --color-error: #ef4444;8 9 --font-sans: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;10 --font-mono: 'SF Mono', Consolas, 'Liberation Mono', Menlo, monospace;11 12 --spacing-xs: 0.25rem;13 --spacing-sm: 0.5rem;14 --spacing-md: 1rem;15 --spacing-lg: 1.5rem;16 --spacing-xl: 2rem;17 18 --border-radius-sm: 0.25rem;19 --border-radius-md: 0.5rem;20 --border-radius-lg: 1rem;21 22 --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);23 --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);24 --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);25}Advanced Definition with @property
The @property at-rule provides greater control over custom property definitions. It allows you to specify the expected syntax type, control whether the property inherits from its parent, and set an initial value.
The syntax descriptor tells the browser what type of value the property should contain. While CSS won't strictly enforce this validation, it enables tooling to provide better error checking and developer feedback.
1/* Typed custom properties with @property */2@property --brand-color {3 syntax: '<color>';4 inherits: false;5 initial-value: #3b82f6;6}7 8@property --spacing-scale {9 syntax: '<number>';10 inherits: true;11 initial-value: 1;12}13 14@property --animation-duration {15 syntax: '<time>';16 inherits: false;17 initial-value: 300ms;18}Using the var() Function with Fallback Values
The var() function accepts an optional second argument that serves as a fallback value. This fallback is used when the referenced custom property is not defined, is invalid, or equals a CSS-wide keyword.
Fallback values can themselves contain var() functions, creating chains of fallbacks that provide great flexibility in handling different scenarios.
1/* Fallback to a literal value */2.alert {3 background-color: var(--alert-bg-color, #fef2f2);4 border-left: 4px solid var(--alert-border-color, #ef4444);5}6 7/* Fallback to another custom property */8.button-secondary {9 background-color: var(--btn-bg, var(--primary-color));10}11 12/* Chained fallbacks */13.component {14 color: var(--theme-text, var(--text-color, #1f2937));15}16 17/* Empty fallback (useful for conditionally applying values) */18.stretch {19 width: var(--full-width,);20}Using Custom Properties with JavaScript
JavaScript provides access to custom properties through the computed style of elements. Runtime modification of custom properties is particularly powerful for implementing dark mode toggles, user preference themes, and dynamic brand customization. This combination of CSS and JavaScript creates powerful possibilities for AI-powered automation scenarios where themes need to adapt based on user behavior or preferences.
Reading Custom Property Values
The getPropertyValue method retrieves the current value of a custom property.
Setting Custom Property Values
JavaScript can modify custom properties at runtime, enabling dynamic theming without page reloads. For more advanced use cases involving dynamic styling, see our documentation on the this keyword which often comes into play when working with element references.
1// Read a custom property from an element2const element = document.documentElement;3const primaryColor = getComputedStyle(element)4 .getPropertyValue('--primary-color');5 6console.log(primaryColor.trim()); // "#3b82f6"7 8// Set a custom property globally9document.documentElement.style.setProperty('--primary-color', '#6366f1');10 11// Update based on user preference12function updateThemeColors() {13 const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;14 15 document.documentElement.style.setProperty(16 '--background',17 isDark ? '#0f172a' : '#ffffff'18 );19 document.documentElement.style.setProperty(20 '--text-primary',21 isDark ? '#f8fafc' : '#1f2937'22 );23}Dark Mode Implementation with CSS Custom Properties
Custom properties provide an elegant solution for dark mode themes. By defining color custom properties on :root and overriding them in a media query or data attribute, you can switch entire color schemes seamlessly. This approach is widely used in modern web development services for creating user-friendly experiences.
1:root {2 --background-primary: #ffffff;3 --background-secondary: #f3f4f6;4 --text-primary: #111827;5 --text-secondary: #4b5563;6 --border-color: #e5e7eb;7}8 9@media (prefers-color-scheme: dark) {10 :root {11 --background-primary: #0f172a;12 --background-secondary: #1e293b;13 --text-primary: #f8fafc;14 --text-secondary: #94a3b8;15 --border-color: #334155;16 }17}18 19/* Manual dark mode toggle */20[data-theme="dark"] {21 --background-primary: #0f172a;22 --background-secondary: #1e293b;23 --text-primary: #f8fafc;24 --text-secondary: #94a3b8;25 --border-color: #334155;26}27 28body {29 background-color: var(--background-primary);30 color: var(--text-primary);31}Best Practices
Naming Conventions
Use descriptive, semantic names for your custom properties. Names should communicate the purpose or role of the value rather than its specific appearance.
Organization
Keep your custom property definitions organized and well-documented. Group related properties together and use comments to explain their purpose.
Performance Considerations
While custom properties offer tremendous flexibility, be mindful of performance implications. Modifying custom properties on many elements can trigger style recalculations. For properties that don't need to change dynamically, consider using CSS custom properties exclusively for values that benefit from the cascade and inheritance.
1/* Good: Semantic naming */2:root {3 --text-primary: #1f2937;4 --text-secondary: #4b5563;5 --background-surface: #ffffff;6 --background-elevated: #f9fafb;7}8 9/* Organize with comments */10:root {11 /* Colors */12 --color-primary: #3b82f6;13 --color-primary-hover: #2563eb;14 --color-secondary: #6366f1;15 16 --color-success: #22c55e;17 --color-warning: #f59e0b;18 --color-error: #ef4444;19 20 /* Typography */21 --font-sans: system-ui, -apple-system, sans-serif;22 --font-mono: 'SF Mono', Consolas, monospace;23 24 /* Spacing */25 --space-1: 0.25rem;26 --space-2: 0.5rem;27 --space-4: 1rem;28 --space-8: 2rem;29 --space-16: 4rem;30}Summary
CSS custom properties and the var() function represent a fundamental shift in how we write and maintain stylesheets. They bring true variables to CSS, enabling better organization, easier maintenance, and powerful runtime customization.
By understanding how to declare custom properties, use the var() function with proper fallbacks, manage scope and inheritance, and integrate with JavaScript, you can build more maintainable and flexible styling systems for modern web applications.
For teams building modern web applications, mastering CSS custom properties is essential for creating maintainable design systems. When combined with proper CSS architecture, custom properties provide a powerful foundation for theming, responsive design, and dynamic styling that scales with your project.