CSS Variables

Fallbacks & Placeholders

A cat looking at a computer screen

Fallbacks

Variable fallbacks are IMO the most powerful feature of CSS variables.

In this context, they don’t refer to brower support like we often think when we hear the term.

They are a way to define a default value for a variable.

--color-primary: var(--color-blue, hsl(219.6 100% 50.2%));

This is especially useful when you want to define a variable that will be used in multiple places, or as a precaution when you don’t know if a variable will be defined or to full-proof in the case of a typo.

.card {
  background: var(--color-white);
  border: solid var(--card-color-primary, gray);
  border-width: 12px 4px 4px;
  border-radius: var(--global-border-radius);

  h2 {
    color: var(--card-color-primary, gray);
  }

  button {
    border-radius: var(--global-border-radius);
    border-color: var(--card-color-primary, gray);
  }

  &:where(.card--hope) {
    --card-color-primary: var(--color-green);
  }

  &:where(.card--empire) {
    --card-color-primary: var(--color-blue);
  }

  &:where(.card--jedi) {

  }
}

Fallbacks can even have their own fallbacks!

There’s no limit to how many fallbacks you can nest
Just don’t get carried away.

--article-block-spacing: var(--site-block-spacing, var(--bu-block-spacing, var(--size-fluid-5, 4rem)));

Placeholders

The fallback’s true power comes into play when you leverage UNDEFINED variables, or Placeholders, with a fallback.

.button {
   background: var(--button-background-color, var(--color-purple-800));
   border: 2px solid var(--button-border-color, var(--color-purple-600));
   color: var(--button-color, var(--color-mono-0));
   font-size: var(--button-font-size, 1.125rem);
   padding-block: 0.75rem;
   padding-inline: 1.5rem;
}

.button--secondary {
   --button-background-color: var(--color-mono-0);
   --button-border-color: var(--color-purple-300);
   --button-color: var(--color-purple-600);
}

.button--sm {
   --button-font-size: 0.75rem;
}

.button--outline {
   --button-background-color: transparent;
}

So what are the advantages here?

  • Cleaner / DRY-er code.
  • Specificity is reduced, making overrides less complex.
  • Retains complex functionality established, especially in regards to STATE.
    we’ll see an example with state change later

Placeholders vs The Cascade

Unlike Sass variables, which are processed during build, CSS variables are processed client-side during page-load within the cascade. Which means they are only computed when they are needed, not stored and reused throughout the stylesheet.

Placeholders set on a parent element and then defined on a child element will NOT modify the parent. Placeholder variables can only be defined on the establishing element or higher in the cascade.

:root {

}

.button {
   background: var(--button-background-color, var(--color-purple-800));
   border: 2px solid var(--button-border-color, var(--color-purple-600));
   color: var(--button-color, var(--color-mono-0));
   font-size: var(--button-font-size, 1.75rem);
   padding-block: 0.75rem;
   padding-inline: 1.5rem;
}

.button__icon {
   --button-color: var(--color-amber-400);
   color: currentColor;
}

Thoughts on naming fallback variables

At all cost you should avoid naming fallbacks and placeholders with phrases like:
-default -fallback -placeholder -override

These names are too generic and can lead to confusion. And in the case of placeholders, you don’t want to give the fallback variable the exact name of the variable it is overriding for fear of creating an infinite loop.

In a future section we’ll look at Private Variables and how they can be used to solve this problem.