Javid
·18 min read

Hex to RGB: Conversion Chart, Mental Math, and What Modern CSS Changed

SelfDevKit color tools converting a hex code to RGB, HSL, CMYK, and OKLCH values with a live color preview

How do you convert hex to RGB?

Split the six hex digits into three pairs and read each pair as a base-16 number. Multiply the first digit by 16, add the second, and you have a channel value between 0 and 255. #4682B4 is 46 → 70, 82 → 130, B4 → 180, which is rgb(70 130 180).

That is the entire hex to RGB operation. Three multiplications and three additions, and you can do it in your head once you have memorized sixteen numbers.

Most people never bother, because there is always a converter one tab away. Fair enough. But a chart is faster than a tab, mental math is faster than a chart, and there is a newer problem that no converter warns you about: a growing share of design systems no longer store colors as hex at all. Tailwind CSS v4 ships its default palette in OKLCH. When you convert one of those to hex, the answer is not exact, and two different tools will hand you two different values.

This post is the reference chart plus the parts of the story that charts leave out.

Table of contents

The hex to RGB math you can do in your head

Convert each hex pair by looking up the first digit's base value, then adding the second digit. The first digit is worth sixteen times its face value, the second digit is worth its face value, and A through F count as 10 through 15.

Here is the only table you have to memorize. Every hex pair starts at one of these sixteen numbers:

Pair starts Base Pair starts Base
0_ 0 8_ 128
1_ 16 9_ 144
2_ 32 A_ 160
3_ 48 B_ 176
4_ 64 C_ 192
5_ 80 D_ 208
6_ 96 E_ 224
7_ 112 F_ 240

Then add the second digit. B4 is 176 + 4 = 180. 4D is 64 + 13 = 77. FE is 240 + 14 = 254. That is it.

Two anchors make the table easier to hold on to. 8_ is 128, exactly half the range, which is why #808080 is the arithmetic middle grey. And F_ is 240, so anything starting with F is already at least 94% of full brightness. If a hex code has an F in the first position of a pair, that channel is effectively maxed.

The repeated-digit shortcut

Hex codes written by humans lean heavily on repeated digits, because those are the pairs that shorthand can express. Each step is exactly 17:

Pair Decimal Pair Decimal
00 0 88 136
11 17 99 153
22 34 AA 170
33 51 BB 187
44 68 CC 204
55 85 DD 221
66 102 EE 238
77 119 FF 255

This table doubles as the shorthand expansion chart. Three-digit hex expands by duplicating each digit, not by padding it with a zero, so #39F is #3399FF, which is rgb(51 153 255). The MDN hex color reference puts it bluntly: "If there is only one number, it is duplicated: 1 means 11."

Because every shorthand color must have both digits equal in each pair, shorthand can only reach 4,096 of the 16,777,216 colors a six-digit code can express. If a hex code does not have three matching pairs, it has no exact shorthand form.

Worked example

#DC143C, the color CSS calls crimson:

Pair Base from first digit Plus second digit Decimal
DC D_ = 208 + 12 220
14 1_ = 16 + 4 20
3C 3_ = 48 + 12 60

rgb(220 20 60). Ten seconds, no tab.

Hex to RGB chart: CSS named colors

The 148 CSS named colors are the one place where specific hex values are fixed by a standard rather than chosen by a designer, which makes them the most useful entries in any hex to RGB chart. Here are the ones that actually show up in stylesheets and code review comments.

