Flexibility with CSS Variables & Custom Properties

You’ve probably written the same hex code like 37 times in one CSS file. Maybe you've even copied and pasted the same padding value across multiple components. And then the day comes when your designer says, “Can we bump the primary color just a little?” Suddenly, your entire stylesheet becomes a minefield. I’ve been there—searching, replacing, breaking stuff. That's when I discovered CSS variables (a.k.a. custom properties), and it changed everything. They're powerful, they're flexible, and they make your CSS sane again.

What Are CSS Variables and Why Should You Care?

Let’s break it down. CSS variables let you define reusable values that can be updated in one place. Think of them like JavaScript consts, but for styling.

Basic syntax:

css
1
2
3
4
          :root {
  --primary-color: #10b981;
  --font-size-base: 16px;
}
        

Then you use them like this:

css
1
2
3
4
          body {
  color: var(--primary-color);
  font-size: var(--font-size-base);
}
        

Why :root? It’s the highest-level selector, so your variables are available globally. No more repeating values. No more brittle updates.

And they’re dynamic, unlike Sass variables. You can change them with JavaScript or within media queries. That’s the superpower.

Creating Themes with CSS Variables

Let’s say we want to support light and dark themes. You’d do something like this:

css
1
2
3
4
5
6
7
8
9
          :root {
  --bg-color: #ffffff;
  --text-color: #111827;
}

[data-theme="dark"] {
  --bg-color: #111827;
  --text-color: #ffffff;
}
        

Now your components just use the variables:

css
1
2
3
4
          body {
  background: var(--bg-color);
  color: var(--text-color);
}
        

You can switch themes by toggling the data-theme attribute on <html> or <body>:

javascript
1
          document.documentElement.setAttribute('data-theme', 'dark');
        

Instant theming, no chaos. I once helped a team refactor a 3000-line CSS file into a design system using variables—took some work, but it turned future redesigns from a nightmare into a weekend task.

Future-Proof Your Styles with Custom Properties

Besides themes, you can use variables for layout spacing, font sizing, even shadows.

css
1
2
3
4
5
6
7
8
9
          :root {
  --spacing-md: 1rem;
  --card-shadow: 0 2px 8px rgba(0,0,0,0.05);
}

.card {
  padding: var(--spacing-md);
  box-shadow: var(--card-shadow);
}
        

Then if your spacing scale changes? One update. Done.

You can even use calculations:

css
1
2
3
4
          :root {
  --base-size: 16px;
  --large-size: calc(var(--base-size) * 1.5);
}
        

This level of control means you can scale your project without spaghetti styles.

For our course project

Let’s refactor our CSS and add some variables at the top of our main stylesheet:

css
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
          :root {
  --primary-color: #10b981;
  --primary-hover: #059669;
  --card-radius: 10px;
  --card-shadow: 0 2px 8px rgba(0,0,0,0.05);
  --transition-fast: 0.3s ease;
}

.project-card {
  border-radius: var(--card-radius);
  box-shadow: var(--card-shadow);
  transition: box-shadow var(--transition-fast);
}

.project-button {
  background: var(--primary-color);
  transition: background var(--transition-fast);
}

.project-button:hover {
  background: var(--primary-hover);
}
        

Boom. Now if we want to update the button color, it’s one line in :root. That’s clean, scalable CSS.

What’s Next?

Variables make your CSS smarter. But sometimes, things still go sideways—layouts break, weird gaps appear, and colors vanish into the void. Up next, we’re getting our hands dirty with Debugging CSS Like a Pro. Time to hunt bugs and fix what’s broken.