Bhinish Dhiman· updated 5 October 2026

How to make a custom Tailwind colour scale that passes contrast

Set a base colour and a name, check the light and dark previews, and copy the Tailwind v4 @theme block or the v3 config. On the default #0066ff scale, steps 600 to 950 carry body text on white (600 at 5.68:1) and 50 to 500 on black, while 500 reaches only 3.96:1 on white. The usual failure is the 300 input border: 1.86:1 on white, where 3:1 is needed.

Adding a brand colour to Tailwind is quick work. Knowing which of its eleven steps you can set text in is the part that usually gets skipped, and it is why a vivid 500 so often ends up as body copy. The Tailwind colours tool builds the scale, previews it on the components people actually ship, and puts a measured ratio under each one.

A Tailwind colour scale is an eleven-step scale registered under one name, so that utilities such as bg-brand-500 and text-brand-700 exist. In Tailwind v4 it is declared in CSS, inside an @theme block, rather than in a JavaScript config, and v4’s own default palette is written in OKLCH.

Making the scale

  1. Set the base colour. Type a hex code into “Base colour”. The tool opens on #0066ff.
  2. Set the name. Whatever you type in “Name” becomes the middle of every utility, so “brand” gives bg-brand-500.
  3. Read the scale. Steps 50 to 950, each with its hex; “yours” marks the step nearest your base colour.
  4. Flip between Light and Dark. Use the Light/Dark switch at the right of the controls, or the D key. The previews redraw on the other theme.
  5. Read the line under each preview. Each gives the measured ratio, what is being measured, and the threshold. A filled square means it passes; an empty one means it does not.
  6. Choose the output and copy it. Tailwind v4, Tailwind v3 or CSS vars, then Copy. Copy scale in the bottom bar (or the C key) does the same.
  7. Paste it. For v4, add the @theme block to the CSS file that imports Tailwind. For v3, put the named entry inside theme.extend.colors in your config, which keeps Tailwind’s default palette alongside it.

Use the output switcher, not the bottom-bar Export, whose Tailwind output numbers the steps 1 to 11 instead of 50 to 950.

What each output looks like

The v4 output writes every step as OKLCH, lightness to one decimal place, chroma to three and hue to one:

@theme {
  --color-brand-50: oklch(…);
  /* … nine more … */
  --color-brand-950: oklch(…);
}

The v3 output is a hex colors entry under a // tailwind.config.js comment. CSS vars gives --brand-50 to --brand-950 in a :root block, without the --color- prefix, so in v4 it defines variables but generates no utilities. Use it when the scale has to reach code outside Tailwind.

Which steps can carry text

These are the figures the tool shows for its default base, #0066ff, measured by the same code that draws the previews. Your hue will move them a little; the pattern holds.

Step Hex On white On black Body text (4.5:1) on
50 #f1f6ff 1.08:1 19.36:1 Black
100 #e0ebff 1.20:1 17.49:1 Black
200 #c1d8ff 1.44:1 14.53:1 Black
300 #99bfff 1.86:1 11.25:1 Black
400 #659dff 2.68:1 7.82:1 Black
500 #2d79ff 3.96:1 5.29:1 Black; large text only on white
600 #005ce8 5.68:1 3.69:1 White; large text only on black
700 #0048b9 7.99:1 2.62:1 White
800 #00358d 11.08:1 1.89:1 White
900 #012464 14.60:1 1.43:1 White
950 #011540 17.75:1 1.18:1 White

500 and 600 are the two steps that work in one direction only, and nothing on the scale carries body text on both grounds. That is why a vivid 500 set as body copy on a white page fails.

What the previews measure

Each preview tests a different pairing, and the dark theme does not simply invert the light one. Light previews sit on white panels over a step-50 page; dark previews sit on step-900 panels over step 950.

Preview line Light theme pair Dark theme pair Needs #0066ff, light / dark
primary label White or black, whichever is stronger, on 600 The same, on 500 4.5:1 5.68 / 5.29
secondary label 800 on 100 100 on 800 4.5:1 9.23 / 9.23
subtle badge, 11px 700 on 100 200 on 800 4.5:1 6.65 / 7.67
input border 300 on white 700 on 900 3:1 1.86 / 1.82
alert body text 700 on 50 300 on 900 4.5:1 7.37 / 7.82
table header 600 on 50 300 on 900 4.5:1 5.23 / 7.82
series on surface 600 on white 600 on 900 3:1 5.68 / 2.57

The border and the dark chart

Two lines in that table fail, and neither is a text pair.

Non-text contrast is the WCAG requirement, success criterion 1.4.11, that the parts of an interface needed to identify a control, such as an input’s border or a focus ring, reach 3:1 against what is next to them. The input preview draws its border in 300 on white: 1.86:1. Even 400 manages only 2.68:1. If the border is what shows a field is there, use 500 or darker on a white panel.

The dark theme repeats the problem in mirror image. A 700 border on a 900 panel is 1.82:1, and a chart series in 600 on that panel is 2.57:1, both short of 3:1. On a 900 panel, 500 is the first step that clears it, at 3.67:1.

So when you write components with the new scale, borders and chart colours deserve the same scrutiny as text, and in dark mode they want a lighter step than the light-theme habit suggests.

Doing it by hand

Tailwind does not generate a scale from one colour; you write the eleven lines yourself. That is easy to do and easy to get wrong in exactly the way the tables above show, so if you do it by hand, test the 300 border and the 600 label in the contrast checker before anything ships. The tool keeps the base and name after the # in its address, which a browser does not send to a server, so the colour you are working on stays on your machine.

Open Tailwind colours, set your base, and read the input border line before anything else. If you want role tokens such as a text colour and a border colour chosen by measurement, the ramp and token generator builds the same eleven steps and assigns them. For why OKLCH replaced HSL for this kind of work, see OKLCH for designers.