Learn · Collections and history

A short history of digital color on the web

From the first #ff0000 hex codes to modern color(oklch …) syntax, web color has changed a lot. Understanding this history makes today's features – wide‑gamut color, perceptual models – easier to appreciate.

1.

Early HTML: simple color attributes

In the very first versions of HTML, color support was minimal:

  • Colors were applied directly on elements via attributes like bgcolor, link, vlink, alink.
  • Values were either basic names (red, blue, green) or hex codes (#ff0000).
html
<body bgcolor="#ffffff" text="#000000" link="blue" vlink="purple">

There were no stylesheets yet, no variables, no themes – just inline color choices that affected the entire page.

2.

Hex codes and 8‑bit color limitations

Hex codes quickly became the dominant way to define colors:

css
color: #ff0000;   /* red */
background: #003366;

Each pair of hex digits describes the intensity of red, green and blue:

  • #RRGGBB, values from 00 (0) to ff (255)
  • 256 possible values per channel → over 16 million combinations

However, in the 1990s and early 2000s many monitors and graphics cards ran in 8‑bit color (256 colors at once). To avoid banding and unexpected dithering, designers often tried to stick to colors that looked consistent across limited palettes.

3.

The 216 "web‑safe" colors

To cope with 256‑color displays, the idea of "web‑safe colors" emerged:

  • A palette of 216 colors defined by using only these hex channel values: 00, 33, 66, 99, cc, ff
  • Every combination of those six values for R, G and B was considered "safe"

The theory

  • Browsers on different systems would map these 216 colors directly to their hardware palettes.
  • Designers who stuck to this set could be confident their colors wouldn't dither or shift too much.

In practice, web‑safe colors were always a compromise:

  • They limited creative choice.
  • Actual device support and palettes varied.

As true 24‑bit color became standard, the need for this restriction disappeared. Today, "web‑safe colors" are mostly a historical curiosity – but you may still see references in old tutorials and design tools.

4.

CSS Color Level 1–3: named colors, rgb() and hsl()

As CSS matured, the way we write colors became more flexible.

Named colors

CSS adopted and standardised a list of named colors, many inherited from X11:

css
color: tomato;
background: darkslategray;
border-color: cornsilk;

There are 147 such names plus special keywords like transparent and currentColor.

rgb() and rgba()

Instead of hex, you could write explicit RGB components:

css
color: rgb(255, 0, 0);
background: rgba(0, 0, 0, 0.5);  /* 50% transparent black */

This made it easier to tweak individual channels or specify opacity.

hsl() and hsla()

CSS Color Level 3 introduced HSL (Hue, Saturation, Lightness):

css
color: hsl(210, 40%, 98%);
background: hsla(210, 100%, 50%, 0.1);

HSL matches how designers think:

Hue

Which color — an angle on a color wheel

Saturation

How intense or muted the color is

Lightness

How light or dark the color is

It made building simple color scales and tints more intuitive than with raw RGB.

5.

From sRGB to wide‑gamut (Display‑P3 and beyond)

For a long time, the web assumed a single color space: sRGB.

  • Designed in the 1990s to match typical CRT monitors.
  • Reasonably small gamut, but good for interoperability: the same hex code looked similar on most displays.

As displays improved – especially high‑end phones, tablets and monitors – many devices began to support wider gamuts such as Display‑P3:

  • Able to show more saturated reds and greens
  • Roughly corresponds to modern cinema and Apple display capabilities

The problem

  • Hex, rgb() and classic hsl() do not say which color space they refer to. By default they're sRGB.
  • To use wider gamuts on the web in a controlled way, CSS needed new syntax and clearly defined color spaces.
6.

Modern CSS: color() syntax and new spaces

CSS Color Level 4+ introduces a more flexible function:

css
color: color(display-p3 0.9 0.2 0.3);

Key ideas:

  • Explicit color space: srgb, display-p3, rec2020, lab, lch, oklab, oklch, etc.
  • Components are defined according to that space's rules, not always 0–255.
  • Browsers can clamp or convert if a device can't display the full gamut.
css
/* Wide-gamut P3 color */
background: color(display-p3 0.85 0.15 0.2);

/* CIELAB - perceptual color space */
color: lab(60% 40 20);

/* LCH (Lightness, Chroma, Hue) */
color: lch(65% 60 250);

This opens the door to

  • richer, more vivid colors on capable displays
  • more accurate conversions and gradients
  • color work based on perceptual spaces instead of raw RGB
7.

Perceptual models and oklch() / oklab()

Recent research led to new perceptual models such as OKLab and OKLCH. They aim to:

  • make numeric distances correspond more closely to perceived differences
  • produce smoother gradients and better‑behaved lightness curves
  • work well across both sRGB and wide‑gamut spaces

CSS now supports them via:

css
color: oklab(0.7 0.1 0.02);
color: oklch(0.7 0.12 230);

L — Lightness

0 to 1, from black to white

C — Chroma

Colorfulness — 0 is neutral gray, higher values are more vivid

H — Hue

Angle in degrees around the color wheel

Benefits for UI

  • You can create scales by stepping lightness and chroma in consistent increments.
  • Dark and light themes can be tuned to have similar perceived contrast.
  • Gradients avoid the "muddy middle" common with plain RGB interpolation.

Frameworks and tools (including newer Tailwind releases and design systems) increasingly build palettes using OKLCH or related perceptual models behind the scenes.

8.

Where we are today

Modern web color has come a long way from bgcolor="#ff0000":

  • Hex and rgb() remain widely used for compatibility and simplicity.
  • hsl() is still popular for hand‑tuned palettes.
  • Data‑driven and design‑system teams are adopting explicit color spaces with color(), OKLCH/OKLab for palette generation, and design tokens to separate semantic roles from raw values.
  • Wide‑gamut displays are becoming common, so P3 colors are more useful every year.

On ColorR

  • Classic formats (hex, RGB, HSL) for everyday work
  • Modern models (Lab, LCH, OKLCH) for building robust, accessible design systems
  • Named collections (Tailwind, Material, Radix, Open Color, Crayola, RAL, Munsell) that sit on top of these representations