@mdo

Global CSS options with custom properties

May 20, 2020

I’ve been toying around with some ideas for how to use custom properties (aka CSS variables) for global settings in a project. The idea is to provide control to designers/developers over consistent styles across multiple components. See it in action in this CodePen, or read on for details.

With Sass, this is pretty easy:

// Global option
$enable-shadows: false;

// Mixin that consumes the shadow
@mixin box-shadow($shadow...) {
  @if $enable-shadows {
    box-shadow: $shadow;
  }
}

// Put it to use
.component {
  @include box-shadow(0 .5rem 1rem rgba(0,0,0,.1));
}

By default now, box-shadows won’t be included in your compiled CSS, unless you change $enable-shadows to true. Doing this with custom properties isn’t as difficult as you might think, though.

With custom properties, the logic is quite similar thanks to fallback values. If there’s a global --enable-shadows option present, that will take precedence over the fallback value. To enable the .component’s box-shadow, comment out or remove --enable-shadows.

// Global option
:root {
  --enable-shadows: none;
}

// Put it to use
.component {
  box-shadow: var(--enable-shadows, 0 .5rem 1rem rgba(0,0,0,.1));
}

You can also go about this in another way by specifying a global shadow variable and overriding that at the component level with a utility or a modifier class.

// Global option
:root {
  --component-shadow: 0 .5rem 1rem rgba(0,0,0,.1);
}

// Put it to use
.component {
  box-shadow: var(--component-shadow);
}
.remove-shadow {
  // Because the custom property is within this ruleset,
  // it will only remove the shadow for this class
  --component-shadow: none;
}

And with that, you have some basic control over global options in your CSS without the need for a preprocessor.