ToyTools Guide
How to Build a 50 to 950 OKLCH Color Scale
How a tint and shade scale holds hue in OKLCH, why chroma drops at the ends, and when the brand stop fails on white.
How do you build a color scale from one hex?
Paste one color. The page returns eleven stops, numbered 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, and 950. Hue stays the hue you typed. Lightness walks from a near-white tint down to a near-black shade. Chroma, the amount of color, is kept when the screen can show it and reduced when it cannot. Under the row is a :root block of CSS variables, from --shade-50 to --shade-950.
That block is the thing you came for. Copy CSS takes all of it. Tap a single swatch to copy only that hex. #3b82f6, rgb(59, 130, 246), hsl(217, 91%, 60%), and a name like teal all count as input. To start from a photo, take a color from the image palette extractor and paste it here. The arithmetic stays in the browser. Nothing is uploaded.
Open the Color Shades Generator →How do tints and shades split the ramp?
A tint moves a color toward white. A shade moves it toward black. On this ramp the light stops are the tints and the dark stops are the shades. The stop whose lightness is closest to the color you typed holds your exact hex, so a mid blue lands on 500 and a pale yellow lands nearer 100 or 50. The other stops are lightness targets in the same hue.
A 50 to 950 color palette contains eleven stops, named the way a Tailwind color scale names them, and tints versus shades is simply the light half against the dark half. The stops use those names because stylesheets already do. A token called --shade-700 reads as the dark step of the same family. You still have to look at the hex.
For example, try a familiar blue. Input: #3b82f6. Output: it lands on stop 500, and on white it is 3.68:1, under the 4.5 that body text needs. Stop 600, #1961d2, reaches 5.70:1, so it is the first stop in this ramp that can carry body text on white.
The row is a grid that fills the width you have. On a phone the stops wrap. On a wide window they share one line of swatches instead of leaving a blank band beside three tiles. Each swatch is a button. The pressed state is the feedback, because a phone has no hover.
A useful check is to type the same hue twice, once dark and once light. The dark one should put the brand stop lower on the ramp, closer to 800 or 900. The light one should put it up near 50 or 100. If both land on 500, the lightness walk is ignoring the color you typed. Here the nearest stop moves with the source, and the CSS names stay put so a stylesheet can keep referring to --shade-500 while you try another brand.
Why build the ramp in OKLCH?
HSL calls yellow and blue the same lightness when one looks much lighter than the other. A ramp that steps HSL lightness by equal numbers goes dull in the middle and jumps at the ends. OKLCH, from Björn Ottosson's OKLab, steps lightness in a space where those steps look closer to even, which is why OKLCH ramps stay more even than HSL ramps of the same step size. Hue is an angle. Chroma is a distance from gray. This OKLCH palette generator holds the angle and walks the lightness, so the whole shade scale keeps one hue.
Chroma is the part that breaks first. A lightness of 0.97 cannot carry the chroma of a strong blue and still land inside sRGB. The hex would have to clip a channel to 0 or 255, and the hue would drift. The page searches, by halves, for the largest chroma at that lightness that still fits. The stop gets duller. It does not change family. A gray input has chroma zero, so every stop stays gray and only lightness moves.
This is a color scale generator, not a clone of a published palette. Tailwind's stop names are the vocabulary. Tailwind's hex values are a hand-tuned set that changes between versions. Deriving eleven stops from your one color will not reproduce blue-500 from a docs page, and it should not. If you need the exact hex from a file, copy it from the file.
When does the brand stop fail on white?
The stop nearest your color is checked as text on white. WCAG asks for 4.5:1 for body text. If the ratio is under that, a line under the row names the stop and the ratio. If the stop already clears 4.5, the line stays hidden. #3b82f6 is a useful demo: the stop near it fails, so the note appears. A deep navy such as #1e3a8a usually passes, and the note does not.
The failure mode is a swatch that looks fine in a row of its siblings. The row is not white body copy. Shipping --shade-500 as a text color because the tile was pretty is how a brand color becomes unreadable. The note is one sentence, and it is absent when there is nothing to warn about. It does not score stop 50, and it does not score colored backgrounds.
For any other pair, open the contrast checker and type the two hex values. For the same color in HEX, RGB, HSL, and OKLCH at once, open the color format converter. This page builds the ramp. Those pages answer a different question about one color.
Common mistakes
What should you open next?
Paste the variables, then check the pair you will actually ship in the contrast checker. If a teammate hands you hsl() and you want the hex before you build a ramp, start in the color format converter and come back with one color.
Rename --shade-500 if the project already uses that name. The later rule in the cascade wins, and a quiet overwrite is worse than a longer name. The ramp is a starting set of tokens, computed on your device, from the single color you typed.
Build a scale from #3b82f6 →Related Tools
You May Also Need
You may also need
- WCAG Color Contrast CheckerScore a chosen foreground and background pair
Next steps
- Color Format ConverterRead the same color as HEX, RGB, HSL, or OKLCH
- Type Scale GeneratorSet the font size ramp next to the color tokens