Skip to content
Jonathan Harrell

Article tags

  • css
  • theming

Component Variants with Scoped CSS Variables

Generating variants of common interface components like buttons, inputs, cards, etc., has typically involved multiple class names, pre-processor mixins, or repeated code. Now, component variants can be created in a clean and straightforward manner using CSS variables.

Let’s take buttons as an example. I want to create a set of different buttons, including common variants like “primary” and “secondary”.

Base Styling

I’m going to start by giving these buttons some base styling. I can give the button a base class and then add additional classes for each variant.

<button class="button is-primary">Primary</button>
<button class="button is-secondary">Secondary</button>
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: auto;
  height: 2.5rem;
  padding: 0 1rem;
  border-radius: 4px;
}

Defining Variables

Next, I’m going to define some variables on the :root. These will be the properties that need to change with each variant.

:root {
  --button-background-color: gray;
  --button-border-color: gray;
  --button-text-color: black;
}

I will use these to add color to my base styling:

.button {
  /* ... */
  background-color: var(--button-background-color);
  border: 1px solid var(--button-border-color);
  color: var(--button-text-color);
}

Overriding Variables for Component Variants

Finally, I will override these variable values within each variant selector.

.button.is-primary {
  --button-background-color: orange;
  --button-border-color: orange;
  --button-text-color: white;
}

.button.is-secondary {
  --button-background-color: black;
  --button-border-color: black;
  --button-text-color: white;
}

Alternate Selector Scheme

If I wanted to simplify my classes further, I could use only one class to define both the base styling and the variants.

<button class="button-primary">Primary</button>
<button class="button-secondary">Secondary</button>

Here I’m nesting variant selectors with PostCSS:

[class^='button'] {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: auto;
  height: 2.5rem;
  padding: 0 1rem;
  background-color: var(--button-background-color);
  border: 1px solid var(--button-border-color);
  border-radius: 4px;
  color: var(--button-text-color);

  &[class*='primary'] {
    --button-background-color: orange;
    --button-border-color: orange;
    --button-text-color: white;
  }

  &[class*='secondary'] {
    --button-background-color: black;
    --button-border-color: black;
    --button-text-color: white;
  }
}

Browser Support for CSS Variables

For browsers that do not support custom properties, you can use the PostCSS custom properties plugin. This will compile the CSS variables as static values.

Keep in mind, however, that this will not allow you to override the variable values, as the variables will no longer exist in the browser, having already been evaluated during the CSS build.

The technique for component variants described in this article is future-looking and, as more and more browsers fully support custom properties, will be able to be used in production sites.

Subscribe

Want more front-end tips and tricks? Sign up for my newsletter to stay up-to-date.