blog

  • Home
  • blog
  • CSS Custom Properties and Theming

CSS Custom Properties and Theming

We posted not long ago about the difference between native CSS variables (custom properties) and preprocessor variables. There are a few esoteric things preprocessor variables can do that native variables cannot, but for the most part, native variables can do the same things. But, they are more powerful because of how they are live-interpolated. Should their values ever change (e.g. JavaScript, media query hits, etc) the change triggers immediate change on the site.

Cool, right? But still, how actually useful is that? What are the major use cases? I think we’re still seeing those shake out.

One use case, it occurred to me, would be theming of a site (think: custom colors for elements around a site). Rather than writing different CSS for a bunch of different themes, or writing JavaScript that targets all the elements we intend to change and changing them), we just write one base set of CSS that utilizes variables and set those variables to the theme colors.

Imagine we allow the header and footer background of our site to be customized.

header {
  background: var(--mainColor);
}

...

footer {
  background: var(--mainColor);
}

Maybe there is a subheader with a darker variation of that color. Here’s a little trick to lay a transparent layer of color over another:

.subheader {
  background: 
    /* Make a bit darker */
    linear-gradient(
      to top,
      rgba(0, 0, 0, 0.25),
      rgba(0, 0, 0, 0.25)
    )
    var(--mainColor);
}

Where does --mainColor come from?

With theming, the idea is that you ask the user for it. Fortunately we have color inputs:

<input type="color" />

And you could store that value in a database or any other storage mechanism you want. Here’s a little demo where the value is stored in localStorage:

See the Pen Theming a site with CSS Custom Properties by Chris Coyier (@chriscoyier) on CodePen.

The value is plucked out of localStorage and used when the page loads. A default value is also set (in CSS), in case that doesn’t exist.

What makes the above demo so compelling, to me, is how little code it is. Maintaining this as a a feature on a site is largely a CSS endeavour and seems flexible enough to stand the test of time (probably).

Not unusually, I was way behind on this one.

Lots of people think of theming as one of the major use-cases for CSS Custom Properties. Let’s look at some other folks examples.

Giacomo Zinetti has the same kind of color-picker implementation

On his site:

Examples and advice from Harry Roberts

He wrote “Pragmatic, Practical, and Progressive Theming with Custom Properties”, in which he pointed to apps like Twitter and Trello that offer theming directly to users:

Harry does a lot of consulting, and to my surprise, finds himself working with companies that want to do this a lot. He warns:

Theming, the vast majority of the time, is a complete nice-to-have. It is not business critical or usually even important. If you are asked to provide such theming, do not do so at the expense of performance or code quality.

In Sass / In React

In a real-world application of theming through Custom Properties, Dan Bahrami recounts how they did it on Geckoboard, the product he works on:

It’s a React product, but they aren’t using any styles-in-JavaScript stuff, so they opted to do the theming with Custom Properties, through Sass.

@mixin variable($property, $variable, $fallback) {
  #{$property}: $fallback;
  #{$property}: var($variable);
}

So they can do:

.dashboard {
  @include variable(background, --theme-primary-color, blue);
}

Which compiles to having a fallback:

.dashboard {
  background: blue;
  background: var(--theme-primary-color);
}

They also created react-custom-properties which all about applying Custom Properties to components, taking advantage of the fact that you can set Custom Properties as inline styles:

<div style="--theme-primary-color: blue;">

</div>

More than one color and property

It’s not only colors that can change, a Custom Property can be any valid value. Here’s Keith Clark with a demo with multiple colors as well as font size:

See the Pen Using CSS custom properties for theme previews by Keith Clark (@keithclark) on CodePen.

And David Darnes with theming built into a Jekyll site:

The Polymer Project themes through Custom Properties

At least it did in the v1 docs. The idea is that you’d have a web compontent, like:

<iron-icon icon="[[toggleIcon]]">
</iron-icon>

That had smart defaults, but was specifically built to allow styling via theming:

<style>
  iron-icon {
    fill: var(--icon-toggle-color, rgba(0,0,0,0));
    stroke: var(--icon-toggle-outline-color, currentcolor);
  }
  :host([pressed]) iron-icon {
    fill: var(--icon-toggle-pressed-color, currentcolor);
  }
</style>

Which meant that you could set those variables and have the component take on new colors.

Support and fallbacks

Support has gotten pretty good recently:

Green indicates full support at the version listed (and above). Yellow indicates partial support. Red indicates no support. See Caniuse for full browser support details.

Desktop

ChromeOperaFirefoxIEEdgeSafari
493631No159.1

Mobile / Tablet

iOS SafariOpera MobileOpera MiniAndroidAndroid ChromeAndroid Firefox
9.337No565752

Opera Mini and IE are notably missing. We already covered the idea of a fallback through setting a valid non-variable property before the one using a Custom Property.

Normally we’d be able to count on @supports to help us with modern CSS features, but Custom Properties are tricky in that they just stand for other values, so can’t really be trusted with @supports. You could probably use a stand-in, like:

@supports (display: grid) {

}

… but that’s a little sketchy.

Michael Scharnagl documents a JavaScript method for testing:

if (window.CSS && window.CSS.supports && window.CSS.supports('--a', 0)) {
  // CSS custom properties supported.
} else {
  // CSS custom properties not supported
}

Colors and Accessibility

When setting colors for text, and the color behind that text, the contrast between those colors is an accessibility issue. Too little contrast, too hard to read.

One somewhat common solution to this is to choose whether the text should be light or dark (white or black) based on the color behind it.

David Halford has a demo calculating this with JavaScript:

See the Pen JS function for accessible color contrast by David Halford (@davidhalford) on CodePen.

And Brendan Saunders with Sass:

See the Pen Sass text-contrast mixin by Brendan Saunders (@bluesaunders) on CodePen.


CSS Custom Properties and Theming is a post from CSS-Tricks

LEAVE A REPLY