Skip to main content

Colour Converter and Contrast Checker

Convert between HEX, RGB, HSL and OKLCH, and check any pair against WCAG AA and AAA. Shows the contrast ratio as you adjust, not after.

Your data never leaves the browserUpdated: July 2026

Format converter

HEX#4F46E5
RGBrgb(79, 70, 229)
HSLhsl(243, 75%, 59%)
OKLCHoklch(0.5106 0.2301 276.97)

Contrast check

Text size

Large text means 18pt and up, or 14pt bold. The threshold is lower there because the heavier stroke carries part of the signal.

This sentence is shown in the two colours you picked.

Contrast ratio6.29:1AA pass

The level usually required. Reaching AAA needs a higher ratio still.

Key Takeaways

  • WCAG asks for 4.5:1 on normal text and 3:1 on large text, where large means 18pt, or 14pt when bold.
  • Two colours with the same HSL lightness can differ enormously in perceived brightness; in OKLCH they do not.
  • Contrast is computed from relative luminance, not from how different two colours look. Vivid red on vivid green scores 1.29:1.
  • A translucent colour has no luminance of its own, so its contrast is undefined until it is composited over a known backdrop.

The brand colour that passed review and failed the audit

The brand colour was chosen, it looked excellent in the deck, and everyone signed off. Months later an accessibility audit measured it against white and returned 2.8:1. Button labels, links, helper text, all failing. The colour was not bad. Nobody had measured it.

A colour looking "dark enough" on screen is not a measurement. Contrast is a ratio between quantities of light, not a judgement of how different two colours appear, and human estimation of that ratio is unreliable in a specific and predictable way.

What each format is good for

All four notations describe the same colour. What separates them is which operation they make easy, so the choice depends on the job rather than on the colour.

FormatExampleReach for it when
HEX#4F46E5Design handoff and token files, where compactness and uniformity matter
RGBrgb(79, 70, 229)Canvas and image work, where you do arithmetic on individual channels
HSLhsl(243, 75%, 59%)Quick manual tweaks: hold the hue and nudge the lightness
OKLCHoklch(0.5106 0.2301 276.97)Building perceptually even palettes and adjusting for contrast
One colour, four notations. The right one depends on the operation, not the colour.

This converter reads and writes all four. On input it accepts both the modern space-separated CSS Color 4 syntax and the legacy comma syntax, because design tools emit the former while your existing stylesheets are still full of the latter.

Short hex hides a common bug worth knowing about. Expanding #f00 duplicates each nibble, so "f" becomes "ff". An implementation that pads with zeros instead produces "f0" and darkens every short hex it touches. On a single swatch nobody notices; across a palette the drift accumulates into colours that no longer match the design.

Out-of-range input is reported rather than clamped. Writing rgb(300, 0, 0) and getting red back would hide a typo that almost certainly came from a calculation upstream, so it returns an error naming the offending channel instead. Hue is the exception, because it is an angle rather than a bounded channel: hsl(400 ...) is legal CSS and simply wraps to 40 degrees.

Why OKLCH is worth the switch

The "lightness" in HSL is not a measure of perception. It is the midpoint between the largest and smallest of the red, green and blue channels, which is a statement about numbers rather than about light. The consequence is easy to demonstrate: hsl(60 100% 50%) and hsl(240 100% 50%) share a lightness of 50%, yet the yellow is dazzling and the blue is nearly dark. Convert both to OKLCH and the lightness values come out at 0.97 and 0.45. The eye is far more sensitive to yellow and green than to blue, and HSL does not model that at all.

The cost shows up most clearly when building a palette. The standard recipe is to pick a colour, hold lightness and saturation fixed, and rotate the hue in even steps. The output is never even. Yellows and cyans jump forward, blues and violets recede, and you end up hand-correcting each swatch until the set looks balanced, which quietly discards the systematic approach you started with.

