TextSorter

HEX, RGB, HSL, and OKLCH: CSS Color Space Conversion Guide

· 11 min read

Colors are a massive part of building websites. If you get them right, your site looks clean, professional, and readable. If you get them wrong, you might end up with something that looks like an early 2000s personal blog.

Over the years, CSS has evolved from supporting a small list of basic named colors (such as tomato or papayawhip) to complex mathematical models. As a developer, you have likely run into HEX codes, RGB functions, HSL declarations, and now, the modern OKLCH color space.

If you have ever looked at a HEX code like #3b82f6 and instantly known what color it is, you are probably a computer. For the rest of us, trying to figure out how to make a color ten percent lighter by editing raw hex characters is a nightmare.

In this guide, we are going to break down how these different color spaces work. We will look at why they exist, compare their strengths and weaknesses, and walk through how to convert between them. Best of all, we will show you how to do this easily using our free client side tool.

Why do we have so many CSS color spaces anyway?

To understand color spaces, we need to understand how screens display color. Most screens use red, green, and blue subpixels to light up your screen. Because of this, early web standards focused on hardware based color spaces. These spaces tell the screen exactly how much light to emit from each subpixel.

The problem is that humans do not think about colors in terms of red, green, and blue subpixel values. When you want a softer blue, you do not think, “Let me decrease the red subpixel by twelve percent and increase the green subpixel by five percent.” You think, “Let me make this blue lighter and less intense.”

This difference led to two main types of color models:

  1. Hardware oriented models (like HEX and RGB)
  2. Human oriented models (like HSL and OKLCH)

As web browsers have gotten more powerful and screens have gotten better, the W3C (World Wide Web Consortium) introduced new color specs. Specifically, CSS Color Module Level 4 has opened up access to wide color gamuts. This means we can now use colors that were physically impossible to display on older monitors. Because of this, older color formats are starting to show their age.

What is a HEX color code and how does it work?

The HEX code is the granddaddy of web colors. It is a hexadecimal representation of the RGB color model. Instead of counting from zero to ten, hexadecimal counts from zero to fifteen using the characters zero through nine, followed by A, B, C, D, E, and F.

A standard six character HEX code looks like #RRGGBB.

  • The first two characters (RR) represent the red value.
  • The middle two characters (GG) represent the green value.
  • The last two characters (BB) represent the blue value.

Each pair can have a value from 00 (which is zero) to FF (which is 255).

We also have eight character HEX codes, which look like #RRGGBBAA. The final two characters (AA) represent the alpha channel, which controls transparency. If you see #3b82f680, the 80 at the end means the color is about fifty percent transparent.

Why do we still use HEX codes?

Hex codes are short, easy to copy, and supported by every single browser on earth. Designers love them because they can easily copy a single string from design software like Figma or Photoshop and paste it directly into their code.

The downside of HEX codes

HEX codes are impossible for humans to read. You cannot easily adjust them. If you want to make a color darker or lighter, you have to go back to a color picker or run some calculations. You cannot just guess what #5c0bb3 will look like if you change the second digit.

How do you convert HEX to RGB manually?

Converting a HEX code to RGB is actually pretty simple math. Because hexadecimal is base sixteen, you just have to convert each pair of characters into a standard base ten number.

Let us take the HEX code #3b82f6 as an example.

First, split it into three pairs:

  • Red: 3b
  • Green: 82
  • Blue: f6

Now, let us convert the red pair 3b:

  1. The first character is 3. Multiply this by sixteen. (3 * 16 = 48)
  2. The second character is b. In hexadecimal, a is ten, and b is eleven.
  3. Add the two values together. (48 + 11 = 59) So, the red value is fifty-nine.

Next, let us convert the green pair 82:

  1. The first character is 8. Multiply by sixteen. (8 * 16 = 128)
  2. The second character is 2.
  3. Add them together. (128 + 2 = 130) So, the green value is 130.