Name Hex RGB
black #000000 rgb(0 0 0)
white #FFFFFF rgb(255 255 255)
red #FF0000 rgb(255 0 0)
lime #00FF00 rgb(0 255 0)
blue #0000FF rgb(0 0 255)
yellow #FFFF00 rgb(255 255 0)
cyan #00FFFF rgb(0 255 255)
magenta #FF00FF rgb(255 0 255)
gray #808080 rgb(128 128 128)
silver #C0C0C0 rgb(192 192 192)
maroon #800000 rgb(128 0 0)
olive #808000 rgb(128 128 0)
green #008000 rgb(0 128 0)
purple #800080 rgb(128 0 128)
teal #008080 rgb(0 128 128)
navy #000080 rgb(0 0 128)
orange #FFA500 rgb(255 165 0)
gold #FFD700 rgb(255 215 0)
pink #FFC0CB rgb(255 192 203)
hotpink #FF69B4 rgb(255 105 180)
crimson #DC143C rgb(220 20 60)
tomato #FF6347 rgb(255 99 71)
coral #FF7F50 rgb(255 127 80)
orangered #FF4500 rgb(255 69 0)
firebrick #B22222 rgb(178 34 34)
brown #A52A2A rgb(165 42 42)
chocolate #D2691E rgb(210 105 30)
goldenrod #DAA520 rgb(218 165 32)
tan #D2B48C rgb(210 180 140)
wheat #F5DEB3 rgb(245 222 179)
khaki #F0E68C rgb(240 230 140)
beige #F5F5DC rgb(245 245 220)
ivory #FFFFF0 rgb(255 255 240)
whitesmoke #F5F5F5 rgb(245 245 245)
lavender #E6E6FA rgb(230 230 250)
plum #DDA0DD rgb(221 160 221)
violet #EE82EE rgb(238 130 238)
indigo #4B0082 rgb(75 0 130)
rebeccapurple #663399 rgb(102 51 153)
midnightblue #191970 rgb(25 25 112)
steelblue #4682B4 rgb(70 130 180)
dodgerblue #1E90FF rgb(30 144 255)
skyblue #87CEEB rgb(135 206 235)
turquoise #40E0D0 rgb(64 224 208)
seagreen #2E8B57 rgb(46 139 87)
forestgreen #228B22 rgb(34 139 34)
limegreen #32CD32 rgb(50 205 50)
salmon #FA8072 rgb(250 128 114)
slategray #708090 rgb(112 128 144)
darkslategray #2F4F4F rgb(47 79 79)

Two things worth noticing. green is #008000, not #00FF00; the bright one is called lime, which trips up more people than it should. And rebeccapurple is #663399, added to the spec in memory of Eric Meyer's daughter, which is why it is the one named color with a story attached.

Named colors are also the fastest sanity check on a converter. If a tool tells you steelblue is anything other than rgb(70 130 180), stop using it. For choosing values rather than looking them up, the hex color picker guide walks through picking and adjusting codes directly.

The grey ramp, and why #808080 is not half as bright as white

Greys are the hex codes developers convert most often, because border, divider, and text colors are almost always neutral. The chart is short because every channel is identical:

Hex RGB Typical use
#000000 rgb(0 0 0) Pure black, rarely correct for text
#111111 rgb(17 17 17) Dark mode background
#222222 rgb(34 34 34) Dark mode surface
#333333 rgb(51 51 51) Body text on white
#444444 rgb(68 68 68) Secondary text
#666666 rgb(102 102 102) Muted text
#808080 rgb(128 128 128) Midpoint grey
#999999 rgb(153 153 153) Placeholder text
#CCCCCC rgb(204 204 204) Borders
#DDDDDD rgb(221 221 221) Dividers
#EEEEEE rgb(238 238 238) Subtle fills
#F5F5F5 rgb(245 245 245) Page background
#FFFFFF rgb(255 255 255) White

Now the part that surprises people. #808080 is the numeric midpoint of the 0-255 range, but it does not emit half as much light as white. sRGB channel values are gamma-encoded, so converting to actual light output means linearizing them first. Channel 128 has a relative luminance of about 21.6% of white. The grey that genuinely sits halfway between black and white in light output is closer to #BBBBBB, at 49.7%.

This matters the moment you touch contrast. WCAG ratios are computed from linearized luminance, not from the raw RGB integers, and the cliff is steeper than the hex codes suggest. Against white, #666666 scores 5.74:1 and passes AA. One step lighter, #777777 scores 4.48:1 and fails, by two hundredths. If you are picking greys off a chart and assuming the numbers track perceived brightness, you will guess wrong in exactly the direction that fails an audit. The rgb color picker guide covers the luminance formula in more detail.

RGB as percentages

CSS rgb() accepts percentages as well as 0-255 integers, and the two are interchangeable: 0% is 0, 100% is 255, and everything scales linearly in between. rgb(100% 50% 0%) is rgb(255 128 0).

Percentages come up when you paste a value out of a design tool, a graphics library, or a print workflow, since many of them normalize channels rather than using bytes. The conversion is simply byte / 255, but the round numbers rarely survive it:

