Shorthand doubles, it does not pad
Three-digit hex is shorthand for six, and the expansion is by duplication: each digit is repeated, so #f0a becomes #ff00aa.
The plausible wrong answer is zero-padding, which would give #f00a00 — a dark red instead of a bright pink, and a completely different colour. It is an easy thing to implement wrongly and an easy thing to assume wrongly when reading a stylesheet quickly.
The reason for duplication rather than padding is that it keeps the shorthand spread across the full channel range. #fff has to be white, so f must become ff rather than f0; and #000 has to be black. Duplication is the only expansion that puts the endpoints where they belong.
This tool expands shorthand and says so, rather than doing it silently — because when a colour looks wrong on screen, knowing which expansion was applied is the first thing worth checking.
One hex digit, two sides of AA
Most people looking up a hex code are about to put text on it or next to it, so the conversion is rarely the real question — readability is. WCAG sets that as a contrast ratio: 4.5:1 for body text, and 3:1 for large text, meaning 18pt or 14pt bold.
The threshold is sharper than it looks. On a white background, #767676 passes at 4.54:1 and #777777 fails at 4.48:1. One hex digit — a change invisible on screen — moves a colour from compliant to non-compliant.
That sharpness is why guessing is a poor strategy. A grey that looks “dark enough” in a mockup can sit either side of the line, and the only way to know is to compute it. The tool does that against both white and black, since a colour that fails on one often passes comfortably on the other.
Two things the ratio does not cover, and both matter. It is a floor rather than a target: a thin 300-weight font at 14px passing at exactly 4.5:1 is still hard to read, and the standard does not model weight at all. And it treats every colour pair alike, so it does not flag combinations that are specifically difficult for colour-blind readers — red on green can pass and still be unusable for roughly one man in twelve.
Why RGB maths is not brightness maths
#808080 is RGB 128, the exact midpoint of the 0–255 byte range. Its relative luminance is about 0.216 — nowhere near half. Mid-grey is much darker than halfway between black and white to the eye, and to any correct measurement.
The reason is the sRGB transfer curve. Channel values are not linear in light output: they are encoded with a gamma curve that allocates more precision to dark tones, because human vision is more sensitive there. Undoing that curve is the first step in the luminance formula, and it is why the formula looks so unlike an average.
The channels are also weighted very unequally — roughly 21% red, 72% green, 7% blue — because the eye is far more sensitive to green than to blue. Pure green is more than eight times brighter than pure blue at full intensity, despite both being ff in their own channel.
The practical consequence: “20% darker” done by scaling RGB channels is not 20% darker. Multiplying every channel by 0.8 produces something the eye reads as considerably more than a fifth darker, and the mismatch is worst in the mid-tones where most interface design happens. That is why HSL lightness and perceptual spaces exist, and why a palette generated by naive channel arithmetic tends to look uneven.
Greys have no hue
When the three channels are equal — #000000, #808080, #ffffff — the colour has zero saturation, and hue has nothing to describe. There is no direction on the colour wheel to point at.
Every implementation reports hue 0 for these, and that is a convention rather than a measurement. It falls out of the arithmetic: the formula picks a branch based on which channel is largest, and when all three are equal the difference is zero and the branch is arbitrary. Reporting zero is tidy and means nothing.
This matters when generating palettes programmatically. Taking a grey, adjusting its “hue”, and converting back does nothing at all while saturation stays at zero — the hue is carried but has no effect. A grey has to gain saturation before a hue is meaningful, which is a different operation from shifting one.
The tool says so when the colour is achromatic, rather than presenting the zero as though it were information.
Doing it in a spreadsheet
Hex to RGB is three HEX2DEC calls on two-character slices: =HEX2DEC(MID(A1,2,2)) for red, with MID(A1,4,2) and MID(A1,6,2) for green and blue, assuming the hash is in position one. Going back is =DEC2HEX(R,2)&DEC2HEX(G,2)&DEC2HEX(B,2), and the second argument is what forces the leading zero on single-digit channels.
For contrast, build the luminance curve in its own column rather than inline, because it is piecewise: =IF(c<=0.03928, c/12.92, ((c+0.055)/1.055)^2.4) per channel with c being the channel divided by 255, then weight them 0.2126, 0.7152 and 0.0722 and sum. The ratio is =(L1+0.05)/(L2+0.05) with the lighter colour on top.
Writing it out that way is worth the extra cells. A single collapsed formula hides the gamma step, and the gamma step is exactly the part that people leave out when they reimplement this from memory — producing a ratio that looks reasonable and passes colours that should fail.
Sources and methodology
Nothing here is fetched and there is no data feed. The format conversions are exact by construction; the contrast figures are not a convention of this site but come from the WCAG relative-luminance formula, including the sRGB transfer curve that makes channel values non-linear in perceived brightness. The three-digit expansion rule comes from the CSS colour specification.