iKit
Technical · 9 min read ·

HEX to RGB: How the Conversion Actually Works (2026)

A HEX to RGB conversion is just base-16 to base-10 on three bytes. Here is the exact math, the shorthand rule, alpha, and the CSS syntax for 2026.

HEX to RGB: How the Conversion Actually Works (2026)

HEX to RGB: How the Conversion Actually Works

A HEX color like #4F46E5 and its RGB equivalent rgb(79 70 229) describe the exact same pixel — they are two spellings of three bytes. HEX writes each byte in base-16; RGB writes it in base-10. Once you see that a color code is just three numbers wearing different clothes, converting between them by hand takes about ten seconds. This post walks the math, the shorthand rules, alpha, and the CSS syntax you'll actually type in 2026.

TL;DR

  • A six-digit HEX code is three bytes: RR, GG, BB, one per color channel.
  • Convert each pair from base-16 to base-10: first digit × 16 + second digit.
  • #4F46E5rgb(79 70 229); #FFFFFF → white; #000000 → black.
  • A 3-digit code expands by doubling each digit first: #4AE#44AAEE.
  • An 8-digit code adds an alpha byte; divide it by 255 for CSS 0–1 opacity.

What a HEX color code actually represents

Both HEX and RGB describe a color in the sRGB color space, the default gamut the web has used for over 25 years. Per the CSS Color Module Level 4, a <hex-color> is defined as hexadecimal notation for an sRGB color's red, green, and blue components plus optional transparency. Nothing more exotic is going on — it's a compact way to write three (or four) 8-bit numbers.

Three channels, one byte each

An 8-bit byte holds 256 distinct values, 0 through 255. Each color channel gets one byte, so a full color is 24 bits: 16,777,216 possible combinations, the familiar "16.7 million colors." Red, green, and blue mix additively — all three at maximum is white, all three at zero is black.

Why hexadecimal instead of plain numbers

A byte's value 255 needs three decimal digits but only two hexadecimal ones (FF), because base-16 packs 4 bits into every character. Two hex digits map cleanly onto one byte, so #RRGGBB lines up perfectly with the three-byte layout. That tidy alignment is the whole reason designers ended up staring at #-strings instead of rgb() triples.

The four valid HEX shapes

The spec accepts exactly four forms. The <hex-color> reference on MDN lists them as #RGB, #RGBA, #RRGGBB, and #RRGGBBAA — three or four channels, in short or long form.

HEX form Digits Meaning
#RGB 3 Shorthand RGB
#RGBA 4 Shorthand RGB + alpha
#RRGGBB 6 Full RGB
#RRGGBBAA 8 Full RGB + alpha

How to convert HEX to RGB by hand

The core operation is converting a two-character hex pair to a decimal 0–255 value. Do it three times and you have your RGB triple.

The base-16 to base-10 math

Each hex digit is worth 0–15 (where A=10, B=11, … F=15). In a two-digit pair, the left digit is the "sixteens" place and the right digit is the "ones" place:

value = (left_digit × 16) + right_digit

Take #4F46E5, iKit's own indigo. Split it into 4F, 46, E5:

4F = (4 × 16) + 15 = 64 + 15 = 79   → R
46 = (4 × 16) + 6  = 64 + 6  = 70   → G
E5 = (14 × 16) + 5 = 224 + 5 = 229  → B

So #4F46E5 is rgb(79 70 229). The two endpoints are easy to memorize as a sanity check: FF is 15×16 + 15 = 255, and 00 is 0. That's why #FFFFFF is white and #000000 is black.

Doing it in JavaScript

You rarely need to convert by hand, but knowing the one-liner helps you trust the tool. parseInt(str, 16) reads a hex string in base 16:

function hexToRgb(hex) {
  const n = parseInt(hex.replace("#", ""), 16);
  return {
    r: (n >> 16) & 255,
    g: (n >> 8) & 255,
    b: n & 255,
  };
}

hexToRgb("#4F46E5"); // { r: 79, g: 70, b: 229 }

