CSS Custom Properties (CSS Variables) are a new feature in CSS that allows us to create dynamic variables. Similar to Sass variables, we can store any value and reuse the value in our CSS codebase. This short post we will show some simple techniques when migrating a Sass theme to use CSS Custom Properties.

Let’s start with a simple example of a button with a few Sass variables that we want to convert to CSS Custom Properties.

<button class="btn">
  Hello
</button>
$color-neutral-100: #fff !default;
$color-neutral-500: #2d2d2d !default;
$button-padding: 12px 24px !default;

.btn {
  padding: $button-padding;
  background-color: $color-neutral-500;
  color: $color-neutral-100;
}

A consumer of this underlying Sass theme would import the Sass file, change the variables, and then compile out a new CSS file for their web page to consume. If we convert our Sass theme to CSS Custom Properties, then our users can change the theme of our CSS without having to have a build step for Sass. Users can alter the CSS properties on the fly.

Let’s make a simple example of a CSS Custom Property.

/* :root is similar to html HTML selector but has a higher specificity */
:root {
  --primary-color: blue;
}

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

CSS Custom Properties are defined by prefixing a property with two dashes, --. Once defined we can refer to it elsewhere in our CSS. In this snippet our button background-color will now be blue from the referring --primary-color property.

CSS Custom Properties can also have fallback values if the variable is not found to have a value.

button {
  background-color: var(--button-background-color, blue);
}

:root {
  /* if we uncomment this line the button will change from red to blue */
  /* --button-background-color: red; */
}

The second parameter in the variable is a fallback value. If no one sets the --button-background-color with a custom value then the button background color will default to blue. This fallback mechanism can be beneficial for theming.

When migrating from Sass variables, we can keep the existing variables but make them internal build-time only variables.

// private variables only available at build time of your theme
$internal-color-neutral-100: #fff;
$internal-color-neutral-500: #2d2d2d;
$internal-button-padding: 12px 24px;

// public custom properties in theme available for consumer
.btn {
  padding: var(--button-padding, $internal-button-padding);
  background-color: var(--button-background-color, $internal-color-neutral-500);
  color: var(--button-color, $internal-color-neutral-100);
}

This Sass will generate the following CSS:

.btn {
  padding: var(--button-padding, 12px 24px);
  background-color: var(--button-background-color, #2d2d2d);
  color: var(--button-color, #fff);
}

In our example, we make our existing Sass variables private for us to use as default values if no custom theme values are defined. Now we can still reuse and share our Sass variables and are only available in the Sass. We only expose the parts of the theme we want users to be able to customize via CSS Custom Properties.

Now users can extend and customize the theme that we explicitly exposed via CSS Custom Properties. This is important and very useful for components that have CSS Encapsulation via Shadow DOM. This allows your components only to expose a subset API that can be themed.

:root {
  --button-background-color: blue; /* all buttons default to blue */
}

.btn.red {
  --button-background-color: red;
}

.btn.green {
  --button-background-color: green;
}
<button class="btn">
  Hello Custom Blue
</button>

<button class="btn red">
  Hello Custom Red
</button>

<button class="btn green">
  Hello Custom Green
</button>

Using both CSS Custom Properties with Sass, we can create flexible and maintainable custom CSS themes.

View Demo Code