CSS custom properties (CSS variables) have revolutionized how we approach styling on the web. But beyond simple value storage, there's a powerful technique that allows you to toggle multiple property values with a single custom property--a technique so clever it was independently discovered by multiple developers before being named the "space toggle hack." This guide explores how you can use a single custom property to conditionally apply entire sets of styles across your codebase, enabling elegant theme switching, component variations, and responsive behavior without JavaScript.
Mastering these CSS techniques is essential for modern web development and building maintainable styling systems that scale efficiently across large applications.
What Are CSS Custom Properties
CSS custom properties are entities defined by CSS authors that contain specific values to be reused throughout a document. Unlike preprocessor variables, custom properties are true CSS properties--they cascade, inherit, and can be changed at runtime with JavaScript.
Key Characteristics
- Cascade inheritance: Child elements inherit custom properties from parents, enabling scoped theming
- Runtime modification: JavaScript can update custom properties instantly without re-rendering
- Fallback values: The var() function accepts a second parameter as fallback when the property is undefined
- Media query integration: Custom properties can respond to viewport changes naturally
Understanding custom properties is foundational for advanced CSS techniques. For a deeper dive into CSS units and responsive sizing, explore our guide on rem vs em units and how they relate to custom property scoping.
The Fallback Mechanism
The var() function accepts two parameters: the custom property name and an optional fallback value. When the custom property is not defined or set to certain values, the fallback is used instead. This fallback mechanism is the key to the toggle trick.
1/* Basic fallback syntax */2color: var(--my-color, #000000);3 4/* Fallback can be another custom property */5background: var(--primary-color, var(--default-color, #fff));The Space Toggle Hack
The space toggle hack leverages two special values that work universally in var() calls:
- The space value (
--var: ;): Creates a literal single-space value that doesn't affect any CSS property - The initial value (
--var: initial): Makes the property use its initial value, effectively triggering fallbacks
How the Space Toggle Works
/* Without toggle - base styles */
button {
background: linear-gradient(white, transparent) hsl(220 10% 50%);
border: 1px solid rgb(0 0 0 / .1);
color: rgb(0 0 0 / .8);
}
/* With space toggle - append styles when toggle is ON */
button {
--is-raised: ; /* Space = OFF, initial = ON */
background: var(--is-raised, linear-gradient(white, transparent)) hsl(220 10% 50%);
border: 1px solid var(--is-raised, rgb(0 0 0 / .1));
color: rgb(0 0 0 / var(--is-raised, .8));
}
When --is-raised is set to initial, the fallback values (gradient, border color, opacity) are applied. When set to a space, and base styles remain the fallback is ignored.
Practical Example: Button State Toggle
:root {
--ON: initial;
--OFF: ;
}
button.flat {
--is-raised: var(--OFF);
}
button.raised {
--is-raised: var(--ON);
background: var(--is-raised, linear-gradient(hsl(0,0%,97%), hsl(0,0%,92%))) hsl(220 10% 50%);
border: 1px solid var(--is-raised, rgb(0 0 0 / .1));
box-shadow: var(--is-raised, inset 0 1px 0 hsl(0,0%,100%), 0 1px 3px rgba(0,0,0,.2));
text-shadow: var(--is-raised, 0 1px 0 hsl(0,0%,100%));
}
The toggle controls:
- Background gradient
- Subtle border
- Box shadow for depth
- Text shadow for engraved look
Four Techniques for CSS Property Toggling
1. The calc() Method
Uses calc() to multiply values by 0 or 1, enabling or disabling styles:
--icon-visible: 1;
.icon {
width: calc(16px * var(--icon-visible, 1));
}
button {
gap: calc(1ch * var(--icon-visible, 1) * var(--label-visible, 1));
background: color-mix(in srgb, #d44, #0000 calc(100% * var(--invert, 0)));
}
Pros: Best browser support, works with transforms and color-mix Cons: Only works with numeric values
2. Container Style Queries
Container style queries allow conditional styling based on custom property values:
@container style(--icon-visible: no) {
.icon {
display: none;
}
}
@container style(--label-visible: no) {
.label {
position: absolute;
clip: rect(0 0 0 0);
}
}
Pros: More intuitive, can toggle non-numeric properties Cons: Limited browser support (Chromium only)
3. @property Value Coalescing
Defining properties with multiple valid syntaxes:
@property --label-visible {
syntax: 'yes | no';
inherits: true;
initial-value: yes;
}
Pros: Type-safe definitions, no runtime calculations Cons: Requires many property definitions, verbose code
4. Animation @keyframes Method
Using animation state to toggle styles:
@keyframes label-visibility {
from {
position: absolute;
clip: rect(0, 0, 0, 0);
}
}
.label {
animation: label-visibility 1s calc(var(--label-visible, 1) * -1s)
steps(2) paused forwards;
}
Pros: Works in all browsers Cons: Complex to understand, animation overhead
Practical Use Cases
Theme Switching
A single property can toggle multiple theme values:
body {
background: var(--theme-bg);
color: var(--theme-text);
}
.highlighted {
--apply-dark-theme: initial;
background: var(--apply-dark-theme, var(--theme-bg));
color: var(--apply-dark-theme, var(--theme-text));
}
Responsive Component Variations
Toggles work with media queries for responsive behavior:
ui-button {
--icon-visible: yes;
--label-visible: yes;
}
@media (max-width: 35em) {
ui-button {
--label-visible: no; /* Hide labels on mobile */
}
}
Interactive States
Hover and focus states with toggles:
button {
--interactive: ;
transition: all 0.2s ease;
}
button:hover {
--interactive: initial;
transform: translateY(-2px);
box-shadow: var(--interactive, 0 4px 12px rgba(0,0,0,.15));
}
Best Practices
Use Named Constants
Semantic naming improves code readability:
:root {
/* Semantic toggle states */
--ON: initial;
--OFF: ;
/* Theme modes */
--DARK_MODE: initial;
--LIGHT_MODE: ;
}
Document Toggle Properties
Add comments for maintainability:
/**
* Toggle for raised button appearance
* @type {initial | empty}
* @usage Set to 'initial' for raised effect, empty for flat
*/
--is-raised: ;
Performance Optimization
- Toggle visual-only properties (transform, opacity) rather than layout properties
- Use will-change for animated properties
- Avoid toggling display or position when possible
- Hardware-accelerated properties (transform, opacity) are more performant
For comprehensive CSS performance optimization, consider partnering with our web development team to implement these best practices across your codebase.
Browser Support
Custom Properties (var())
| Browser | Version | Support |
|---|---|---|
| Chrome | 49+ | Full |
| Firefox | 31+ | Full |
| Safari | 9.1+ | Full |
| Edge | 15+ | Full |
@property At-Rule
| Browser | Version | Support |
|---|---|---|
| Chrome | 73+ | Full |
| Firefox | 97+ | Full |
| Safari | 16.4+ | Full |
| Edge | 73+ | Full |
Container Style Queries
| Browser | Version | Support |
|---|---|---|
| Chrome | 105+ | Full |
| Edge | 105+ | Full |
| Firefox | 110+ | Behind flag |
| Safari | Not yet | None |
Fallback Strategy
/* Base styles - works everywhere */
button {
/* Essential styles */
}
/* Toggle enhancements - @property with fallback */
@supports (--custom-property: initial) {
button {
/* Toggle-enhanced styles */
}
}
Limitations and Future of CSS Conditionals
Current Limitations
- No true if/else: The toggle hack only works for appending values or using fallbacks
- Cannot swap values entirely: Cannot say "red if X, white otherwise"
- Readability concerns:
--var: ;looks like a mistake - Property limitations: Some properties cannot be toggled this way
Upcoming Features
- CSS if() function: In discussions at W3C
- container style() queries: Gaining browser support
- CSS @when: Proposal for conditional CSS rules
Conclusion
The CSS custom property toggle trick opens up new possibilities for conditional styling without JavaScript. Whether you use the space toggle hack for maximum compatibility, the initial value for semantic clarity, or @property for type-safe definitions, this technique enables elegant theming systems, responsive components, and interactive states.
Start with:
- Space toggle for maximum compatibility
- @property for larger projects
- Container style queries as browser support improves
Ready to level up your CSS skills? Our web development services team can help you implement advanced CSS patterns like these to build maintainable, performant websites.
Sources
- Lea Verou - The var: ; hack to toggle multiple values with one custom property
- Keith Clark - Pseudo-boolean CSS custom properties
- Ryan Mulligan - CSS @property and the New Style
- MDN Web Docs - @property
- Web.dev - @property: Next-gen CSS variables now with universal browser support
- Smashing Magazine - CSS Custom Properties In The Cascade