What is a hex color code?
A hex color code is a six-character (or three, or eight) hexadecimal string that represents a color on the screen. The format dates to early HTML and remains the dominant way developers and designers communicate colors today. The string #FF5733 encodes three numbers: FF (red, 255), 57 (green, 87), 33 (blue, 51) — together producing a vibrant orange.
Each pair of hex digits represents one byte (0–255) of intensity for one of the three primary additive light colors: red, green, blue. The combinations span 16,777,216 distinct colors (256³) — far more than the human eye can distinguish in most viewing conditions. The leading # is HTML/CSS convention; in CSS, both #FF5733 and FF5733 are valid in some contexts but the hash is universally accepted.
Hex codes have variants for different use cases: 3-digit shorthand (#F53) where each digit is duplicated to form 6 — useful for short writing of palette colors; the 6-digit standard; and 8-digit with alpha (#FF573380) where the last two digits encode opacity from 00 (transparent) to FF (opaque).
How hex, RGB, HSL, and OKLCH relate
All four formats describe the same color in different ways. Picking the right format matters when you're tweaking colors programmatically or designing accessible palettes.
| Format | Syntax | Best for | Limitations |
|---|---|---|---|
| Hex | #FF5733 or #FF573380 | Compact transmission, design specs, cross-language compatibility | Can't tweak hue/saturation directly. Hard to interpolate. |
| RGB / RGBA | rgb(255 87 51) / rgb(255 87 51 / 0.5) | Same as hex, more readable. Native to <canvas> and image processing. | Doesn't reflect human perception of color similarity. |
| HSL / HSLA | hsl(11 100% 60%) | Designing palettes — adjust lightness or saturation while preserving hue. | "Lightness" doesn't match perceived brightness across hues. Yellow at L=50% looks brighter than blue at L=50%. |
| OKLCH | oklch(0.71 0.18 27) | Modern accessible palettes — perceptually uniform. Best for fade transitions and dark-mode generation. | Newer; some color picker tools don't support it. Check browser support. |
| Named colors | tomato, cornflowerblue | Quick prototyping, readable demos | Only 147 named CSS colors. Limited palette. |
The same color in every format
/* All identical — vibrant orange */
color: #FF5733;
color: rgb(255 87 51);
color: hsl(11 100% 60%);
color: oklch(0.66 0.18 32);
color: tomato; /* close, but not exact */
How hex → RGB conversion actually works
The math is trivial — just convert each pair of hex digits to a base-10 number 0–255. The clever parts are handling the variants:
// Parse hex string to RGB tuple
function hexToRgb(hex) {
hex = hex.replace('#', '');
// 3-digit shorthand: F53 → FF5533
if (hex.length === 3) {
hex = hex.split('').map(c => c + c).join('');
}
// Now we have 6 (RGB) or 8 (RGBA) chars
const r = parseInt(hex.slice(0, 2), 16);
const g = parseInt(hex.slice(2, 4), 16);
const b = parseInt(hex.slice(4, 6), 16);
const a = hex.length === 8 ? parseInt(hex.slice(6, 8), 16) / 255 : 1;
return { r, g, b, a };
}
hexToRgb('#FF5733'); // { r: 255, g: 87, b: 51, a: 1 }
hexToRgb('F53'); // { r: 255, g: 85, b: 51, a: 1 }
hexToRgb('#FF573380'); // { r: 255, g: 87, b: 51, a: 0.5 }
RGB to HSL conversion is more involved (~25 lines of arithmetic). The full algorithm finds the min and max channel, derives lightness, then derives saturation and hue based on which channel is dominant. Most languages have a built-in or one-line library function — chroma-js, colord, tinycolor2 in JS; colorsys in Python; CSS Color Module Level 4 has native conversion functions.
OKLCH — the modern perceptually uniform color space
OKLCH (Oklab + L = lightness, C = chroma, H = hue) is a modern color space designed by Björn Ottosson in 2020. Unlike HSL, the lightness component matches human perception. Two colors with the same OKLCH lightness look equally bright to the eye — finally, a color space that does what designers expect.
- OKLCH lightness 0 = black, 1 = white. Linear and predictable.
- Chroma is unbounded (typical max ~0.4 for sRGB-displayable colors).
- Hue is 0–360°, same as HSL.
- Use case: generating accessible color palettes by varying lightness while keeping chroma + hue constant.
Browser support for OKLCH in CSS landed in 2023 (Chrome 111, Firefox 113, Safari 16.4) — safe to use in production for modern audiences.
Color in CSS — practical patterns
CSS custom properties (variables)
:root {
--color-primary: #00d084;
--color-primary-50: #e6faf3;
--color-primary-500: #00d084;
--color-primary-900: #003d27;
}
.btn { background: var(--color-primary); }
.btn--soft { background: var(--color-primary-50); }
RGB with alpha for layering
:root {
--primary-rgb: 0, 208, 132; /* store as RGB triple */
}
.glass {
background: rgba(var(--primary-rgb), 0.08); /* easy alpha tweaks */
border: 1px solid rgba(var(--primary-rgb), 0.3);
}
Modern color spaces with fallback
:root {
/* Fallback for older browsers */
--primary: #00d084;
/* Modern, perceptually-uniform fallback */
--primary: oklch(0.78 0.18 158);
}
/* Or use color-mix for tints */
.tint-50 { background: color-mix(in oklch, var(--primary), white 50%); }
Tailwind v4 / arbitrary values
<!-- Tailwind v4 supports OKLCH natively -->
<div class="bg-[oklch(0.78_0.18_158)]">...</div>
<!-- Or with hex -->
<div class="bg-[#00d084]">...</div>
Color conversion in 8 programming languages
JavaScript / TypeScript
// Vanilla — hex to RGB
const hexToRgb = h => {
const v = h.replace('#','').match(/.{2}/g);
return v.map(x => parseInt(x, 16));
};
hexToRgb('#FF5733'); // [255, 87, 51]
// Better — use a library like colord (1KB, robust)
import { colord } from 'colord';
const c = colord('#FF5733');
c.toRgb(); // { r: 255, g: 87, b: 51, a: 1 }
c.toHsl(); // { h: 11, s: 100, l: 60, a: 1 }
c.alpha(0.5).toHex(); // "#ff573380"
Python
import colorsys
# Hex → RGB tuple (0-255)
def hex_to_rgb(h):
h = h.lstrip('#')
return tuple(int(h[i:i+2], 16) for i in (0, 2, 4))
# RGB → HSL (note: colorsys uses 0-1 range)
r, g, b = hex_to_rgb('#FF5733')
h, l, s = colorsys.rgb_to_hls(r/255, g/255, b/255)
# h*360 = 11°, l*100 = 60%, s*100 = 100%
# Modern approach: use 'colour' or 'colormath' libs
PHP
function hexToRgb($hex) {
$hex = ltrim($hex, '#');
if (strlen($hex) === 3) {
$hex = $hex[0].$hex[0].$hex[1].$hex[1].$hex[2].$hex[2];
}
return [
'r' => hexdec(substr($hex, 0, 2)),
'g' => hexdec(substr($hex, 2, 2)),
'b' => hexdec(substr($hex, 4, 2)),
];
}
print_r(hexToRgb('#FF5733'));
Java / Kotlin
import java.awt.Color;
Color c = Color.decode("#FF5733");
int r = c.getRed(); // 255
int g = c.getGreen(); // 87
int b = c.getBlue(); // 51
// HSB (HSL variant)
float[] hsb = Color.RGBtoHSB(r, g, b, null);
// hsb[0] * 360 = hue, hsb[1] * 100 = saturation, hsb[2] * 100 = brightness
Swift / iOS
extension UIColor {
convenience init(hex: String) {
let h = hex.replacingOccurrences(of: "#", with: "")
var rgb: UInt64 = 0
Scanner(string: h).scanHexInt64(&rgb)
self.init(
red: CGFloat((rgb >> 16) & 0xFF) / 255,
green: CGFloat((rgb >> 8) & 0xFF) / 255,
blue: CGFloat(rgb & 0xFF) / 255,
alpha: 1
)
}
}
let orange = UIColor(hex: "#FF5733")
Android Kotlin (Jetpack Compose)
// Compose Color from hex
import androidx.compose.ui.graphics.Color
val orange = Color(0xFFFF5733) // ARGB hex literal
// Convert from String
val orange2 = Color(android.graphics.Color.parseColor("#FF5733"))
// Get RGB components
val r = orange.red // 1.0f (0..1 range)
val g = orange.green // 0.341f
val b = orange.blue // 0.2f
Go
import "image/color"
func HexToRGBA(hex string) color.RGBA {
var r, g, b, a uint8 = 0, 0, 0, 255
if hex[0] == '#' { hex = hex[1:] }
fmt.Sscanf(hex, "%02x%02x%02x", &r, &g, &b)
if len(hex) == 8 { fmt.Sscanf(hex[6:], "%02x", &a) }
return color.RGBA{r, g, b, a}
}
Bash / shell
# Hex to RGB
hex="FF5733"
r=$((16#${hex:0:2}))
g=$((16#${hex:2:2}))
b=$((16#${hex:4:2}))
echo "rgb($r, $g, $b)" # rgb(255, 87, 51)
# Reverse: RGB to hex
printf '#%02X%02X%02X\n' 255 87 51 # #FF5733
WCAG contrast — making colors accessible
Color choice has legal and ethical consequences in 2026. The Web Content Accessibility Guidelines (WCAG) specify minimum contrast ratios between text and background:
| Level | Normal text (under 18pt or 14pt bold) | Large text (18pt+ or 14pt+ bold) | Non-text (icons, borders) |
|---|---|---|---|
| WCAG AA (legal minimum in EU, US ADA) | 4.5:1 | 3:1 | 3:1 |
| WCAG AAA (gold standard) | 7:1 | 4.5:1 | 3:1 |
The contrast ratio is computed from the relative luminance of the two colors using the WCAG 2.1 formula. White on black is 21:1 (maximum). White text (#FFF) on the FreeDevTool accent green (#00d084) is 1.79:1 — fails AA badly. Use our WCAG Contrast Checker to verify pairs before shipping.
Common color pitfalls that fail WCAG
- Light gray text on white — looks elegant in mockups, fails 4.5:1 below
#767676. - Brand color buttons with white text — most pastel/neon brand colors fail. You need a darker variant for button backgrounds.
- Red error / green success indicators alone — 8% of men have red-green colorblindness. Pair color with an icon or label.
- Dark mode <#222 with regular text colors — dark backgrounds need lighter text than you'd think.
Color spaces — sRGB, Display P3, and beyond
The hex code #FF5733 alone is ambiguous about which color space — the same numbers display differently on a standard monitor (sRGB) vs a wide-gamut display (Display P3, ProPhoto RGB). Modern CSS Color Module Level 4 lets you specify color space:
/* sRGB (default — what hex/rgb() means historically) */
.box { background: #FF5733; }
/* Display P3 — about 25% wider gamut, vivid greens and reds */
.box { background: color(display-p3 1 0.341 0.2); }
/* Rec. 2020 — even wider, mostly for HDR video */
.box { background: color(rec2020 1 0.341 0.2); }
/* Same color in different spaces — pixel intensity differs */
For most web apps in 2026, sRGB is still the safe default. But on macOS and modern phones (iPhone X+, iPad Pro, all M-series Macs), users have wide-gamut displays. Brands that care about color fidelity (Apple itself, Stripe, Linear) ship sRGB and Display P3 variants together.
Color picker best practices
- Use CSS custom properties for every color, not hardcoded hex. Refactoring becomes a one-line change. Theming, dark mode, and brand updates all benefit.
- Define semantic names, not visual.
--color-primary,--color-text,--color-error— not--color-blue-500or--color-red. When the brand changes, the names still make sense. - Always check WCAG contrast. 4.5:1 minimum for body text. Use the contrast checker on every text-on-bg pair.
- Test in dark mode. A color that works on white might fail on dark backgrounds. Define paired colors for each mode.
- Use OKLCH for generating palettes. Perceptually uniform lightness means tints/shades look consistent across hues.
oklch(70% 0.15 H)at multiple H values gives an accessible, harmonious palette. - Ship a fallback for OKLCH. Older browsers don't support it. Use
@supportsor stack fallbacks. - Don't use exact RGB triples for transparency. Define base colors as RGB triples (
--primary-rgb: 0 208 132), then usergb(var(--primary-rgb) / 0.5)for variable alpha. - Avoid relying on color alone for state. Pair with icons, labels, or shape changes — accessibility for color-blind users.