Hex and rgb() side by side, so a colour you like goes straight into CSS, a design file or a brand doc without a second conversion step.
A hex colour code is not a name, it is three numbers written in base 16. #RRGGBB splits into red, green and blue, each stored in a single byte, and a byte holds 256 levels, 00 through FF, or 0 through 255 in decimal. Multiply the three and you get 16,777,216 distinct codes, exactly 2^24, which is why 24-bit colour and true colour describe the same thing.
Two digits per channel is not arbitrary. One hexadecimal digit encodes four bits, so a pair encodes exactly one byte, and one byte per channel is what nearly every graphics pipeline stores. The order is fixed: red, then green, then blue, most significant digit first within each pair. #FF8000 is 255 red, 128 green, zero blue, a saturated orange.
Case is irrelevant, so #ff8000 and #FF8000 parse identically, but in CSS the leading hash is required and a bare ff8000 is invalid. The limitation worth naming immediately is that those six digits carry no information about which colour space they belong to, no brightness and no gamut. They are three integers waiting for something else to interpret them, and every practical question about a random colour (is it readable, is it printable, will it match) is a question about that interpretation rather than about the digits themselves.
CSS has accepted a three-digit form since its earliest colour specifications, and the expansion rule is stricter than most people assume. Each digit is duplicated, not padded with a zero: #abc becomes #aabbcc, #f0c becomes #ff00cc, #000 becomes #000000. Padding would produce #a0b0c0, a different and duller colour, and no browser has ever done that.
Duplication is the right rule because it preserves the endpoints. Digit 0 maps to 00 and digit F maps to FF, so pure black and pure white are both writable in shorthand. Zero-padding would cap every channel at 0xF0, which is 240 of 255, and white would become unreachable in three digits.
The cost is coverage. Three digits address 16 levels per channel, so 16 × 16 × 16 gives 4,096 colours, a little under 0.03% of the 24-bit space. A shorthand colour is always one whose long form has matching digits in each pair, which is why #33BB77 shortens to #3B7 while #34BB77 cannot shorten at all. A generator that emitted shorthand would silently restrict every draw to those 4,096 options and make repeats far more likely, so the codes here are always full six-digit values. Shorten one by hand only when all three pairs match, and treat shorthand as a typing convenience rather than a colour format of its own.
CSS Color Module Level 4 added two more forms, both carrying alpha. The eight-digit #RRGGBBAA appends a fourth byte for opacity: 00 is fully transparent, FF fully opaque, and #FF000080 is red at roughly half alpha, since 0x80 is 128 and 128 ÷ 255 is about 0.502. The four-digit form expands by the same duplication rule, so #abcd becomes #aabbccdd, and the address space grows to 2^32.
The trap is channel order. CSS puts alpha last; Android colour integers are ARGB, with alpha first. Paste a CSS eight-digit value into a tool expecting #AARRGGBB and every channel shifts one position, so your opacity becomes the red level and the result reads like a bug somewhere else entirely.
The deeper limitation is that alpha is not a property of a colour at all. It is an instruction for compositing, resolved by the Porter-Duff source-over operation against whatever sits behind it. #FF000080 over white composites near #FF7F7F; over black it lands near #800000. Neither is the colour you typed, and the two have nothing in common visually. So never run a contrast check against a translucent hex value. Flatten it over the real backdrop first and test the flattened result, because that is the only colour a reader ever sees.
Draw three independent random bytes and you are sampling the RGB cube uniformly, which sounds fair and produces disappointing colours. The reason is geometric. The most saturated colours live on the surface of that cube, where at least one channel sits at 00 or FF, and the probability that a uniform triple lands on that surface is 1 − (254/256)^3, roughly 2.3%. Almost every other draw falls in the interior, clustered near the neutral diagonal running from #000000 to #FFFFFF, which is exactly why so many random codes read as olive, taupe or mud.
Lightness skews too. Perceived brightness is a weighted sum of the three channels, and sums of independent uniform variables pile up in the middle, so genuinely dark and genuinely pale results are rare. Most draws land in a mid-tone band that fights a white page and a dark one equally.
Then there is the weighting itself. RGB is not perceptually uniform, so equal steps in the digits are not equal steps in appearance. Shifting green by 16 changes apparent brightness far more than shifting blue by the same 16, because the luminance coefficients are 0.2126 for red, 0.7152 for green and 0.0722 for blue. A uniform sampler knows none of that, and treats the axis your eye is most sensitive to exactly like the one it barely registers.
Sample hue uniformly across 0 to 360 degrees, then constrain the other two axes (saturation somewhere in the 60 to 85% band, lightness in the 45 to 60% band) and almost every draw comes back usable. HSL and HSV are straightforward cylindrical remappings of the same RGB cube, so no new colours appear, but the axes now line up with the decisions you are actually making: which colour, how vivid, how light.
The limitation here is specific and worth knowing. HSL lightness is not perceived lightness. Pure yellow #FFFF00 and pure blue #0000FF both sit at L = 50% in HSL, yet their relative luminances are about 0.928 and 0.072, more than a factor of ten apart. Generate a set at constant HSL lightness and some swatches will vanish against white while others vanish against black, even though the numbers claimed they matched.
OKLCH, added in CSS Color Module Level 4 and derived from Björn Ottosson's Oklab work published in 2020, was designed to fix precisely that: its L axis tracks perceived lightness, so equal L looks equally bright and equal hue steps look evenly spaced. CIELAB, standardised in 1976, and the CIEDE2000 difference formula answer the related question of how far apart two colours look.
CSS defines a hex colour as an sRGB value, and sRGB is standardised as IEC 61966-2-1, published in 1999, using the Rec. 709 primaries and a D65 white point. That shared default is the only reason #3366CC looks broadly similar on two different machines. The digits alone are not a colour; the digits plus an assumed space are.
Display-P3 keeps the D65 white point but adopts the wider DCI-P3 primaries, reaching noticeably further into saturated reds and greens than sRGB manages. Most current phone and laptop panels support it, and CSS Color 4 exposes it through the color() function, as in color(display-p3 1 0 0).
Here is what that costs you. color(display-p3 1 0 0) is a more saturated red than #FF0000, and no six-digit hex code can express it. Hex is bounded by the sRGB gamut by definition, so wide-gamut work has to be written with color(), oklch() or lab() instead. Untagged assets make it worse: hand sRGB numbers to a pipeline that assumes P3 and performs no conversion, and everything oversaturates, a common outcome with untagged images and documents set to a P3 working space. State the limitation plainly. A random hex generator is an sRGB instrument by construction, right for the web and wrong for a wide-gamut target.
WCAG 2 contrast is computed from relative luminance, never from the hex digits directly. Each channel is divided by 255, then linearised with the sRGB transfer function, values at or below 0.04045 are divided by 12.92, higher values become ((c + 0.055) / 1.055) raised to 2.4, and the linearised channels are weighted 0.2126 red, 0.7152 green, 0.0722 blue. The ratio is (lighter + 0.05) divided by (darker + 0.05), so it runs from 1:1 up to 21:1 for black on white.
The thresholds are the most-checked numbers on any colour page. Success Criterion 1.4.3, Level AA, requires 4.5:1 for normal text and 3:1 for large text, where large means at least 18 point, or 14 point bold. Success Criterion 1.4.11 requires 3:1 for user interface components and meaningful graphical objects. Success Criterion 1.4.6, Level AAA, raises text to 7:1 and 4.5:1. WCAG 2.2 reached W3C Recommendation status in October 2023 without changing any of those values.
Run a random blue through it and the point lands. #0000FF scores roughly 8.6:1 against white and about 2.4:1 against black, comfortably passing on one backdrop and failing the AA minimum on the other. Yellow reverses the outcome almost exactly, and nothing in the six digits told you so.
A single code decides one thing and leaves the other four undecided. Treat the draw as a hue commitment and build the rest deterministically: hold saturation and lightness while rotating hue in fixed steps for a related family, or hold hue while stepping lightness for a tint and shade ramp. Then re-test every pairing you intend to render, because contrast is a property of pairs, not of swatches. This generator can hand you a colour; it cannot see the background you will put it on, and that is its honest boundary.
Colour vision deficiency imposes a harder constraint than taste does. Deuteranomaly, a shifted response in the medium-wavelength cones, is the most common form, and because the L- and M-cone opsin genes sit on the X chromosome, red-green deficiencies are far more frequent in men than in women. For those viewers, hues you read as opposites can converge: red and green status dots, or a red line and a green line crossing on one chart.
That is the reasoning behind Success Criterion 1.4.1 Use of Color at Level A, which requires that colour never be the only visual means of conveying information or indicating an action. The fix is not a better hue. Vary lightness as well as hue and add a second channel: a label, an icon, a dash pattern, a position.
No, and it does not need to be. A colour picker can use the browser's ordinary pseudorandom source, because the output is a visual suggestion rather than a secret. Never derive a password, token or key from a colour generator of any kind. When you genuinely need unpredictable bytes, use crypto.getRandomValues, which is what our password tools call.
Sooner than you would guess. With 16,777,216 equally likely codes, the birthday bound puts a 50% chance of a duplicate at roughly 4,800 draws, not at eight million. If you are generating a large batch and need every entry distinct, deduplicate the list yourself rather than trusting the odds to protect you.
Split the six digits into three pairs and read each pair as a base-16 number: the first digit times 16, plus the second. So 3C is 3 × 16 + 12 = 60, and #3C8A2F is rgb(60, 138, 47). Reverse it by dividing each value by 16 for the first digit and taking the remainder for the second.
Often not. Hex describes light emitted by a display in sRGB, while printing lays down ink in a CMYK process with a different gamut, and vivid screen blues and greens are the usual casualties. Conversion is handled by ICC profiles tied to a specific press, ink set and paper stock. Ask your printer which profile to use rather than converting blind.
Panel gamut and calibration differ, and a wide-gamut screen showing untagged sRGB content can render it oversaturated. Ambient light matters too, since the sRGB standard assumes a dim viewing environment that a sunny pavement is nothing like. Add per-device colour management and auto-brightness on top, and identical digits produce visibly different colours.
Yes. In CSS, transparent computes to rgba(0, 0, 0, 0), exactly what the eight-digit form #00000000 expresses. Use whichever reads better in your codebase, but remember that a fully transparent colour still carries RGB values underneath, which is why gradients fading to transparent once produced grey fringes before premultiplied interpolation became the norm.
Author in a perceptual space and export hex. Defining ramps in oklch() lets you step lightness evenly and derive shades arithmetically instead of hand-tuning each swatch, then emit hex for anything downstream. Hex remains the safest interchange format, because every browser, config file, game client and design tool reads it without negotiation.
Because they treat the colour as an integer literal rather than as CSS. The 0x prefix is C-style hexadecimal notation, used in Java, Kotlin, C# and many game engines where a colour is a 32-bit number. The digits themselves are identical, but check whether the value is RGB or ARGB before converting, since the channel order differs.