Back to blog

CSS Custom Properties vs Sass Variables: Dynamic vs Static Styling Compared

When organizing CSS styling systems, using variables is essential for maintaining design system tokens (like colors, margins, and typography sizes) in a single source of truth.

To implement variables, developers choose between two systems: Sass Variables (compiled CSS preprocessors) and CSS Custom Properties (browser-native CSS variables).

These technologies look similar but operate in different execution phases: Sass variables compile down to static values before hitting the browser, while CSS variables remain dynamic at runtime.

In this guide, we will compare Sass and CSS variables, analyze compile-time vs. runtime behaviors, and combine them for themes toggles.

1. Sass Variables: Compile-Time Static Tokenization

Sass variables are declared using the $ symbol and are processed during your local build compilation phase:

// Sass Source Code
$primary-color: #3498db;
$padding-base: 16px;

button {
  background-color: $primary-color;
  padding: $padding-base;
}

When you compile Sass to vanilla CSS, the preprocessor replaces all variable tags with their literal values:

/* Output Vanilla CSS sent to browser */
button {
  background-color: #3498db;
  padding: 16px;
}

Advantages of Sass Variables

  • Zero Browser Overhead: The browser does not spend CPU cycles parsing variables; it reads standard CSS styles.
  • Math and Functions Integration: Sass variables integrate with preprocessor functions (like darken($primary-color, 10%) or mix($color-a, $color-b)). The preprocessor calculates the hex values during compilation.
  • Old Browser Support: Because they compile to standard CSS, they work on every browser version.

2. CSS Custom Properties: Runtime Dynamic Variables

CSS Custom Properties are declared using double dashes -- and evaluated natively inside the browser window:

/* Raw CSS File */
:root {
  --primary-color: #3498db;
  --padding-base: 16px;
}

button {
  background-color: var(--primary-color);
  padding: var(--padding-base);
}

Advantages of CSS Custom Properties

  • DOM Awareness and Inheritance: CSS variables follow the Cascade. You can declare a variable in a parent block and redefine its value inside a specific child component, and the browser adjusts child styles dynamically.
  • JavaScript Interaction: You can read and overwrite CSS variables in real-time using JavaScript, enabling interactive theme switchers:
// Change theme colors dynamically on click events
document.documentElement.style.setProperty('--primary-color', '#2ecc71');
  • Responsive Overrides: You can redefine CSS variables inside @media queries directly, avoiding rewriting duplicate layout rules:
:root {
  --padding-base: 12px;
}

@media (min-width: 768px) {
  :root {
    --padding-base: 20px; /* Automatically applies to all elements using var(--padding-base) */
  }
}

The Crucial Limitation: Sass Functions with CSS Variables

Because Sass variables compile before the browser runs, Sass built-in functions cannot parse native CSS variables.

For example, this code will fail to compile:

// Compiling Error! Sass cannot calculate darken() because the browser has not resolved the CSS variable yet
$bg: var(--theme-color);
body {
  background-color: darken($bg, 10%); 
}

To darken a color dynamically in the browser, you must use modern CSS functions like color-mix() instead:

/* Native CSS alternative */
body {
  background-color: color-mix(in srgb, var(--theme-color) 90%, black);
}

Feature Comparison Matrix

Metric Sass Variables ($) CSS Custom Properties (--)
Execution Phase Build-time (Static compilation) Runtime (Dynamic evaluation)
Browser Awareness None (preprocessed out) Full (accessible via inspector)
JS Manipulation No Yes (native API access)
Cascade Inheritance No (strictly scoping values) Yes (cascades down DOM trees)
Responsive Tweaks Requires duplication rules Redefine variable inside Media Query

The Modern Best Practice: Hybrid Approach

For modern web projects, combine the benefits of both worlds:

  1. Use CSS variables for theme-dependent tokens: Declare colors, background states, and margins as CSS custom properties on the :root level so you can toggle dark modes or swap layout spacing dynamically.
  2. Use Sass variables for local build constants: Use Sass variables for compilation configurations, layout breakpoint limits, and private mathematical variables that do not change based on user interactions.

Conclusion

Sass variables are static design tokens suited for compiling stable stylesheet frameworks. CSS Custom Properties are dynamic browser variables that enable responsive overrides inside media queries, real-time client-side theme modifications using JavaScript, and DOM inheritance. Understanding their compile-time vs. runtime behaviors is key to designing clean, maintainable web design systems.