A palette is only as maintainable as the way it is wired into code. This guide covers three durable patterns — CSS custom properties, Sass variables and a Tailwind theme — all fed from the same generated palette, so the design and the build cannot drift apart.
Key takeaways
- Declare raw colors once, then add a semantic layer that components reference.
- Custom properties are live values, which is what makes runtime theming possible.
- Sass variables are compile-time and best for deriving tints and shades during a build.
- Whatever the toolchain, one canonical list of hex codes feeds all of it.
CSS custom properties
Declare the palette once at the root, then reference it everywhere. The generator's export button produces this block directly:
:root {
--plum: #8B2D55;
--blush: #FFA8C2;
--ink: #28151F;
--cream: #FFF9FB;
--deep: #6E2143;
}
Add a semantic layer on top — --color-primary: var(--plum) — so components reference roles rather than raw colors. A rebrand then changes one token instead of a thousand rules, and nobody has to decide whether a given --plum meant "brand" or "this particular pink".
Sass variables
If your project uses Sass, define the palette as variables and lean on Sass color functions to derive tints and shades at build time:
$plum: #8B2D55;
$blush: #FFA8C2;
$ink: #28151F;
.button { background: $plum; }
.button:hover { background: darken($plum, 8%); }
Because Sass resolves at compile time, these values cannot respond to a runtime theme switch — which is exactly why many projects use Sass for derivation and custom properties for delivery.
Tailwind theme
In Tailwind, extend the theme's color object so your palette becomes utility classes such as bg-brand-plum:
colors: {
brand: {
plum: '#8B2D55',
blush: '#FFA8C2',
ink: '#28151F',
cream: '#FFF9FB',
}
}
For themable projects, point those entries at custom properties instead of literal hex values. You then get Tailwind's utilities and runtime theme switching at the same time.
Keep contrast in the tokens
Whichever system you use, pair each background role with a verified text color — an --color-on-primary beside --color-primary, checked once in the contrast checker. Components then never have to guess which text color is safe, and the accessible choice becomes the easiest one to make. The article on CSS custom properties for palettes goes deeper on the two-layer token pattern.
One source of truth
All three approaches should trace back to a single canonical list of hex codes from the generator, stored in the repository rather than in someone's design file. When the palette changes, it changes in one place and flows outward. That discipline is what keeps a design and its build in agreement over years rather than weeks.
Automate the export
The weakest link between a palette and a codebase is the human retyping hex values. Remove them. Keep the canonical palette as a small JSON or plain list in the repository, and generate the CSS custom properties, the Sass map and the Tailwind config from that one file during the build. Even a twenty-line script pays for itself the first time a color changes, because the change happens once and reaches every toolchain without anyone remembering to update the third one. It also makes the palette reviewable in a pull request, which is where color changes belong.
Frequently asked questions
Should Tailwind colors be hex or custom properties?
Hex for simple projects, custom properties when you need theming. The utility class names stay identical either way, so the migration is painless.
How do I name color tokens?
Two layers: raw names such as plum-500, and role names such as color-primary. Components use role names exclusively.
Where should the token file live?
In version control, next to the code. A palette that lives only in a design tool is a palette developers will eventually recreate by hand.
To decide what belongs in the palette before you wire it up, see the guide to using a palette generator for branding.