</>Jonathan Harrell

Main Menu

Site Tools

Component Variants with Scoped CSS Variables

Scoped CSS variables provide an incredibly easy and clean way to create variants of common interface components like buttons.

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 creating buttons as an example. I want to replicate the familiar set of buttons made popular by Bootstrap and similar frameworks that includes primary, secondary, success, danger, warning, info, light and dark variants.

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

Link to this section

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-border-color: gray;
  --button-text-color: white;
  --button-background-color: gray;
}

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

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

Overriding Variables for Component Variants

Link to this section

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

.button.is-primary {
  --button-border-color: #0069d9;
  --button-text-color: white;
  --button-background-color: #0069d9;
}

.button.is-secondary {
  --button-border-color: #727b84;
  --button-text-color: white;
  --button-background-color: #727b84;
}

/* ... */

Alternate Selector Scheme

Link to this section

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;
  border: 1px solid var(--button-border-color);
  border-radius: 4px;
  color: var(--button-text-color);
  background-color: var(--button-background-color);

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

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

  /* ... */
}

Check out the Codepen for the working result:

Go to experiment

Button Variants with Scoped CSS Variables

Click here to view the experiment on Codepen

Browser Support for CSS Variables

Link to this section

For browsers that do not support custom properties at all, 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 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.

Get a Quick Start with HiQ

Link to this section

My new CSS framework HiQ lets you get started quickly by providing variables that you can customize and then override within your variant selectors. Check out the Buttons section for an example.

In addition, you can use the theme builder for a visual editing experience as you customize your own theme.

HiQ is lightweight and provides truly fluid typography that changes in relation to the viewport width. It is built using PostCSS and can be integrated into your own project as a single static file, or as part of your own PostCSS build.

You can learn more about CSS variables in my article here:

Go to article

Unlocking the Benefits of CSS Variables

More and more developers are starting to use CSS variables, or as they are more correctly known, custom properties. Native CSS custom properties…

More Articles

Go to article

Article published date April 9, 2020

System-Based Theming with Styled Components

Learn how to support system-based theming in Styled Components, while allowing a user to select their preferred theme and persist that choice.

Go to article

Article published date November 5, 2018

Implicit State Sharing in React & Vue

Learn to use React’s Context API and provide/inject in Vue to share state between related components without resorting to a global data store.

Go to article

Article published date August 6, 2018

Component Reusability in React & Vue

Learn how to use render props in React and scoped slots in Vue to create components that are flexible and reusable.

Go to article

Article published date March 11, 2018

What’s the Deal with Margin Collapse?

Learn about margin collapse, a fundamental concept of CSS layout. See visual examples of when margin collapse happens, and when it doesn’t.