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.
<!DOCTYPE html>
<html>
<head>
<title>ID Sandbox</title>
<meta charset="UTF-8" />
<link rel="stylesheet" href="/base.css" />
<link rel="stylesheet" href="/styles.css" />
</head>
<body>
<div class="grid">
<article class="card card--hope">
<h2>Episode IV:</br>A New Hope</h2>
<div class="skeleton-text"></div>
<div class="skeleton-text"></div>
<button>Watch</button>
</article>
<article class="card card--empire">
<h2>Episode V:</br>The Empire Strikes Back</h2>
<div class="skeleton-text"></div>
<div class="skeleton-text"></div>
<button>Watch</button>
</article>
<article class="card card--jedi">
<h2>Episode VI:</br>Return of the Jedi</h2>
<div class="skeleton-text"></div>
<div class="skeleton-text"></div>
<button>Watch</button>
</article>
</div>
</body>
</html>
:root {
--global-border-radius: 0.25rem;
--color-blue: hsl(219.6 100% 50.2%);
--color-green: hsl(141.6 72.4% 45.2%);
--color-red: hsl(0.7 89.6% 57.2%);
--color-slate: hsl(227.1 29.5% 16.7%);
--color-white: hsl(0 0% 100%);
}
body {
background: var(--color-slate);
border: 2px solid white;
block-size: calc(100vh - 12px);
display: grid;
font-family: 'Silka', sans-serif;
font-size: 1.125rem;
margin: 0.25em;
padding: 0 2rem;
place-items: center;
}
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
}
h2 {
font-size: 1.25rem;
font-weight: bold;
margin-block-start: 0;
}
.skeleton-text {
block-size: 1.5rem;
background: hsl(0 0% 90%);
border-radius: 2px;
inline-size: 90%;
}
button {
appearance: none;
background: hsl(0 0% 100%);
border: 2px solid hsl(0 0% 50%);
border-radius: 4px;
font-size: 1rem;
font-weight: bold;
letter-spacing: 0.125em;
padding: 0.5rem 1rem;
text-transform: uppercase;
margin-block-start: 1rem;
}
.card {
display: grid;
gap: 1rem;
grid-template-rows: 1fr auto auto auto;
padding: 2rem 1rem;
}
.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.
<!DOCTYPE html>
<html>
<head>
<title>ID Sandbox</title>
<meta charset="UTF-8" />
<link rel="stylesheet" href="/base.css" />
<link rel="stylesheet" href="/styles.css" />
<link rel="stylesheet" href="/colors.css" />
</head>
<body>
<div class="grid">
<button class="button">Default</button>
<button class="button button--secondary">Secondary</button>
<button class="button button--sm">Small</button>
<button class="button button--outline">Outline</button>
</div>
</body>
</html>
:root {
--color-slate-800: hsl(227.1 29.5% 16.7%);
}
body {
background: var(--color-slate-800);
block-size: 100vh;
display: grid;
margin: 0;
place-items: center;
}
.grid {
display: grid;
gap: 8vmin;
place-items: center;
}
button {
appearance: none;
background: none;
border: none;
border-radius: 0.25rem;
cursor: pointer;
font-family: 'Silka', sans-serif;
margin: 0;
padding: 0;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
}
.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.
<!DOCTYPE html>
<html>
<head>
<title>ID Sandbox</title>
<meta charset="UTF-8" />
<link rel="stylesheet" href="/base.css" />
<link rel="stylesheet" href="/styles.css" />
<link rel="stylesheet" href="/colors.css" />
</head>
<body>
<div class="grid">
<button class="button">
<span>Explore</span>
<svg class="button__icon" xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 256 256"><path d="M152,224a8,8,0,0,1-8,8H112a8,8,0,0,1,0-16h32A8,8,0,0,1,152,224ZM128,112a12,12,0,1,0-12-12A12,12,0,0,0,128,112Zm95.62,43.83-12.36,55.63a16,16,0,0,1-25.51,9.11L158.51,200h-61L70.25,220.57a16,16,0,0,1-25.51-9.11L32.38,155.83a16.09,16.09,0,0,1,3.32-13.71l28.56-34.26a123.07,123.07,0,0,1,8.57-36.67c12.9-32.34,36-52.63,45.37-59.85a16,16,0,0,1,19.6,0c9.34,7.22,32.47,27.51,45.37,59.85a123.07,123.07,0,0,1,8.57,36.67l28.56,34.26A16.09,16.09,0,0,1,223.62,155.83ZM99.43,184h57.14c21.12-37.54,25.07-73.48,11.74-106.88C156.55,47.64,134.49,29,128,24c-6.51,5-28.57,23.64-40.33,53.12C74.36,110.52,78.31,146.46,99.43,184Zm-15,5.85Q68.28,160.5,64.83,132.16L48,152.36,60.36,208l.18-.13ZM208,152.36l-16.83-20.2q-3.42,28.28-19.56,57.69l23.85,18,.18.13Z"></path></svg>
</button>
</div>
</body>
</html>
:root {
--color-slate-800: hsl(227.1 29.5% 16.7%);
}
body {
background: var(--color-slate-800);
block-size: 100vh;
display: grid;
margin: 0;
place-items: center;
}
.grid {
display: grid;
gap: 8vmin;
place-items: center;
}
button {
appearance: none;
background: none;
border: none;
border-radius: 0.25rem;
cursor: pointer;
font-family: 'Silka', sans-serif;
margin: 0;
padding: 0;
display: grid;
font-weight: 700;
gap: 0.5rem;
grid-auto-flow: column;
letter-spacing: 0.1em;
place-items: center;
text-transform: uppercase;
}
.button__icon {
aspect-ratio: 1;
block-size: 1.25em;
}
: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.