SCSS color variables are dead. Use design tokens instead.
We need to talk about your _variables.scss file. The one with 47 $color- variables, three that are unused, two that are duplicates with
different names, and one that was supposed to be temporary in 2019.
SCSS color variables were a legitimate solution in 2015. CSS didn't have variables. Sass
gave us $brand-primary and we felt like we were living in the future. We were.
But the future caught up. CSS has native variables now. They're better. And your SCSS
color variables are a build-step dependency that does less than what the browser gives you
for free.
Time to migrate.
What SCSS variables can't do
SCSS variables compile to static values. $color-accent: #E8C547 becomes color: #E8C547 in your output CSS. The variable is gone. It was a compile-time
convenience, nothing more.
That means:
- No cascade. A child component can't inherit a color from its parent through the DOM. SCSS variables exist in the Sass scope, not the browser scope. You have to import the same file in every partial that needs colors.
- No runtime changes. Dark mode with SCSS means compiling two separate stylesheets, or using CSS classes to swap hardcoded values. You can't just change a variable at runtime because the variable doesn't exist at runtime.
- Build step required. Every color change requires a recompile. Your dev server restarts. Your CI pipeline runs Sass. It's a dependency you don't need.
- Locked to one tool. SCSS variables only work in Sass. Can't use them in a Vue
<style>block without a preprocessor config. Can't use them in a Tailwind config. Can't use them in Figma. They're a walled garden.
CSS custom properties have none of these limitations. They cascade through the DOM. They change at runtime with a single JavaScript line. They need no build step. They work in every context that accepts CSS.
The side-by-side comparison
Here's the same color system in SCSS variables versus CSS custom properties.
SCSS
// _variables.scss
$color-background: #FAF7F2;
$color-ink: #1A1A1A;
$color-accent: #E8C547;
$color-support: #7A7A6E;
$color-neutral: #D4CFC6;
// _buttons.scss
@import 'variables';
.btn {
background: $color-accent;
color: $color-ink;
border: 2px solid $color-ink;
} CSS custom properties
/* tokens.css */
:root {
--color-background: #FAF7F2;
--color-ink: #1A1A1A;
--color-accent: #E8C547;
--color-support: #7A7A6E;
--color-neutral: #D4CFC6;
}
/* buttons.css */
.btn {
background: var(--color-accent);
color: var(--color-ink);
border: 2px solid var(--color-ink);
} Nearly identical syntax. But the CSS version cascades, changes at runtime, needs no import, needs no build step, and works in every framework without configuration.
Now add dark mode. In SCSS, you're writing a second set of static overrides or managing two compiled themes. In CSS custom properties:
@media (prefers-color-scheme: dark) {
:root {
--color-background: #1A1917;
--color-ink: #F0EDE8;
--color-accent: #F0D060;
--color-support: #9A9A8E;
--color-neutral: #2A2A28;
}
} Done. Every component adapts automatically. No second stylesheet. No class toggling. No build step. Four lines of code and dark mode works across your entire app.
The migration path
You don't have to rewrite everything at once. SCSS and CSS custom properties coexist perfectly. Here's the gradual approach.
Step 1: Identify your SCSS color variables
Open your _variables.scss and list every $color- variable. Most
projects have between 15 and 60. Many are unused. Some are duplicates. This is a good time
to audit.
Step 2: Map to semantic roles
Group your variables by purpose. Which ones are backgrounds? Which are text colors? Which are accent/interactive? Which are structural? You'll likely find that your 47 variables map to 5-8 roles, with everything else being tints, shades, or one-offs.
Step 3: Create your CSS custom properties
Define the CSS custom properties with
role-based names in a tokens.css file. Import it in your app entry point.
Step 4: Bridge the gap
In your SCSS, reassign the old variables to reference the new custom properties. This lets existing SCSS code work while new code uses custom properties directly.
// _variables.scss (bridge) // Old SCSS code still works via these $color-background: var(--color-background); $color-ink: var(--color-ink); $color-accent: var(--color-accent);
Fair warning: this only works for properties that accept var() — which is most
of them. It won't work in Sass functions like darken() or mix(). Those references need to be replaced with CSS-native alternatives or
pre-computed values.
Step 5: Migrate component by component
As you touch each component, replace $color-* with var(--color-*). No rush. Do it as part of normal development. When the last
SCSS variable reference is gone, delete the Sass dependency.
What about Sass functions?
The most common objection: "But I use darken() and lighten() and mix() on my color variables." Yes. Sass color functions are genuinely
useful. CSS has answers for most of them now.
- darken/lighten: Use
color-mix(in oklch, var(--color-accent) 80%, black)or pre-compute your tint/shade scale and use tokens like--color-accent-700. - mix:
color-mix(in srgb, var(--color-accent) 50%, var(--color-ink))is the native CSS equivalent. - rgba with variable: Use
oklch()or store separate channel values. But honestly, a pre-computed tint scale is cleaner than runtime manipulation.
Paletter exports a full tint/shade scale for each color role. Instead of darken($accent, 20%), use var(--color-accent-700). The result is
consistent and you don't need a preprocessor to compute it.
But honestly, just use CSS custom properties
It's 2026. CSS has nesting. CSS has :has(). CSS has container queries. CSS has color-mix(). The reasons to reach for Sass get thinner every year. Color
variables were one of the last good reasons. That reason is gone.
Paletter generates both formats — SCSS variables and CSS custom properties — from the same palette. So you can export SCSS for your legacy code and CSS custom properties for new work. Migrate at your own pace. But migrate.
Your _variables.scss had a good run. Let it rest.
Export both. Migrate when you're ready.
Generate a palette with role-based naming. Export as CSS custom properties for new code and SCSS variables for legacy support. Same palette, same roles, both formats.
Generate your palette