The Three Main CSS Color Formats

Every color you see on a screen is produced by mixing red, green, and blue light at different intensities. CSS gives you three primary ways to express these colors: HEX, RGB, and HSL. Each format describes the same set of colors, but they use different notation systems that are better suited to different workflows.

All three formats can represent the full range of approximately 16.7 million colors available in the standard sRGB color space. Choosing between them is not about capability — it's about readability, convenience, and how you think about color in your particular project. A design system might favor HSL for its intuitive adjustments, while a quick prototype might use HEX values copied directly from a design tool.

/* The same color in all three formats */
color: #4F6EF7;            /* HEX */
color: rgb(79, 110, 247);  /* RGB */
color: hsl(229, 91%, 64%); /* HSL */

HEX — Compact and Universal

HEX (hexadecimal) is the most common color format on the web. A HEX color code starts with a # symbol followed by six hexadecimal characters representing the red, green, and blue channels. Each pair of characters encodes a value from 00 (0) to FF (255) in base-16 notation.

The format is #RRGGBB. For example, #FF0000 is pure red (red=255, green=0, blue=0), #00FF00 is pure green, and #0000FF is pure blue. Black is #000000 and white is #FFFFFF. The letters A through F represent values 10 through 15, extending the single-digit decimal system to base-16.

CSS also supports a 3-digit shorthand when each pair consists of identical characters. For example, #FF3366 can be shortened to #F36. The browser expands each digit by doubling it, so #F36 becomes #FF3366. This shorthand is convenient for common colors but only works when the condition is met.

For transparency, CSS supports an 8-digit HEX format: #RRGGBBAA, where the last two characters represent the alpha (opacity) channel. A value of FF means fully opaque, while 00 means fully transparent. For example, #4F6EF780 gives roughly 50% opacity. There's also a 4-digit shorthand: #RGBA.

#4F6EF7     /* fully opaque */
#4F6EF780   /* ~50% opacity */
#4F6EF700   /* fully transparent */
#F36        /* shorthand for #FF3366 */

HEX is compact, universally supported, and the default output of most design tools like Figma and Photoshop. Its main drawback is that it's not human-readable — looking at #4F6EF7 doesn't immediately tell you what color you're seeing.

RGB — Channel-Level Control

The rgb() function expresses colors using decimal values for red, green, and blue, each ranging from 0 to 255. This format is mathematically equivalent to HEX but uses a more familiar decimal notation that many developers find easier to read and adjust.

rgb(79, 110, 247)   /* ToolPlex primary blue */
rgb(255, 0, 0)      /* pure red */
rgb(0, 0, 0)        /* black */
rgb(255, 255, 255)  /* white */

RGB becomes especially useful when you need to calculate or animate color values programmatically. Since each channel is a simple integer, you can easily increment, interpolate, or randomize values in JavaScript. Adjusting a single channel by a known amount is straightforward in RGB but requires mental hexadecimal math in HEX.

For transparency, use the rgba() function, which adds a fourth parameter for alpha. The alpha value ranges from 0 (fully transparent) to 1 (fully opaque). Modern CSS also allows the newer syntax rgb(79 110 247 / 0.5) with a forward slash separating the alpha value, which works in all current browsers.

rgba(79, 110, 247, 0.5)     /* 50% opacity, legacy syntax */
rgb(79 110 247 / 0.5)       /* 50% opacity, modern syntax */
rgb(79 110 247 / 50%)       /* same thing, percentage alpha */

RGB is the best choice when you're working with color manipulation in JavaScript, building color pickers, or when you find decimal values more intuitive than hexadecimal pairs.

HSL — Designed for Humans

HSL stands for Hue, Saturation, and Lightness, and it represents colors in a way that matches how humans naturally think about them. Instead of mixing abstract red, green, and blue channels, you describe a color by its identity (hue), its vividness (saturation), and its brightness (lightness).

Hue is the color angle on a 360-degree color wheel. Red sits at 0 degrees (and 360 degrees), green at 120 degrees, and blue at 240 degrees. Every other color falls somewhere between these points — orange is around 30 degrees, yellow is 60, cyan is 180, and purple is 270.

Saturation controls the intensity of the color, from 0% (completely gray) to 100% (fully vivid). Desaturated colors appear muted and washed out, while fully saturated colors are bright and vibrant.

Lightness controls brightness, from 0% (pure black) to 100% (pure white). A lightness of 50% gives you the purest version of the hue. Values below 50% darken toward black, and values above 50% lighten toward white.

hsl(229, 91%, 64%)   /* ToolPlex primary blue */
hsl(229, 91%, 44%)   /* darker variant */
hsl(229, 91%, 84%)   /* lighter variant */
hsl(229, 40%, 64%)   /* desaturated variant */

The power of HSL becomes clear when building color palettes and themes. To create a darker shade of a color, you simply reduce the lightness. To create a muted variant, reduce the saturation. The hue stays the same, keeping the color family consistent. This kind of systematic adjustment is difficult with HEX or RGB, where changing one channel affects the perceived hue unpredictably.

HSL supports transparency with hsla() or the modern slash syntax hsl(229 91% 64% / 0.5), just like RGB.

Which Format to Use When

Each format has situations where it shines. Here's a practical guide:

In practice, many professional stylesheets use a mix of formats. HEX for one-off values copied from design specs, and HSL for systematic colors defined as custom properties. The browser treats them all the same internally — it's purely a matter of developer experience.

CSS Custom Properties and Color Formats

CSS custom properties (variables) are where your color format choice has the biggest practical impact. When you define your design system's colors as custom properties, HSL offers a significant advantage: you can expose the individual components and recombine them for variations.

:root {
  --primary-h: 229;
  --primary-s: 91%;
  --primary-l: 64%;
  --primary: hsl(var(--primary-h), var(--primary-s), var(--primary-l));
}

.button {
  background: var(--primary);
}

.button:hover {
  /* Darken by reducing lightness */
  background: hsl(var(--primary-h), var(--primary-s), 54%);
}

.button:disabled {
  /* Desaturate for disabled state */
  background: hsl(var(--primary-h), 30%, var(--primary-l));
}

This pattern lets you create hover states, active states, backgrounds, and borders that are all mathematically derived from a single base color. Changing one variable updates the entire palette. You get consistent, harmonious color schemes without maintaining dozens of separate color values.

With HEX or RGB, achieving the same flexibility requires JavaScript or a CSS preprocessor like Sass. HSL gives you this power in pure CSS, making it the preferred format for modern design systems and component libraries.

Whichever format you choose, the most important thing is consistency within your project. Pick a primary format, use it for your design tokens and custom properties, and only deviate when a specific situation calls for it.