CSS Custom Properties (Variables)
debt(d3/e3/b3/t5)
Closest to 'default linter catches the common case' (d3). Stylelint (from detection_hints.tools) can detect hard-coded color values and missing custom property usage. The code_pattern explicitly mentions these are detectable: repeated hard-coded colors, no --variable usage.
Closest to 'simple parameterised fix' (e3). The quick_fix describes defining theme values as custom properties on :root - this is a straightforward pattern replacement across CSS files. Converting hard-coded values to var(--token) is mechanical but touches multiple style declarations, making it slightly more than a one-liner but not a significant refactor.
Closest to 'localised tax' (b3). Custom properties apply to web context only and are frontend-scoped. Once established, they reduce burden by centralizing design tokens. The gravitational pull is moderate - they affect styling decisions but don't define system architecture. They're a design-system enabler that can be adopted incrementally.
Closest to 'notable trap' (t5). The misconception field explicitly states developers confuse CSS custom properties with preprocessor variables (Sass/Less). This is a documented gotcha: preprocessor variables resolve at compile-time producing static CSS, while CSS custom properties are live DOM properties changeable at runtime. Developers familiar with Sass will initially treat them identically and miss the runtime/cascade benefits.
Also Known As
TL;DR
Explanation
CSS custom properties (declared with -- prefix, accessed with var(--name, fallback)) are live values that participate in the cascade and inherit through the DOM. Unlike preprocessor variables (Sass $var) which are compiled away, custom properties exist at runtime — JavaScript can read and set them: element.style.setProperty('--color', '#f00'). This enables: theme switching without reloading CSS, component-level overrides by redefining variables on a parent, and dynamic animations driven by JS. Scope: variables defined on :root are global; scoped to a selector they only apply within it. Combined with @layer and container queries, custom properties replace many Sass/Less use cases with native browser support.
Common Misconception
Why It Matters
Common Mistakes
- Not defining custom properties on :root — they need a global scope to be accessible everywhere.
- Using preprocessor variables (Sass $variable) when CSS custom properties would allow runtime theming.
- Not providing fallback values: var(--color, #333) for environments that don't support custom properties.
- Deeply nested overrides without documentation — the cascade applies to custom properties too.
Code Examples
/* Magic hex values repeated everywhere — impossible to retheme:
.btn { background: #3b82f6; }
.link { color: #3b82f6; }
.badge { border-color: #3b82f6; }
/* With custom properties — change once, update everywhere: */
:root { --color-primary: #3b82f6; }
.btn { background: var(--color-primary); }
.link { color: var(--color-primary); }
:root { --gap: 1rem; --accent: #4f9cff; }
.card { padding: var(--gap); border-color: var(--accent); }
/* Override at component level */
.compact .card { --gap: 0.5rem; }