Use CSS custom properties in WordPress themes

CSS custom properties — commonly called CSS variables — were introduced in the CSS spec in 2014 and are now supported by every modern browser with zero polyfill required. They let you define a value once in a central place and reference it throughout your stylesheet, making global changes like a colour scheme swap or a font-size adjustment a one-line edit rather than a search-and-replace operation across hundreds of rules. Unlike SASS/LESS variables that are resolved at compile time, CSS custom properties are resolved at runtime, which means JavaScript can read and modify them, and they can cascade through the DOM just like any other CSS property — child elements inherit them from parent elements. Defining your theme’s brand colours, spacing scale, and typography as custom properties in the :root selector is considered best practice for any modern WordPress theme. Gutenberg’s theme.json file (introduced in WordPress 5.8) automatically generates CSS custom properties from your theme’s design tokens, so understanding this syntax is essential for block theme development. Custom properties also enable clean dark-mode implementations: define a light-mode palette at :root and override it inside a @media (prefers-color-scheme: dark) block with zero JavaScript. Combining this with Flexbox and CSS Grid gives you a powerful, maintainable layout system. The examples below show how to define and use CSS variables in a WordPress theme and how to update them with JavaScript.

Problem: Your WordPress theme hard-codes colour, spacing, and font values in dozens of places, making global design changes tedious and error-prone.

Solution: Define CSS custom properties in your theme’s style.css and reference them throughout:

/* ── Design tokens — define once, use everywhere ── */
:root {
    --color-primary:    #2563eb;
    --color-secondary:  #1e40af;
    --color-text:       #1f2937;
    --color-bg:         #ffffff;
    --color-border:     #e5e7eb;

    --font-sans:        'Inter', sans-serif;
    --font-size-base:   1rem;       /* 16px */
    --line-height-base: 1.6;

    --space-sm:  0.5rem;
    --space-md:  1rem;
    --space-lg:  2rem;
    --space-xl:  4rem;

    --border-radius: 0.375rem;
    --shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
}

/* ── Dark mode override ── */
@media (prefers-color-scheme: dark) {
    :root {
        --color-primary: #60a5fa;
        --color-text:    #f9fafb;
        --color-bg:      #111827;
        --color-border:  #374151;
    }
}

/* ── Usage ── */
body {
    font-family: var(--font-sans);
    font-size:   var(--font-size-base);
    line-height: var(--line-height-base);
    color:       var(--color-text);
    background:  var(--color-bg);
}

.btn-primary {
    background-color: var(--color-primary);
    padding: var(--space-sm) var(--space-md);
    border-radius: var(--border-radius);
}

/* ── Update a variable with JavaScript ── */
/* document.documentElement.style.setProperty('--color-primary', '#e11d48'); */

NOTE: CSS custom properties are case-sensitive: --Color-Primary and --color-primary are two different properties. You can provide a fallback value as the second argument to var(): var(--color-primary, blue) — this is useful during development or when a property might not be defined in all contexts. In WordPress block themes, custom properties defined in theme.json follow the naming pattern --wp--preset--color--{slug}, so avoid naming conflicts by prefixing your own variables with your theme or plugin name.