Finally, let us convert the blue pair f6:

  1. The first character is f. In hexadecimal, f is fifteen. Multiply by sixteen. (15 * 16 = 240)
  2. The second character is 6.
  3. Add them together. (240 + 6 = 246) So, the blue value is 246.

Put them all together and you get rgb(59, 130, 246).

Here is a simple JavaScript function to do this conversion automatically. It handles both three character shorthand codes (like #3af) and six character codes.

function hexToRgb(hex) {
  const cleanHex = hex.trim().replace(/^#/, "");
  
  if (cleanHex.length !== 3 && cleanHex.length !== 6) {
    throw new Error("Invalid hex code length");
  }
  
  let r, g, b;
  
  if (cleanHex.length === 3) {
    const redChar = cleanHex.charAt(0);
    const greenChar = cleanHex.charAt(1);
    const blueChar = cleanHex.charAt(2);
    
    r = parseInt(redChar + redChar, 16);
    g = parseInt(greenChar + greenChar, 16);
    b = parseInt(blueChar + blueChar, 16);
  } else {
    r = parseInt(cleanHex.substring(0, 2), 16);
    g = parseInt(cleanHex.substring(2, 4), 16);
    b = parseInt(cleanHex.substring(4, 6), 16);
  }
  
  return { r, g, b };
}

// Example usage
console.log(hexToRgb("#3b82f6")); // Outputs: { r: 59, g: 130, b: 246 }

HSL stands for Hue, Saturation, and Lightness. It was introduced to CSS to solve the human readability problem of HEX and RGB.

Here is how the three parts work:

  • Hue: This represents the color itself, measured in degrees around a color wheel from 0 to 360. Zero is red, 120 is green, and 240 is blue.
  • Saturation: This is the intensity of the color, written as a percentage. 100% is full color, while 0% is completely gray.
  • Lightness: This is the brightness of the color, written as a percentage. 0% is pitch black, 100% is pure white, and 50% is the normal color.

Because of this structure, HSL is incredibly easy to tweak in your code. If you want to create a hover state for a button, you can use the exact same Hue and Saturation, and just lower the Lightness by ten percent.

For example, a button might have a background of hsl(220, 80%, 50%). For the hover state, you can simply write hsl(220, 80%, 40%). This makes creating design systems and themes very intuitive.

Why is HSL actually broken?

Despite how great HSL sounds, it has a massive flaw. It is not perceptually uniform.

What does this mean? It means that HSL is based on math, not on how human eyes actually perceive light. The human eye is not equally sensitive to all wavelengths of light. We perceive yellow as much brighter than blue, even if they have the exact same lightness value.

Let us test this. Consider these two HSL colors:

  1. Yellow: hsl(60, 100%, 50%)
  2. Blue: hsl(240, 100%, 50%)

Both colors have a Lightness value of fifty percent. But if you look at them on a screen, the yellow looks incredibly bright, while the blue looks deep and dark.

If you convert both colors to grayscale, the yellow turns into a very light gray, while the blue turns into a dark gray. This mismatch causes massive issues when trying to build accessible interfaces. If you try to place white text over both of these colors because they both have fifty percent lightness, the text will be easily readable on the blue button but completely unreadable on the yellow button.

Furthermore, because HSL is mapped to the sRGB color space, it cannot represent colors outside that narrow range.

How does OKLCH fix the problems of HSL?

This is where OKLCH comes in. OKLCH is a modern color space that was added to CSS in the CSS Color Module Level 4 specification. It stands for Lightness, Chroma, and Hue.

Let us look at what makes it different:

  • Lightness (L): Represents the perceived brightness. It is a percentage or decimal value from 0% (0) to 100% (1). Unlike HSL, this lightness is perceptually uniform. If you set two different colors to seventy percent lightness in OKLCH, they will look exactly as bright as each other to a human eye.
  • Chroma (C): Represents the saturation or purity of the color. It starts at zero (gray) and has no mathematical limit, though in practice it caps around 0.4 for screen colors. Unlike HSL, chroma is not capped at 100%, allowing it to access colors outside the traditional sRGB gamut.
  • Hue (H): Represents the color angle, usually from 0 to 360 degrees. 0 is a purplish red, 120 is green, and 240 is blue.

Because OKLCH is perceptually uniform, it makes color scaling incredibly easy. If you want to make a light theme and a dark theme, you can change the Hue while keeping the Lightness constant, and the contrast will remain identical. You do not have to manually tweak the lightness values for yellow and blue buttons to pass accessibility guidelines.

Here is what an OKLCH color looks like in CSS:

.button {
  background-color: oklch(0.63 0.25 264);
}

In this example:

  • 0.63 is the lightness.
  • 0.25 is the chroma.
  • 264 is the hue angle.

What is the difference between sRGB and Display P3 gamuts?

A color gamut is the range of colors that a device or color space can physically display.

For decades, the web relied on the sRGB color space. It was created in 1996 by HP and Microsoft to standardize colors across monitors. Since then, screen technology has improved dramatically. Modern phones, laptops, and TVs can display colors that are much more vibrant than what sRGB allows.

The most common wide gamut is Display P3, which was popularized by Apple. Display P3 offers roughly thirty percent more color range than sRGB. It can display much deeper greens, brighter reds, and more intense yellows.

If you write colors using HEX, RGB, or HSL, you are locked into the sRGB gamut. Even if a user is viewing your site on a high end display, they will not see the richest colors your monitor can display.

OKLCH allows you to tap into the Display P3 gamut. Because the Chroma value is not artificially capped at 100%, you can increase the chroma to access those super vibrant, wide gamut colors. If a browser does not support wide gamut, it will automatically scale the color down to the closest sRGB equivalent, making it perfectly safe to use.

How do you write color conversions in JavaScript?

To handle colors in your applications, you often need to convert between these spaces. Below is a full set of JavaScript functions showing how to convert between RGB, HEX, and HSL.

RGB to HEX conversion

To convert RGB back to a HEX string, you convert each number to its base sixteen string representation and pad it with a zero if it is only a single digit.

function rgbToHex(r, g, b) {
  const red = Math.max(0, Math.min(255, Math.round(r)));
  const green = Math.max(0, Math.min(255, Math.round(g)));
  const blue = Math.max(0, Math.min(255, Math.round(b)));
  
  const rHex = red.toString(16).padStart(2, "0");
  const gHex = green.toString(16).padStart(2, "0");
  const bHex = blue.toString(16).padStart(2, "0");
  
  return `#${rHex}${gHex}${bHex}`;
}

console.log(rgbToHex(59, 130, 246)); // Outputs: "#3b82f6"

RGB to HSL conversion

Calculating HSL from RGB requires finding the minimum and maximum values among the red, green, and blue channels, and then running them through a series of formulas.

function rgbToHsl(r, g, b) {
  const red = r / 255;
  const green = g / 255;
  const blue = b / 255;
  
  const max = Math.max(red, green, blue);
  const min = Math.min(red, green, blue);
  
  let h = 0;
  let s = 0;
  const l = (max + min) / 2;
  
  if (max !== min) {
    const diff = max - min;
    s = l > 0.5 ? diff / (2 - max - min) : diff / (max + min);
    
    switch (max) {
      case red:
        h = (green - blue) / diff + (green < blue ? 6 : 0);
        break;
      case green:
        h = (blue - red) / diff + 2;
        break;
      case blue:
        h = (red - green) / diff + 4;
        break;
    }
    
    h /= 6;
  }
  
  const hue = Math.round(h * 360);
  const saturation = Math.round(s * 100);
  const lightness = Math.round(l * 100);
  
  return { h: hue, s: saturation, l: lightness };
}

console.log(rgbToHsl(59, 130, 246)); // Outputs: { h: 220, s: 91, l: 60 }

Converting to OKLCH

Converting RGB to OKLCH is much more complex because it requires transitioning through a linear color space and then applying the OKLab matrix transformation.

The process follows these steps:

  1. Normalize RGB to a range between 0 and 1.
  2. Remove gamma encoding to convert the colors to linear RGB.
  3. Multiply by a matrix to convert the values to the LMS color space.
  4. Apply a non-linear transform by taking the cube root of the LMS values.
  5. Multiply by a second matrix to get the OKLab coordinate values (L, a, b).
  6. Convert the a and b coordinates into Chroma (C) and Hue (H) using trigonometry.

Because of this complexity, doing these conversions in your head or writing them by hand is a massive chore. That is why having a reliable converter is so important.

Why are online color converters sometimes a bad idea?

If you search for a color converter on Google, you will find hundreds of websites. While many of them work fine, they are not always ideal.

First, many of these websites are incredibly bloated. They are covered in ads, tracker scripts, and pop-ups that slow down your browser.

Second, some of these tools send your inputs back to their servers for analytics. While a color code is not a password, corporate developers often work with proprietary design specs or unreleased branding colors. Uploading your color schemes to a random server is an unnecessary security risk.

How does the TextSorter Color Converter make your life easier?

Our free Color Converter tool runs completely client-side in your browser. This means your data is never uploaded, tracked, or saved. It is fast, secure, and clean.

Here is how you can use it:

  1. Input any color format: You can paste a HEX code, an RGB string, an HSL declaration, or an OKLCH value. The tool automatically detects the format.
  2. Instant conversions: It translates your input color into all other formats immediately.
  3. Interactive sliders: You can drag sliders to adjust Hue, Saturation, Lightness, or Chroma, and see the preview update in real time.
  4. Contrast checking: The tool displays the color contrast against black and white text, helping you make sure your designs are fully accessible.

To try it out, head over to the TextSorter Color Converter.

Frequently Asked Questions about CSS Color Spaces

What is the difference between OKLCH and OKLab?

OKLab is a color space that represents colors using three axes: Lightness (L), a (green to red), and b (blue to yellow). OKLCH is the cylindrical form of OKLab. Instead of using the rectangular coordinates a and b, it uses Chroma (C) for distance from the center and Hue (H) for the angle around the circle. OKLCH is much easier for humans to read and write.

Does every browser support OKLCH?

As of 2023, all major modern browsers (Chrome, Safari, Firefox, and Edge) support OKLCH. If you are targeting very old browsers or legacy corporate environments, you should provide a fallback color in HSL or HEX format in your CSS rules.

How do I write a fallback for OKLCH in CSS?

You can provide fallback colors by writing the older color format first, followed by the OKLCH version. Browsers that do not understand OKLCH will ignore the second rule and use the first one.

.fallback-button {
  background-color: rgb(59, 130, 246); /* Fallback for older browsers */
  background-color: oklch(0.63 0.25 264); /* Modern browsers will use this */
}

Why does yellow look so bright in HSL?

In HSL, the lightness calculation treats red, green, and blue light equally. However, the human eye has different sensitivities for different wavelengths of light. We have more receptors for green and red light, which combine to make yellow, than we do for blue light. OKLCH accounts for this biological difference, while HSL does not.

Can I convert HEX directly to OKLCH?

Yes, but you have to go through a multi-step mathematical process. You first convert the HEX code to RGB, then normalize it, convert it to linear RGB, convert it to the LMS space, apply the cube root, convert to OKLab, and finally calculate the Chroma and Hue angles. Our client-side converter automates this entire pipeline instantly.

Frequently Asked Questions

What is OKLCH and why is it better than HSL?

OKLCH is a modern color space representing Lightness, Chroma, and Hue. Unlike HSL, it is perceptually uniform, meaning adjustments to lightness look consistent to the human eye regardless of hue. It also supports wide-gamut colors (like Display P3) not reachable by sRGB.

How do I convert a HEX color code to RGB?

To convert a 6-digit HEX color code to RGB, split it into three 2-digit groups representing Red, Green, and Blue, and convert each hexadecimal value to a decimal integer (from 0 to 255).

Are colors converted online secure?

Yes, when using client-side tools like the TextSorter Color Converter. The color codes are processed inside your browser using JavaScript and are not uploaded to any server.