Percentage Byte Hex Exact value back
0% 0 00 0%
10% 26 1A 10.196%
20% 51 33 20%
30% 77 4D 30.196%
40% 102 66 40%
50% 128 80 50.196%
60% 153 99 60%
70% 179 B3 70.196%
80% 204 CC 80%
90% 230 E6 90.196%
100% 255 FF 100%

The multiples of 20 land exactly, because 51, 102, 153, and 204 are all clean multiples of 51. The rest drift by about two tenths of a percent. That drift is invisible on screen and extremely visible in a snapshot test diff.

Your palette might not be hex anymore

The biggest change to hex to RGB workflows in the last two years is that a lot of design systems stopped shipping hex. Tailwind CSS v4 rebuilt its default palette in OKLCH, so blue-500 is no longer a hex code:

--color-blue-500: oklch(62.3% 0.214 259.815);
--color-emerald-500: oklch(69.6% 0.17 162.48);
--color-amber-400: oklch(82.8% 0.189 84.429);

Hex to RGB is lossless. Both notations describe the same three bytes in sRGB, so the round trip is exact in either direction. OKLCH to hex is not lossless, because OKLCH can describe colors that sRGB cannot reach. Several of Tailwind's own defaults fall outside the sRGB gamut, and converting them produces channel values that are not valid bytes:

Token Raw sRGB conversion Naive clamp Chroma-mapped
blue-500 rgb(43 127 261) #2B7FFF #3280FF
emerald-500 rgb(-55 188 125) #00BC7D #00B981
amber-400 rgb(255 185 -59) #FFB900 #FABC00
indigo-500 rgb(97 95 257) #615FFF #6260FF
red-500 rgb(251 44 54) #FB2C36 #FB2C36

Look at the blue channel on blue-500: 261. There is no such byte. Something has to give, and the two common strategies give different answers.

Naive clamping pins every out-of-range channel to 0 or 255. It is fast and it is what most quick converters do, but it shifts the hue, because clamping one channel while leaving the others alone changes the ratio between them.

Gamut mapping reduces the color's chroma in OKLCH until it fits, which preserves the hue and produces a slightly less saturated but visually closer result. The CSS Color Module Level 4 spec describes the intended outcome: a color outside the display gamut "would need to be CSS gamut mapped for display, producing a similar-looking but lower chroma (less saturated) color."

Here is the practical kicker. The spec asks for chroma reduction, but browsers do not do that yet. As Evil Martians documents in its OKLCH writeup, "The CSS Colors 4 spec requires browsers to use the OKLCH method for gamut mapping. But still, right now, Chrome and Safari use the fast, but inaccurate, clipping method."

So #2B7FFF is closer to what your users currently see, and #3280FF is closer to what the color is supposed to be. Neither is wrong. They are answers to different questions.

What to do about it:

  1. Never treat an OKLCH-to-hex conversion as a source of truth. Keep the OKLCH token as canonical and generate hex as a fallback, not the other way around.
  2. Pick one converter and use it everywhere. Mixing a clamping tool and a gamut-mapping tool across a codebase is how one brand blue becomes two.
  3. Check whether the color is in gamut at all before converting. If it is, like Tailwind's red-500, both methods agree exactly and you can stop worrying.

Two different colors, one hex code

Hex notation can only describe sRGB. The spec is explicit on this: "The CSS hex color notation allows an sRGB color to be specified by giving the components as hexadecimal numbers." That is a hard ceiling, and it means hex is a lossy destination for anything wider. Note that the ceiling applies to rgb() too, since MDN's rgb() reference defines it in the sRGB color space as well. Converting hex to RGB does not buy you any extra range.

A concrete example. color(display-p3 0 1 0) is the greenest green a P3 display can produce. Converted to sRGB it comes out as rgb(-131 260 -79), which clips to #00FF00. But #00FF00 on its own is sRGB pure green, a visibly duller color on the same screen. Two genuinely different greens, one hex code.

This is the real answer to "why does my color look different from the design file." The hex to RGB math is exact and is almost never the culprit. The mismatch comes from somewhere upstream: a designer picking in a P3 space, an image with an embedded ICC profile, or a screenshot color-picked from a wide-gamut display and pasted into a stylesheet as if it were sRGB.

The workflow fix is boring but effective. Decide at the top of the project whether your tokens are sRGB or wide gamut, write it down, and stop color-picking values off screenshots.

When a chart is not enough

