|$ curl https://forge-ai.dev/api/markdown?path=docs/css/variables
$cat docs/css-variables.md
updated Recently·12 min read·published

CSS Variables

CSSVariablesFundamentalsIntermediate
Introduction

CSS Custom Properties, commonly known as CSS variables, are entities defined by authors that contain specific values to be reused throughout a document. Unlike preprocessor variables (Sass, Less), CSS variables are live — they can be changed at runtime via JavaScript, respond to the cascade, and inherit through the DOM tree.

CSS variables enable powerful patterns that were previously impossible: dynamic theming without duplicate stylesheets, component-scoped customization, responsive property changes, and real-time style updates from JavaScript.

variables-basics.css
CSS
1:root {
2 --accent: #00FF41;
3 --bg: #0D0D0D;
4 --text: #E0E0E0;
5 --muted: #808080;
6}
7
8.element {
9 color: var(--accent);
10 background: var(--bg);
11}
Defining Custom Properties

Custom properties use a -- prefix followed by a name (case-sensitive). They are scoped to the element they are defined on, plus its descendants. The :root pseudo-class is used for global variables.

defining-variables.css
CSS
1/* Global variables on :root */
2:root {
3 --color-primary: #00FF41;
4 --color-bg: #0D0D0D;
5 --color-text: #E0E0E0;
6 --color-muted: #808080;
7 --color-border: #222222;
8 --font-mono: "SF Mono", Menlo, monospace;
9 --font-sans: system-ui, -apple-system, sans-serif;
10 --spacing-xs: 4px;
11 --spacing-sm: 8px;
12 --spacing-md: 16px;
13 --spacing-lg: 24px;
14 --spacing-xl: 32px;
15 --radius-sm: 4px;
16 --radius-md: 8px;
17 --radius-lg: 12px;
18 --shadow-sm: 0 1px 3px rgba(0,0,0,0.12);
19 --shadow-md: 0 4px 12px rgba(0,0,0,0.15);
20 --transition-fast: 150ms ease;
21 --transition-normal: 250ms ease;
22}
23
24/* Scoped component variables */
25.card {
26 --card-padding: 20px;
27 --card-bg: #1A1A2E;
28
29 background: var(--card-bg);
30 padding: var(--card-padding);
31 border-radius: var(--radius-md);
32}
33
34/* Variables hold any CSS value */
35:root {
36 --gradient: linear-gradient(135deg, #00FF41, transparent);
37 --border: 2px solid var(--color-primary);
38}

info

CSS custom properties are case-sensitive: --color and --Color differ. Follow a consistent naming convention like BEM or design-token hierarchy (e.g. --color-primary-hover).
The var() Function

The var() function retrieves a custom property value. It accepts the property name and an optional fallback. If the property is not defined, the fallback is used.

var-function.css
CSS
1/* Basic usage */
2.element {
3 color: var(--color-primary);
4 background: var(--color-bg);
5}
6
7/* With fallback */
8.element {
9 color: var(--color-primary, #00FF41);
10 background: var(--color-bg, #0D0D0D);
11}
12
13/* Nested fallbacks */
14.element {
15 padding: var(--spacing-xl, var(--spacing-lg, 32px));
16}
17
18/* In shorthand properties */
19.element {
20 border: var(--border-width, 2px) solid var(--color-primary, #00FF41);
21}
22
23/* In calc() */
24.element {
25 padding: calc(var(--spacing-md) * 2);
26 width: calc(100% - var(--sidebar-width, 250px));
27}
Fallback Values

Providing robust fallbacks ensures graceful degradation when custom properties are undefined or when the browser does not support them.

fallback-values.css
CSS
1/* Direct fallback */
2.element {
3 color: var(--undefined-var, #00FF41);
4}
5
6/* Fallback chain */
7.element {
8 padding: var(--spacing-xl, var(--spacing-lg, var(--spacing-md, 32px)));
9}
10
11/* Fallback for unsupported browsers */
12.element {
13 color: #00FF41; /* older browsers */
14 color: var(--color-primary); /* modern override */
15}
16
17/* Practical terminal fallback */
18.terminal-element {
19 background: #0D0D0D;
20 background: var(--terminal-bg, #0D0D0D);
21 color: #E0E0E0;
22 color: var(--terminal-text, #E0E0E0);
23}

warning

The var() fallback only works when the custom property is not defined. If defined but invalid for the property, the browser uses the inherited or initial value — not the fallback.
Scope & Inheritance

CSS variables inherit through the DOM tree. A variable on a parent is accessible to all children. Overriding on a child creates a new scope for that subtree. This inheritance crosses shadow DOM boundaries, making variables ideal for web component theming.

scope-inheritance.css
CSS
1/* Global scope */
2:root {
3 --color: #00FF41;
4 --bg: #0D0D0D;
5}
6
7/* Override in section */
8.sidebar {
9 --color: #FFB000;
10 --bg: #1A1A1A;
11}
12
13.sidebar .item {
14 color: var(--color); /* #FFB000 — from .sidebar */
15 background: var(--bg); /* #1A1A1A */
16}
17
18/* Per-element override */
19.item.special {
20 --color: #3B82F6;
21}
22
23/* Shadow DOM theming */
24/* Custom properties penetrate shadow DOM automatically */
preview
Dynamic Theming

CSS variables make dynamic theming trivial. Define color schemes as variable sets and swap a class or attribute on a parent element — the entire UI updates instantly without repaints or re-layouts.

dynamic-theming.css
CSS
1/* Light theme (default) */
2:root {
3 --bg-primary: #FFFFFF;
4 --bg-secondary: #F5F5F5;
5 --text-primary: #1A1A1A;
6 --text-secondary: #666666;
7 --accent: #00FF41;
8 --border: #E0E0E0;
9}
10
11/* Dark theme */
12[data-theme="dark"] {
13 --bg-primary: #0D0D0D;
14 --bg-secondary: #1A1A2E;
15 --text-primary: #E0E0E0;
16 --text-secondary: #808080;
17 --accent: #00FF41;
18 --border: #222222;
19}
20
21/* Terminal theme */
22[data-theme="terminal"] {
23 --bg-primary: #0D0D0D;
24 --text-primary: #E0E0E0;
25 --accent: #00FF41;
26 --font-family: "SF Mono", Menlo, monospace;
27}
28
29/* Use variables */
30body {
31 background: var(--bg-primary);
32 color: var(--text-primary);
33 font-family: var(--font-family, system-ui);
34}
35
36.card {
37 background: var(--bg-secondary);
38 border: 1px solid var(--border);
39}
40
41/* Toggle via class on html */
42html.dark {
43 --bg-primary: #0D0D0D;
44 --text-primary: #E0E0E0;
45}
Changing Variables with JavaScript

CSS custom properties can be read and modified at runtime using the CSS Object Model API. This enables dynamic theming, real-time adjustments, and user-driven customization.

javascript-variables.js
JavaScript
1// Set a variable on an element
2element.style.setProperty("--color-primary", "#00FF41");
3
4// Set global variable on :root
5document.documentElement.style.setProperty("--theme", "dark");
6
7// Read a variable value
8const color = getComputedStyle(element)
9 .getPropertyValue("--color-primary")
10 .trim();
11
12// Toggle theme
13function toggleTheme() {
14 const root = document.documentElement;
15 root.style.setProperty("--bg-primary",
16 root.style.getPropertyValue("--bg-primary") === "#0D0D0D"
17 ? "#FFFFFF" : "#0D0D0D"
18 );
19 root.style.setProperty("--text-primary",
20 root.style.getPropertyValue("--text-primary") === "#E0E0E0"
21 ? "#1A1A1A" : "#E0E0E0"
22 );
23}
24
25// Responsive variables
26window.addEventListener("resize", () => {
27 const vw = window.innerWidth;
28 document.documentElement.style.setProperty(
29 "--responsive-padding",
30 vw < 768 ? "16px" : "32px"
31 );
32});
theme-toggle.html
HTML
1<!-- Theme toggle with persistence -->
2<button onclick="setTheme('light')">Light</button>
3<button onclick="setTheme('dark')">Dark</button>
4<button onclick="setTheme('terminal')">Terminal</button>
5
6<script>
7 function setTheme(theme) {
8 document.documentElement.setAttribute("data-theme", theme);
9 localStorage.setItem("theme", theme);
10 }
11 const saved = localStorage.getItem("theme");
12 if (saved) document.documentElement.setAttribute("data-theme", saved);
13</script>
🔥

pro tip

CSS variables enable runtime theming without JavaScript frameworks. Unlike preprocessor variables which compile to static values, CSS variables update live via element.style.setProperty() — perfect for color pickers, dark mode, and real-time theme switching.
@property — Typed Variables

The @property at-rule registers custom properties with a defined syntax, initial value, and inheritance behavior. Typed variables enable browser animation of custom properties and value validation — impossible with regular var().

property-rule.css
CSS
1/* Register typed custom properties */
2@property --brand-color {
3 syntax: "<color>";
4 inherits: false;
5 initial-value: #00FF41;
6}
7
8@property --spacing {
9 syntax: "<length>";
10 inherits: true;
11 initial-value: 16px;
12}
13
14@property --rotation {
15 syntax: "<angle>";
16 inherits: false;
17 initial-value: 0deg;
18}
19
20/* Now CSS can animate custom properties */
21@keyframes spin {
22 from { --rotation: 0deg; }
23 to { --rotation: 360deg; }
24}
25
26.animated {
27 --rotation: 0deg;
28 transform: rotate(var(--rotation));
29 animation: spin 2s linear infinite;
30}
31
32/* Supported syntax types */
33/* <length>, <number>, <percentage>, <color> */
34/* <image>, <url>, <integer>, <angle> */
35/* <time>, <resolution>, <custom-ident> */
36/* <transform-function>, <transform-list> */
37/* * — any valid value */

best practice

Use @property for any custom property that needs animation, type safety, or a guaranteed initial value. Without @property, CSS cannot animate custom properties because it does not know their value type.
Terminal Theme Example

A complete terminal design token system using CSS variables. This pattern powers this documentation site and can be the foundation for any terminal-styled application.

terminal-theme.css
CSS
1/* Terminal Design Token System */
2:root {
3 /* Core palette */
4 --terminal-bg: #0D0D0D;
5 --terminal-surface: #0A0A0A;
6 --terminal-elevated: #1A1A2E;
7
8 /* Text hierarchy */
9 --terminal-text-primary: #E0E0E0;
10 --terminal-text-body: #A0A0A0;
11 --terminal-text-muted: #808080;
12 --terminal-text-dim: #525252;
13
14 /* Accent colors */
15 --terminal-accent: #00FF41;
16 --terminal-accent-dim: rgba(0, 255, 65, 0.15);
17 --terminal-warning: #FFB000;
18 --terminal-error: #EF4444;
19 --terminal-info: #3B82F6;
20
21 /* Borders */
22 --terminal-border: #222222;
23 --terminal-border-light: rgba(0, 255, 65, 0.1);
24
25 /* Typography */
26 --terminal-font-mono: "SF Mono", Menlo, "Fira Code", monospace;
27 --terminal-font-ui: system-ui, -apple-system, sans-serif;
28 --terminal-font-size: 13px;
29 --terminal-line-height: 1.5;
30
31 /* Spacing */
32 --terminal-space-xs: 4px;
33 --terminal-space-sm: 8px;
34 --terminal-space-md: 16px;
35 --terminal-space-lg: 24px;
36 --terminal-space-xl: 32px;
37
38 /* Effects */
39 --terminal-radius: 8px;
40 --terminal-glow: 0 0 20px rgba(0, 255, 65, 0.15);
41 --terminal-transition: 200ms ease;
42}
43
44/* Usage */
45.terminal-panel {
46 background: var(--terminal-surface);
47 border: 1px solid var(--terminal-border);
48 border-radius: var(--terminal-radius);
49 padding: var(--terminal-space-md);
50 color: var(--terminal-text-body);
51 font-family: var(--terminal-font-mono);
52 font-size: var(--terminal-font-size);
53 line-height: var(--terminal-line-height);
54}
55
56.terminal-accent {
57 color: var(--terminal-accent);
58}
59
60.terminal-dim {
61 color: var(--terminal-text-dim);
62}
preview
Best Practices
Use :root for global design tokens and scope variables to components for encapsulation
Follow a consistent naming convention like --component-property-state (e.g. --button-bg-hover)
Always provide fallback values for defensive styling
Duplicate declarations for older browsers that do not support CSS variables
Use @property for variables that need animation or type safety
Use CSS variables for theming — they cascade through shadow DOM, enabling web component theming
Avoid using CSS variables in media queries (not supported — use regular values or JavaScript)
Use calc() with variables for responsive math (e.g. calc(var(--spacing) * 2))
Organize variables logically: palette, typography, spacing, effects, component-specific

best practice

CSS variables are the backbone of modern CSS architecture. Combine them with calc() for responsive math, @property for typed animations, and JavaScript for runtime theming. They eliminate the need for CSS preprocessors for variable management and unlock patterns like dark mode that were previously complex to implement.
$Blueprint — Engineering Documentation·Section ID: CSS-17·Revision: 1.0