Skip to content
← Back to blog
Tailwind

Tailwind custom colors: extend your theme without fighting the defaults

Tailwind ships with a great default palette. Your brand does not. Here is how to add custom colors so utilities like bg-brand-600 feel as natural as bg-slate-200.

The first time you paste a one-off hex into className="text-[#7c3aed]", it works. The tenth time, you have invented a parallel design system nobody can search. Tailwind’s fix is theme.extend.colors: name your ramp once, use predictable utilities everywhere.

Extend, do not replace (usually)

Putting colors under extend keeps Tailwind’s slate, red, and amber scales available for alerts and neutrals while your brand lives beside them. Replacing the entire color object is valid for greenfield apps, but most teams only need a brand key and maybe surface neutrals tuned to their marketing site.

// tailwind.config.js (excerpt)
export default {
  theme: {
    extend: {
      colors: {
        brand: {
          50: '#f5f3ff',
          500: '#7c3aed',
          900: '#4c1d95',
        },
      },
    },
  },
};

After that, bg-brand-500, text-brand-900, and ring-brand-500/40 behave like first-class citizens — including opacity modifiers Tailwind generates from modern color syntax.

Numeric ramps beat one-off swatches

Designers and engineers negotiate faster when both sides say “brand-700” instead of “the darker purple, not that dark.” A 50–950 ladder does not have to mirror Tailwind’s defaults exactly; it has to be internally consistent. Generate the ladder from one anchor in the Tailwind config generator, optionally using OKLCH-based steps that compress chroma when sRGB cannot keep up, then paste the object into your config.

Match CSS variables

If you also ship plain CSS tokens, generate the same ramp in the CSS variables tool so web and Tailwind never drift to different hex values after a rebrand.

Semantic aliases on top of ramps

Some teams map meaning in config: primary: colors.brand[600] via a small JS helper, or use CSS variables in tailwind.config with var(--color-primary) for runtime theming. Either approach beats sprinkling raw hex in components. Dark mode often means swapping variable values under .dark rather than duplicating every utility.

When arbitrary values are still OK

One-off marketing gradients or experimental charts can use arbitrary values. If a color appears twice in production, promote it to the theme. Your future self reviewing a pull request will thank you.

Reference palettes like Tailwind’s published scales are useful spacing benchmarks even when your hues are unrelated — they show how many steps you need between “subtle border” and “solid button.”