Charts cover lookup. They do not cover the cases where hex to RGB conversion actually breaks in code, and those are worth knowing before you write your own parser:

  • Alpha. Eight-digit hex encodes opacity as a byte, so #80 is 50.196% rather than a clean half.
  • Byte ordering. CSS reads eight-digit hex as #RRGGBBAA. Android reads it as #AARRGGBB. Paste a CSS value into an Android color resource and you get a plausible-looking wrong color that compiles fine and ships.
  • Input hygiene. parseInt('#394DFE', 16) returns NaN because # is not a hex digit, and parseInt('39zz', 16) returns 57 rather than failing, so you have to validate before you parse.

All three, with working JavaScript, Python, Go, and shell implementations, are covered in the companion post on hex to RGB color conversion rules and edge cases. If you are validating hex codes across a whole config file rather than one at a time, a regex validator is the faster route, and when a code that looks correct still refuses to parse, a text inspector will show you the stray non-breaking space you copied out of the brand guidelines.

Converting hex to RGB without a browser tab

SelfDevKit color tools showing a hex to RGB conversion alongside HSL, CMYK, and OKLCH values

Most hex to RGB conversions are not one-offs. You are walking a palette, checking contrast on each pair, generating a couple of tints, and pasting results into three different files. Doing that through a web converter means a round trip per color, and the color itself is frequently the most confidential thing on your screen: an unreleased brand palette, a client identity system under NDA, an accent color for a product that has not been announced.

SelfDevKit's color tools convert between HEX, RGB, HSL, CMYK, and OKLCH entirely on your machine. Nothing leaves the app, because the app has no reason to open a socket. The same panel generates complementary, triadic, analogous, split complementary, and tetradic harmonies, and checks contrast against WCAG 2.0 AA and AAA thresholds, so the palette walk happens in one place instead of four tabs.

The offline part is also just faster. No page load, no cookie banner, no ad script racing your keystrokes. We wrote about that tradeoff at length in why offline matters, and the download page has builds for macOS, Windows, and Linux.

Frequently asked questions

What is the RGB value of #FFFFFF?

#FFFFFF is rgb(255 255 255), pure white, because FF is the maximum byte value of 255. Its inverse #000000 is rgb(0 0 0). Any hex code with three identical pairs is a neutral grey somewhere between them.

Is hex to RGB conversion lossless?

Yes. Hex and rgb() describe the same three 8-bit sRGB integers in different notations, so converting either direction and back returns the original value exactly. Conversions involving HSL, OKLCH, or wide-gamut spaces are not lossless, because those can express values that do not land on clean bytes or fall outside sRGB entirely.

How do I convert an OKLCH color to hex?

Convert to sRGB, then check whether all three channels land inside 0-255. If they do, round and format as hex. If any channel falls outside, the color is out of gamut and you must choose between clamping the channels, which shifts the hue, or reducing chroma until it fits, which preserves it. Document which one you used.

Why does my hex code look different in the design file?

Almost always a color space difference rather than a conversion error. Hex is sRGB by definition, while design tools may be working in Display P3 or a CMYK print profile. The hex to RGB math is exact; the pipeline around it is what shifts the color.

Try it yourself

Keep this page bookmarked for the chart. When you need more than a lookup, whether that is contrast ratios across a palette, harmonies from a base color, or converting a stubborn OKLCH token, SelfDevKit does it offline alongside 50+ other developer tools. New here? The getting started guide is a five-minute tour.

Download SelfDevKit and stop pasting your brand colors into other people's servers.

Related Articles

Hex to RGB Color: Conversion Rules, Edge Cases, and Code That Works
DEVELOPER TOOLS

Hex to RGB Color: Conversion Rules, Edge Cases, and Code That Works

Convert hex to RGB color values correctly: the math, shorthand and alpha rules, parser code, and the platform gotchas that break conversions.

Read →
Hex Color Picker: The Developer Guide to Color Formats, Contrast, and Workflows
DEVELOPER TOOLS

Hex Color Picker: The Developer Guide to Color Formats, Contrast, and Workflows

Use a hex color picker to find exact color codes, convert between formats, and check WCAG contrast.

Read →
RGB Color Picker: How RGB Works, CSS Syntax, and Developer Workflows
DEVELOPER TOOLS

RGB Color Picker: How RGB Works, CSS Syntax, and Developer Workflows

Use an RGB color picker to find exact color values, convert formats, and write modern CSS with confidence.

Read →