The bit-shifts pull each byte out of the 24-bit integer. >> 16 moves the red byte down to the low 8 bits, and & 255 masks off everything above it. The reverse direction is just as short — pad each channel back to two hex digits and concatenate:

function rgbToHex(r, g, b) {
  const h = (n) => n.toString(16).padStart(2, "0");
  return "#" + h(r) + h(g) + h(b);
}

rgbToHex(79, 70, 229); // "#4f46e5"

The padStart(2, "0") matters: a channel like 9 converts to "9", and without padding you'd emit a malformed five-digit code. Casing is cosmetic — #4f46e5 and #4F46E5 are the same color — so uppercase the result only if your style guide asks for it.

When you just want the answer

For a one-off, paste the code into the iKit Color Picker & Converter — it runs entirely in your browser, so the color you're checking never leaves your machine, and it shows HEX, RGB, HSL, and OKLCH side by side.

How to expand a 3-digit HEX shorthand

Short codes like #4AE look different but convert with one extra step: expansion.

The doubling rule

Each digit is duplicated to form the matching channel pair. #RGB expands to #RRGGBB, so #4AE becomes #44AAEE. Then you convert normally:

  • 44 = (4 × 16) + 4 = 68 → R
  • AA = (10 × 16) + 10 = 170 → G
  • EE = (14 × 16) + 14 = 238 → B

Result: rgb(68 170 238). A common mistake is padding with zeros instead — #4AE is not #40A0E0. Doubling, not zero-filling, is the rule the CSS spec defines.

Why shorthand only reaches 4,096 colors

Shorthand can only represent channels where both digits are identical (44, AA, EE). That's 16 possible values per channel instead of 256, so 16 × 16 × 16 = 4,096 colors total — a tiny slice of the full 16.7 million. It's fine for flat brand colors but useless for photographic or gradient work.

The alpha shorthand

#RGBA follows the same doubling logic for the fourth digit. #4AE8 expands to #44AAEE88, giving an alpha byte of 88 (136 in decimal).

Handling alpha: the 8-digit HEX code

Modern CSS lets you bake opacity straight into the color, and RGB has kept pace.

Reading the fourth byte

In #RRGGBBAA, the final pair is alpha: 00 is fully transparent, FF fully opaque. It converts to 0–255 exactly like a color channel. #4F46E580 has an alpha of 80 = 128.

Converting alpha to CSS 0–1

CSS rgb() expresses alpha as a 0–1 fraction, not a byte, so divide by 255:

128 ÷ 255 ≈ 0.502

So #4F46E580 is roughly rgb(79 70 229 / 0.5) — half-opaque indigo. If you'd rather layer transparency with a gradient than a flat alpha, the iKit Gradient Generator writes the CSS for you.

Modern vs legacy RGB syntax

As of CSS Color Level 4, rgba() is a pure alias for rgb() — they take the same arguments and behave identically, a change documented in Chrome's high-definition CSS color guide. The syntax also modernized: the space-separated form with a slash before alpha is now preferred, though the comma form still works everywhere.

Style Example
Modern rgb(79 70 229 / 0.5)
Legacy rgba(79, 70, 229, 0.5)

Channel values may also be percentages (0%–100%) instead of 0–255 bytes, which is handy when you're thinking in proportions rather than raw levels.

Common conversions worth memorizing

A few pairs come up so often that recognizing them saves a trip to any converter:

HEX RGB
#000000 rgb(0 0 0)
#FFFFFF rgb(255 255 255)
#FF0000 rgb(255 0 0)
#808080 rgb(128 128 128)

#808080 is the useful one to internalize: 80 in hex is 128, the midpoint of 0–255, so equal-and-middling channels give neutral grey. Once 80 = 128 and FF = 255 are muscle memory, you can eyeball most codes.

If you're picking a whole set of colors rather than converting one, build them as a group in the iKit Color Palette Generator so the tones stay consistent across your design system.

References

Related on iKit

Related posts