What Are CSS Custom Properties?
CSS custom properties (also known as CSS variables) are entities defined by CSS authors that represent specific values to be reused throughout a document. They are set using custom property syntax and accessed using the CSS var() function.
Custom properties are subject to the cascade and inherit their value from their parent, making them ideal for defining color arrays that can be customized at different scoping levels. This native CSS feature eliminates the need for preprocessor variables when you need runtime flexibility, allowing you to create design systems that adapt to user preferences and different contexts.
Unlike preprocessor variables that compile away, CSS custom properties remain in the browser, enabling dynamic updates through JavaScript and responsive changes based on media queries.
1:root {2 --primary-color: #3b82f6;3 --secondary-color: #6366f1;4 --success-color: #22c55e;5 --warning-color: #f59e0b;6 --error-color: #ef4444;7 --neutral-100: #f3f4f6;8 --neutral-500: #6b7280;9 --neutral-900: #111827;10}Defining Color Palettes with :root
For global color arrays that apply throughout your application, define custom properties on the :root pseudo-class. This ensures the colors are available across all elements in your document.
Semantic Color Naming
Rather than naming colors by their appearance (like --blue-500), consider semantic naming that describes their purpose (like --primary, --secondary, --success). This approach makes your color system more maintainable as you can change the underlying color values without updating every reference throughout your codebase. When working on responsive design projects, this flexibility becomes invaluable for maintaining consistent branding across different contexts.
Organizing Color Groups
Group related colors into logical categories: brand colors, semantic status colors, and neutral colors. This organization makes it easier to find and update colors as your design evolves and helps team members understand the color system quickly.
Following consistent UX writing principles for naming makes your CSS more intuitive for developers and designers alike.
1:root {2 /* Brand Colors */3 --brand-primary: #2563eb;4 --brand-primary-light: #60a5fa;5 --brand-primary-dark: #1d4ed8;6 --brand-accent: #f59e0b;7 8 /* Semantic Colors */9 --color-success: #22c55e;10 --color-warning: #eab308;11 --color-error: #ef4444;12 --color-info: #3b82f6;13 14 /* Neutral Colors */15 --color-text-primary: #1f2937;16 --color-text-secondary: #6b7280;17 --color-text-muted: #9ca3af;18 --color-background: #ffffff;19 --color-surface: #f9fafb;20 --color-border: #e5e7eb;21}Using @property for Type-Safe Color Variables
The @property at-rule allows more control over custom properties by defining type, inheritance, and initial value. This is particularly useful for color arrays where you want to enforce type consistency and improve the developer experience.
Syntax for @property
When defining a color property with @property, specify the syntax as <color>, set whether it inherits, and provide an initial value. This creates a property that browser tools and frameworks can recognize as a color value, enabling better autocomplete in code editors and clearer debugging information in developer tools.
Benefits for Color Arrays
Using @property with color arrays provides better developer experience through autocomplete in code editors, validation in CSS preprocessing tools, and clearer debugging information in browser developer tools. This type safety helps catch errors early in development.
1@property --brand-primary {2 syntax: "<color>";3 inherits: false;4 initial-value: #3b82f6;5}6 7@property --brand-secondary {8 syntax: "<color>";9 inherits: false;10 initial-value: #6366f1;11}12 13/* Now these properties are type-safe */14:root {15 --brand-primary: #2563eb;16 --brand-secondary: #7c3aed;17}Dynamic Color Mixing with color-mix()
The CSS color-mix() function lets you mix colors in any supported color space directly from your CSS. This eliminates the need for preprocessor functions or JavaScript for creating color variations. According to Chrome for Developers documentation, this function is supported in Chrome 111+, Firefox 113+, and Safari 16.2+.
Basic color-mix() Syntax
The color-mix() function takes a color space, two colors to mix, and optional percentages to control the mix ratio. The percentage indicates how much of each color to include in the mix.
Color Spaces for Mixing
Different color spaces produce different mixing results:
- oklab/oklch: Perceptually uniform, best for natural-looking mixes
- srgb: Good compatibility, predictable results
- hsl: Produces vibrant results for certain color combinations
Creating Tints and Shades
Use color-mix() to create tints by mixing with white and shades by mixing with black. This approach ensures all variations maintain the same hue while varying in lightness, creating harmonious color scales for your design system.
1/* Basic color mixing */2.element {3 --primary-tint: color-mix(in oklab, var(--primary), white 20%);4 --primary-shade: color-mix(in oklab, var(--primary), black 20%);5}6 7/* Different color spaces produce different results */8.element-srgb {9 --mixed-srgb: color-mix(in srgb, blue, white 50%);10}11 12.element-oklab {13 --mixed-oklab: color-mix(in oklab, blue, white 50%);14}15 16/* Creating a full color scale */17:root {18 --primary: #2563eb;19 --primary-100: color-mix(in oklch, var(--primary), white 90%);20 --primary-200: color-mix(in oklch, var(--primary), white 70%);21 --primary-300: color-mix(in oklch, var(--primary), white 50%);22 --primary-400: color-mix(in oklch, var(--primary), white 30%);23 --primary-500: var(--primary);24 --primary-600: color-mix(in oklch, var(--primary), black 30%);25 --primary-700: color-mix(in oklch, var(--primary), black 50%);26 --primary-800: color-mix(in oklch, var(--primary), black 70%);27 --primary-900: color-mix(in oklch, var(--primary), black 90%);28}Building Theme Systems with CSS Color Arrays
CSS custom properties excel at implementing theme systems that can switch between light and dark modes or custom color themes. This capability is essential for modern web applications that need to respect user preferences.
Theme Variable Structure
Structure your color array to support theme switching by having semantic variable names that point to different base colors for each theme. This allows complete theme changes by updating just a few root-level custom properties, reducing maintenance overhead.
Dark Mode Implementation
For dark mode, define a complementary set of colors that follow the same semantic structure as your light theme. Elements reference the same custom property names but the underlying values change based on the color scheme preference. Implementing proper accessibility standards ensures your theme system remains usable for all users.
1:root {2 /* Light theme (default) */3 --bg-primary: #ffffff;4 --bg-secondary: #f9fafb;5 --text-primary: #1f2937;6 --text-secondary: #6b7280;7 --border-color: #e5e7eb;8}9 10@media (prefers-color-scheme: dark) {11 :root {12 /* Dark theme */13 --bg-primary: #111827;14 --bg-secondary: #1f2937;15 --text-primary: #f9fafb;16 --text-secondary: #9ca3af;17 --border-color: #374151;18 }19}20 21/* Class-based dark mode override */22.dark-theme {23 --bg-primary: #111827;24 --bg-secondary: #1f2937;25 --text-primary: #f9fafb;26 --text-secondary: #9ca3af;27 --border-color: #374151;28}JavaScript Integration with Color Variables
CSS custom properties can be read and modified through JavaScript, enabling dynamic color changes based on user interaction or system preferences. As documented by MDN Web Docs, this runtime modification capability is one of the key advantages of CSS variables over preprocessor variables.
Reading Color Values
Use getComputedStyle() to read the current value of a custom property, accounting for any cascading or inherited values. This is useful for analytics, synchronization with non-CSS systems, or implementing color pickers.
Updating Colors Dynamically
Modify custom properties through the style property of elements. Changes to custom properties on the :root element propagate throughout the document, making it efficient to update entire color themes instantly.
1// Read a color variable value2const styles = getComputedStyle(document.documentElement);3const primaryColor = styles.getPropertyValue('--primary-color').trim();4console.log('Primary color:', primaryColor);5 6// Update a color variable7document.documentElement.style.setProperty('--primary-color', '#ff6b6b');8 9// Update theme color dynamically10function setThemeColor(theme) {11 const colors = {12 blue: { primary: '#3b82f6', secondary: '#6366f1' },13 green: { primary: '#22c55e', secondary: '#10b981' },14 purple: { primary: '#a855f7', secondary: '#7c3aed' }15 };16 17 const themeColors = colors[theme];18 if (themeColors) {19 document.documentElement.style.setProperty('--primary-color', themeColors.primary);20 document.documentElement.style.setProperty('--secondary-color', themeColors.secondary);21 }22}Fallback Values and Invalid Values
CSS custom properties support fallback values for when a variable is undefined or contains an invalid value. This capability is essential for creating robust color systems that gracefully handle missing or misconfigured variables.
Fallback Syntax
The var() function accepts a second parameter that serves as a fallback when the referenced variable is not defined. You can chain multiple fallbacks for progressive enhancement scenarios.
Invalid Values
When a custom property contains an invalid value for its context, the property acts as if it were not declared. Understanding this behavior helps prevent unexpected styling issues and ensures your color system remains predictable even when variables contain unexpected values.
1/* Fallback syntax examples */2.element {3 /* Use --primary-color if defined, otherwise use blue */4 background-color: var(--primary-color, blue);5 6 /* Chain multiple fallbacks */7 color: var(--undefined-var, var(--another-var, #333));8}9 10/* Using fallback for progressive enhancement */11.button {12 background-color: var(--button-bg, var(--default-button-bg, #3b82f6));13 border: 1px solid var(--button-border, var(--default-border, transparent));14}Best Practices for Color Arrays
Single Source of Truth
Keep all color definitions in a centralized location, typically a dedicated CSS file that imports into your main stylesheet. This approach makes it easy to audit colors, ensure consistency, and make global changes across your application.
Documentation and Comments
Document the purpose of each color, including when it should be used and any accessibility considerations. This documentation helps team members maintain consistency as the project evolves and serves as a reference for future updates.
Accessibility Considerations
Ensure sufficient contrast between foreground and background colors. Include both primary and emphasis versions of each color to support different interaction states while maintaining accessibility compliance with WCAG guidelines. Following design principles helps create color systems that work for all users.
Naming Conventions
Establish and follow consistent naming conventions across your color array. Use a consistent pattern for scales, variants, and semantic meanings throughout your project.
Maintainability
Update colors in one place and have changes propagate throughout your entire project.
Consistency
Ensure all instances of a color use the exact same value across your application.
Theme Support
Easily implement multiple themes by swapping just a few variable values.
Dynamic Updates
Modify colors in real-time based on user preferences or system settings.
Frequently Asked Questions
Sources
-
MDN Web Docs - Using CSS Custom Properties - Official documentation on CSS custom properties, covering declaration syntax, inheritance, and JavaScript integration
-
Chrome for Developers - CSS color-mix() - Official Chrome documentation on the color-mix() function, syntax, color spaces, and browser support