CSS Custom Properties (Variables)

Master dynamic design tokens, runtime DOM cascade inheritance, JavaScript live mutation, and the modern `@property` API.

1. Real-World Analogy: Live Electrical Power Grid vs Static Drywall

Think of CSS Custom Properties like a modern smart electrical power grid in a high-tech building:

⚑ Dynamic CSS Variables (`var(--token)`)

Like a live electrical grid with adjustable voltage dials. If you turn the master theme dial from light to dark at the breaker box (:root or data-theme), every lamp and device throughout the building adjusts its color instantly in real-time!

πŸͺ΅ Static Sass/SCSS Variables (`$color`)

Like pre-cut wooden planks. Once the carpenter nails the planks together during the build step (Sass compilation), the wood is fixed in stone. It cannot respond to DOM state changes or user toggles at runtime.

2. Declaration, Scoping & Fallbacks

CSS Variables begin with a double hyphen (--) and are read using the var() function:

/* 1. Global Scope (Available throughout the entire DOM tree) */
:root {
    --primary-color: #6C4CE8;
    --surface-bg: #FFFFFF;
    --base-radius: 8px;
    --font-brand: 'Inter', sans-serif;
}

/* 2. Component Scoped Override */
.warning-banner {
    --primary-color: #EF4444; /* Re-scopes --primary-color only inside this container */
}

/* 3. Usage with Fallback Value */
.action-btn {
    background: var(--btn-accent, var(--primary-color, #0284C7));
    border-radius: var(--base-radius);
}

3. CSS Custom Properties vs Sass/Preprocessor Variables

Feature Native CSS Variables (`--var`) Sass / SCSS Variables (`$var`)
Evaluation Time 🟒 Runtime (In the browser) πŸ”΄ Build-time (Compiled to static CSS)
DOM Cascade & Inheritance βœ… Yes (Obeys DOM nesting & media queries) ❌ No (Static compile-time scoping)
JavaScript Manipulation βœ… Live read/write via element.style.setProperty() ❌ Inaccessible to JavaScript at runtime
Media Query Scoping βœ… Can redefine values directly inside @media ❌ Cannot change variables dynamically in media queries

4. Interactive Workbench: Dynamic Theme Mutation

Observe how redefining design tokens on a container instantly repaints all child components:

Token CSS Architecture CSS
/* 1. Theme Variant Token Defintions */
.theme-light {
    --card-bg: #FFFFFF;
    --card-text: #0F172A;
    --accent-color: #6C4CE8;
    --border-color: #E2E8F0;
}

.theme-dark {
    --card-bg: #0F172A;
    --card-text: #F8FAFC;
    --accent-color: #38BDF8;
    --border-color: #334155;
}

/* 2. Abstract Component uses Tokens */
.token-card {
    background: var(--card-bg);
    color: var(--card-text);
    border: 1.5px solid var(--border-color);
    padding: 16px;
    border-radius: 10px;
}
Visual Result Canvas PREVIEW
.theme-light
Design Token Box
var(--card-bg) = #FFF
.theme-dark
Design Token Box
var(--card-bg) = #0F172A

5. Modern `@property` Registration & Gradient Transitions

By default, CSS cannot interpolate custom variables in transitions because it doesn't know their data type. The modern @property rule registers explicit types (<color>, <percentage>, <angle>), enabling smooth gradient animations:

/* Register typed custom property */
@property --gradient-angle {
    syntax: '<angle>';
    initial-value: 0deg;
    inherits: false;
}

.animated-gradient-border {
    background: conic-gradient(from var(--gradient-angle), #6C4CE8, #38BDF8, #6C4CE8);
    transition: --gradient-angle 0.6s ease;
}
.animated-gradient-border:hover {
    --gradient-angle: 180deg; /* Smoothly rotates the conic gradient! */
}

6. CSS Custom Properties Mastery Quiz Deck

Test your knowledge with 5 placement-ready interactive multiple choice questions:

🎯 Custom Properties Interview Quiz
Score: 0 / 5 Question 1 of 5
JavaScript Interop

How can JavaScript dynamically update the value of a CSS custom property on the root document?

A) `document.variables.set('--primary', 'blue')`
B) `window.cssRoot('--primary', 'blue')`
C) `document.documentElement.style.setProperty('--primary', 'blue')`
D) `document.cssVariables['--primary'] = 'blue'`
Invalid Values

What happens if a CSS variable evaluates to an invalid property value at runtime (e.g. `color: var(--invalid-val)`)?

A) The browser throws a fatal console error and halts CSS rendering.
B) The property is treated as `unset` (inherits from parent or reverts to initial).
C) The browser uses the previous rule in the CSS stylesheet.
D) The element is hidden automatically.
@property API

Why is the `@property` rule essential for animating CSS custom properties in transitions?

A) It registers an explicit type syntax (like <color> or <angle>), enabling the browser to interpolate it.
B) It encrypts variable values in memory.
C) It automatically minifies the CSS file.
D) It converts variables to Sass syntax.
Fallback Syntax

In `padding: var(--card-pad, 16px);`, what happens if `--card-pad` has not been declared anywhere?

A) Padding defaults to 0px.
B) The CSS declaration is ignored completely.
C) The browser generates a compile error.
D) The fallback value `16px` is applied.
Case Sensitivity

Are CSS custom property names case-sensitive?

A) No, `--primaryColor` and `--primarycolor` are identical.
B) Yes, CSS custom property names are strictly case-sensitive.
C) Only when declared inside media queries.
D) Only in Safari browsers.

🎯 Placement Interview Key Takeaways

  • Q: Why is `:root` preferred over `html` for declaring global CSS custom properties?
    Ans: :root is a pseudo-class that has a higher specificity (0, 0, 1, 0) than the html type selector (0, 0, 0, 1) and matches the top-level document element in any document format (including SVG).
  • Q: How do you build high-performance dark mode themes using CSS custom properties?
    Ans: Define all semantic color tokens in :root, and simply redefine them under [data-theme="dark"] or @media (prefers-color-scheme: dark). When switching themes, zero CSS classes on individual components need to changeβ€”the browser cascades the token changes instantly!
  • Q: Can CSS custom properties contain unitless numbers for math?
    Ans: Yes! E.g. --gap: 8; and use it in gap: calc(var(--gap) * 1px);. However, you cannot concatenate strings and numbers directly like var(--gap)px (use calc() multiplication instead).