css
/* HSL: lightness is constant, perceived brightness is not */.a { background: hsl(0   80% 55%); }   /* red, mid brightness */.b { background: hsl(60  80% 55%); }   /* yellow, visibly much brighter */.c { background: hsl(240 80% 55%); }   /* blue, visibly much darker */ /* OKLCH: L tracks perception, so the same rotation gives an even palette */.a { background: oklch(0.65 0.18 25); }.b { background: oklch(0.65 0.18 85); }.c { background: oklch(0.65 0.18 265); } /* The same property makes theme ramps predictable:   subtracting 0.08 from L gives one step darker without shifting hue. */

Because the L channel in OKLCH corresponds to perceived lightness, two colours sharing an L value genuinely look equally bright, and rotating hue at fixed L produces a palette that is even without hand-correction. The same property makes darkening safe: lower L and the hue stays put. That is exactly how the accessible-colour suggestion here works, binary-searching on OKLCH lightness alone while carrying chroma and hue through untouched, so the suggestion still reads as your colour rather than a different one.

WCAG contrast and what 4.5:1 means

The contrast ratio is a number between 1 and 21. Both colours are converted to relative luminance, 0.05 is added to each, and the larger is divided by the smaller. Those offsets model ambient light reflecting off the screen, and they are what fixes the ceiling at exactly 21, since (1 + 0.05) divided by (0 + 0.05) is 21.

Normal text needs 4.5:1 for AA and 7:1 for AAA. Large text gets a lower bar, 3:1 for AA and 4.5:1 for AAA, where large means 18pt and up, or 14pt and up when bold. The reason is stroke width: a thicker letterform stays legible at a ratio that would break a thin one. The thresholds are stated as minimums, so a pair landing on exactly 4.5 passes AA.

Warning

Two colours looking nothing alike does not make them contrasty. A vivid #FF0000 on a vivid #00AA00 scores 1.29:1 and is unreadable as text, because their relative luminances are close even though their hues are opposite. Contrast measures light, not difference, which is also why red text on white manages only 4.00:1 and still fails AA for body copy.

Why alpha makes contrast unmeasurable

rgba(0, 0, 0, 0.6) looks like a colour but has no luminance on its own. What reaches the eye is that colour blended with whatever sits behind it: a light grey over white, very nearly black over dark grey. One declaration, many actual colours.

For that reason the engine ignores the alpha channel when computing contrast. Guessing the backdrop would produce a confidently wrong number, which is worse than no number. To measure a translucent layer, composite it first and enter the resulting opaque colour. The blend is per channel: result = foreground alpha + background (1 - alpha).

The same ambiguity applies to gradients and to text over photography. If the backdrop is not a single colour, there is no single contrast ratio to report. The workable approach is to find the lightest and the darkest point the text can land on, measure both, and hold yourself to the worse of the two.

Frequently Asked Questions

What is a good contrast ratio?
At least 4.5:1 for normal text, and 7:1 if you are targeting AAA. Large text (18pt, or 14pt bold) drops to 3:1. For body copy it is worth clearing 4.5 with some margin rather than landing on it, so later colour tweaks do not silently push you under.
Should I use HSL or OKLCH?
For nudging a single colour by hand, HSL is fine and already familiar. For generating a set of colours that need to feel balanced, or for darkening a colour without shifting its hue, OKLCH is the right tool because its lightness channel corresponds to perception.
Does opacity affect contrast?
It does, but it cannot be computed until you composite. A translucent colour has no luminance by itself, so this tool leaves alpha out of the contrast calculation. Blend it against its actual backdrop and enter the resulting opaque colour for a correct figure.
Why does my colour look different in dark mode?
Perception depends on surroundings: the same swatch looks brighter and more saturated against a dark background. The contrast ratio also changes, because the other colour in the pair is now a different colour. Dark themes usually need slightly lower chroma and slightly higher lightness to feel equivalent.
Are HEX and RGB the same thing?
They are two spellings of the same numbers. #4F46E5 and rgb(79, 70, 229) describe an identical colour, one in hexadecimal and one in decimal. The eight-digit HEX form carries alpha in a final byte, while rgba() expresses alpha as a value between 0 and 1.