# Tailwind colours: using and extending the palette

[Canonical HTML page](https://gradiently.design/guide/tailwind-colors)

The default palette is a good starting point and a poor finishing one, because thousands of sites share it. Here is how Tailwind colours work in v4, and how to add your own without breaking anything.

## The short version

- Tailwind colors are named families such as blue, violet and slate, each with shades from 50 for the lightest to 950 for the darkest.
- In Tailwind v4 every colour is a CSS variable such as `--color-blue-500`, defined in OKLCH, and you add or replace colours inside an `@theme` block in your CSS.
- Any variable you name `--color-something` in `@theme` becomes a full set of utilities, including `bg-something`, `text-something` and `border-something`.
- The opacity modifier, as in `bg-sky-500/50`, works with custom colours too, because v4 mixes the transparency in with `color-mix()`.
- Contrast depends on the pair, not the shade number, so check text and background combinations rather than assuming 500 on white is readable.

**Tailwind colors** are a set of named families, from red to rose plus a set of greys such as slate, gray, zinc, neutral and stone, each with eleven shades numbered 50, 100 to 900 in hundreds, and 950. You apply them with utilities such as `bg-blue-600`, `text-slate-900` and `border-rose-300`. In Tailwind v4 the whole palette is defined in OKLCH as CSS variables, which you extend or replace in an `@theme` block rather than a JavaScript config file.

## How the default palette is organised

Every family follows the same ladder. 50 to 200 are tints for backgrounds and hover fills, 300 and 400 are soft accents, 500 and 600 are the saturated middle, and 700 to 950 are for text, borders and dark surfaces. Because each family uses the same steps, swapping `blue` for `violet` in a component usually just works.

- blue-100: #dbeafe
- blue-200: #bfdbfe
- blue-300: #93c5fd
- blue-400: #60a5fa
- blue-500: #3b82f6
- blue-600: #2563eb
- blue-700: #1d4ed8
- blue-800: #1e40af
- blue-900: #1e3a8a
- blue-950: #172554

The blue family as sRGB hex, from the v3 palette. The v4 values are written in OKLCH and are slightly more vivid on wide gamut screens, but the ladder is the same.

| Group | Families | Typical use |
| --- | --- | --- |
| Greys | slate, gray, zinc, neutral, stone | Text, borders, surfaces |
| Warm | red, orange, amber, yellow | Alerts, warnings, energy |
| Greens | lime, green, emerald, teal | Success, nature, calm |
| Blues | cyan, sky, blue, indigo | Links, primary actions |
| Purples and pinks | violet, purple, fuchsia, pink, rose | Accents, creative brands |

The default families grouped by temperature. Slate and zinc lean cool, stone leans warm, neutral has no tint at all.

Pick one grey and stick to it. Mixing `gray-500` text with `slate-200` borders gives a faintly dirty look that is hard to name and easy to avoid.

## Using Tailwind colours in your markup

Colour utilities exist for almost every property that takes a colour: `bg-*`, `text-*`, `border-*`, `ring-*`, `fill-*`, `stroke-*`, `decoration-*`, `shadow-*`, `accent-*`, `caret-*` and the gradient stops `from-*`, `via-*` and `to-*`. Add a slash and a number to set opacity.

```html
<div class="rounded-xl bg-slate-900 p-6 text-slate-100">
  <p class="text-sm text-slate-400">Status</p>
  <p class="text-2xl font-semibold">All systems normal</p>
  <span class="mt-4 inline-flex rounded-full bg-emerald-500/15 px-3 py-1 text-emerald-300">
    Live
  </span>
</div>

<!-- One off values when nothing in the palette fits -->
<button class="bg-[#0b5c4a] text-[oklch(97%_0.02_160)]">Book a table</button>
```

A dark card from one grey family plus one accent at 15% opacity. Arbitrary values in square brackets cover the rare exception.

In v4 every shade is also a variable you can use in plain CSS: `color: var(--color-slate-400)`. That makes it easy to style third party widgets, SVG or a canvas with the same colours as your utilities.

## Adding custom colours in Tailwind v4

In v4 you configure the theme in CSS. Any variable that starts with `--color-` inside `@theme` becomes a colour, with every utility generated for it. Name colours by role or by brand, and give them a full ladder if you will need tints.

```css
@import "tailwindcss";

@theme {
  /* A brand ramp on one hue, written in OKLCH */
  --color-ember-50: oklch(97% 0.02 45);
  --color-ember-100: oklch(94% 0.04 45);
  --color-ember-200: oklch(88% 0.07 45);
  --color-ember-300: oklch(80% 0.11 45);
  --color-ember-400: oklch(71% 0.15 45);
  --color-ember-500: oklch(63% 0.17 45);
  --color-ember-600: oklch(55% 0.16 45);
  --color-ember-700: oklch(47% 0.14 45);
  --color-ember-800: oklch(39% 0.11 45);
  --color-ember-900: oklch(31% 0.08 45);
  --color-ember-950: oklch(22% 0.05 45);

  /* Single role colours */
  --color-ink: oklch(18% 0.02 270);
  --color-paper: oklch(98% 0.01 85);
}
```

Now `bg-ember-500`, `text-ink`, `border-ember-200/60` and `from-ember-400` all exist.

- ember-50: #fff1ea
- ember-200: #ffcab2
- ember-300: #f9a782
- ember-400: #ec7f4b
- ember-500: #d95f19
- ember-600: #b94a00
- ember-700: #973900
- ember-900: #502007
- ember-950: #2d1205

The ember ramp above, converted to hex. Holding the hue at 45 and stepping lightness keeps every shade recognisably the same colour.

Building the ramp in OKLCH is the reason this works. Lightness in OKLCH matches how bright a colour looks, so equal steps look equal, which HSL cannot promise. [OKLCH explained](https://gradiently.design/guide/oklch-explained) goes deeper, and [oklch.com](https://oklch.com) is a good place to try values.

### Replacing the palette entirely

To remove the defaults and keep only your own colours, reset the namespace first with `--color-*: initial;` at the top of `@theme`. Utilities like `bg-blue-500` will then stop existing, which is a useful guard on a project with a strict brand.

## Custom colours in Tailwind v3

Older projects still configure colours in JavaScript. Use `extend` to add colours beside the defaults, or set `colors` directly to replace them.

```js
// tailwind.config.js (v3)
module.exports = {
  theme: {
    extend: {
      colors: {
        ember: {
          50: '#fff1ea',
          500: '#d95f19',
          900: '#502007',
        },
        ink: '#14121f',
      },
    },
  },
}
```

The v3 way. When you upgrade, these move into `@theme` as `--color-ember-500` and so on.

## Contrast: which shades are safe for text

Shade numbers are not a contrast scale. White text on `blue-600` passes the 4.5:1 that WCAG asks for body text, while white on `blue-500` does not. Check the actual pair, using the ratios below or a tool from [WebAIM](https://webaim.org/resources/contrastchecker/), and read [colour contrast and accessibility](https://gradiently.design/guide/color-contrast-accessibility) for the rules.

| Text on background | Contrast | Body text |
| --- | --- | --- |
| slate-900 on white | 17.85:1 | Passes |
| gray-500 on white | 4.83:1 | Passes, just |
| gray-400 on white | 2.54:1 | Fails |
| white on blue-600 | 5.17:1 | Passes |
| white on blue-500 | 3.68:1 | Large text only |
| slate-400 on slate-900 | 6.96:1 | Passes |

Ratios calculated from the v3 hex values. Rerun them for your exact v4 colours, which differ slightly.

## Make the palette yours

The default palette is used on a vast number of sites, which is why so many products share the same `indigo-600` button and `slate-50` background. That sameness is the web version of [why every feed looks the same](https://gradiently.design/guide/why-every-feed-looks-the-same).

### Looks like every other site

- Default indigo or blue as the brand colour
- Several grey families mixed together
- Accent colour used on everything
- Light theme colours reused in dark mode

### Looks like you

- One custom ramp on your own hue
- One grey, tinted towards the brand hue
- Accent kept for actions and one highlight
- A dark set chosen separately, see [dark mode gradients](https://gradiently.design/guide/dark-mode-gradients)

1. **Choose a hue** Start from your brand colour and read its hue in OKLCH. [Website colour schemes](https://gradiently.design/guide/website-color-scheme) helps if you have none yet.
2. **Build the ramp** Hold the hue, step lightness from about 97% to 22%, and lower chroma at both ends.
3. **Name roles too** Add `--color-ink`, `--color-paper` and similar, so components say what a colour is for.
4. **Keep one source** Treat the `@theme` block as your [design tokens](https://gradiently.design/guide/design-tokens), and reuse the same values in your graphics.

The last step is where brands usually drift: the site gets the new ramp while social posts and slides keep last year's colours. A Gradiently brand kit stores your palettes alongside your fonts and logos, so the Designer lays out posts in the same colours your Tailwind theme uses, and a Mark gives the brand a gradient look no other site shares.

## FAQ

### What colors are in Tailwind by default?

Families from red, orange, amber and yellow through greens, blues, purples and pinks to rose, plus greys including slate, gray, zinc, neutral and stone. Each has shades 50, 100 to 900 and 950, plus black and white.

### How do I add custom colors in Tailwind v4?

Define variables such as `--color-brand-500` inside an `@theme` block in your CSS. Tailwind generates `bg-brand-500`, `text-brand-500` and every other colour utility from it.

### Does Tailwind use OKLCH?

Yes. Since v4 the default palette is defined in OKLCH, and you can write your own colours in OKLCH, hex, RGB or any other CSS colour format.

### How do I use a Tailwind color in plain CSS?

In v4 use the variable directly, for example `var(--color-slate-400)`. In v3 use the `theme()` function, such as `theme(colors.slate.400)`.

### Which Tailwind shades are accessible for text?

It depends on the pair. On white, gray-500 and darker pass for body text, and white text passes on blue-600 but not on blue-500, so always check the specific combination.
