If you asked me a few months ago, “What does it take for a website to stand out?” I may have said fancy animations, creative layouts, cool interactions, and maybe just the general aesthetics, without pointing out something in particular. If you ask me now, after working on color for the better part of the year, I can confidently say it’s all color. Among all the aspects that make a design, a good color system will make it as beautiful as possible.
However, color in CSS can be a bit hard to fully understand since there are many ways to set the same color, and sometimes they even look the same, but underneath are completely different technologies. That’s why, in this guide, we will walk through all the ways you can set up colors in CSS and all the color-related properties out there!
They are in your phone, in what your eye sees, and on any screen you look at; they essentially capture everything. Design-wise, I see the amazing use of colors on sites listed over at awwwards.com, and I’m always in awe.
Not all color is the same. In fact, similar colors can live in different worlds, known as color spaces. Take for example, sRGB, the color space used on the web for the better part of its existence and hence the most known. While it’s the most used, there are many colors that are simply missing in sRGB that new color spaces like CIELAB and Oklab bring, and they cover a wider range of colors sRGB could only dream of.
A color space is the way we arrange and represent colors that exist within a device, like printers and monitors. We have different types of color spaces that exist in media (Rec2020, Adobe RGB, etc), but not all of them are covered in CSS. Luckily, the ones we have are sufficient to produce all the awesome and beautiful colors we need. In this guide, we will be diving into the three main color spaces available in CSS: sRGB, CIELAB, and OkLab.
The sRGB is one of the first color spaces we learn. Inside, there are three color functions, which are essentially notations to define a color: rgb(), hsl(), and hwb().
sRGB has been a standard color space for the web since-1996. However, it’s closer to how old computers represented color, rather than how humans understand it, so it had some problems like not being able to capture the full gamut of modern screens. Still, many modern applications and websites use sRGB, so even though it is the “old way” of doing things, it is still widely accepted and used today.
.element {
color: #abc;
}
In reality, each value in the 3-digit system is duplicated and then translated to a visible color
.element {
color: #abc; /* Equals #AABBCC */
}
BUT, this severely limits the colors you can set. What if I want to target the color 213 in the red space, or how would I get a blue of value 103? It’s impossible. That’s why you can only get a total number of 4,096 colors here as opposed to the 17 million in the 6-digit notation. Still, if you want a fast way of getting a certain color in hexadecimal without having to worry about the millions of other colors, use the 3-digit notation.
.element {
color: #abcd; /* Same as #AABBCCDD */
}
For the alpha value, 0 represents 00 (a fully transparent color) and F represents FF (a fully opaque color).
.element {
color: #abcdef;
}
.element {
color: #faded101;
}
Both hsl() and rgb() live in the sRGB space, but they access colors differently. And while the consensus is that hsl() is far more intuitive than rgb(), it all boils down to your preference.
hsl() takes three values: h, s, and l, which set its hue, saturation, and lightness, respectively.
0deg to 360deg.0 (or 0%) to 100 (or 100%).One cool thing: the hue angle goes from (0deg–360deg), but we might as well use negative angles or angles above 360deg, and they will circle back to the right hue. Especially useful for infinite color animation. Pretty neat, right?
Plus, you can easily get a complementary color from the opposite angle (i.e., adding 180deg to the current hue) on the color wheel.
/* Current color */
.element {
color: hsl(120deg 40 60 / 0.8);
}
/* Complementary color */
.element {
color: hsl(300deg 40 60 / 0.8);
}
You want to combine the two syntax formats like in rgb(), yes? That’s also a no-no. It won’t work.
/* This would not work */
.element {
color: hsl(130deg, 50, 20 / 0.5);
}
/* Neither will this */
.element {
color: hsl(130deg 50 20, 0.5);
}
/* Or this */
.element {
color: hsl(130deg 50, 20 / 0.5);
}
Instead, stick to one of the syntaxes, like in rgb():
/* Valid (Modern syntax) */
.element {
color: hsl(130deg 50 20 / 0.5);
}
/* Valid (Modern syntax) */
.element {
color: hsl(130deg, 50, 20, 0.5);
}
hwb() also uses hue for its first value, but instead takes two values for whiteness and blackness to determine how your colors will come out (and yes, it also does have an optional transparency value, a, just like rgb() and hsl()).
.element {
color: hwb(80deg 20 50 / 0.5);
}
h is the same as the hue angle in hsl(), which represents the color position in the color wheel from 0 (or 0deg) to 360 (or 360deg).w, represents the whiteness in the color. It ranges from 0/0% (no white) to 100/100% (full white if b is 0).b, represents the blackness in the color. It ranges from 0/0% (no black) to 100/100% (fully black if w is 0).a, for the color’s opacity, preceded by a forward slash The value’s range is from 0.0 (or 0%) to 1.0 (or 100%).Although this color function is barely-used, it’s completely valid to use, so it’s up to personal preference.
The CIELAB color space is a relatively new color space on the web that represents a wider color gamut, closer to what the human eye can see, so it holds a lot more color than the sRGB space.
For this color function, we have three axes in a space-separated list to determine how the color is set.
.element {
color: lab(50 20 20 / 0.9);
}
l represents the degree of whiteness to blackness of the color. Its range being 0/(or 0%) (black) to 100 (or 100%) (white).a represents the degree of greenness to redness of the color. Its range being from -125/0% (green) to125 (or 100%) (red).b represents the degree of blueness to yellowness of the color. Its range is also from -125 (or 0%) (blue) to 125 (or 100%) (red).0.0 (or 0%) to 1.0 (or 100%).This is useful when you’re trying to obtain new colors and provide support for screens that do support them. Actually, most screens and all major browsers now support lab(), so you should be good.
The CSS
lab()color function’saandbvalues are actually unbounded. Meaning they don’t technically have an upper or lower limit. But, at practice, those are their limits according to the specifications.
The CSS lch() color function is said to be better and more intuitive than lab().
.element {
color: lch(10 30 300deg);
}
They both use the same color space, but instead of having l, a, and b, lch uses lightness, chroma, and hue.
l represents the degree of whiteness to blackness of the color. Its range being 0 (or 0%) (black) to 100 (or 100%) (white).c represents the color’s chroma (which is like saturation). Its range being from 0 (or 100%) to 150 or (or 100%).h represents the color hue. The value’s range is also from 0 (or 0deg) to 360 (or 360deg).0.0 (or 0%) to 1.0 (or 100%).The CSS
lch()color function’s chroma (c) value is actually unbounded. Meaning it doesn’t technically have an upper or lower limit. But, in practice, the chroma values above are the limits according to the specifications.
The OkLAB Color Space
Björn Ottosson created this color space as an “OK” and even better version of the lab color space. It was created to solve the limitations of CIELAB and CIELAB color space like image processing in lab(), such as making an image grayscale, and perceptual uniformity. The two color functions in CSS that correspond to this color space are oklab() and oklch().
Perceptual uniformity occurs when there’s a smooth change in the direction of a gradient color from one point to another. If you notice stark contrasts like the example below for rgb() when transitioning from one hue to another, that is referred to as a non-uniform perceptual colormap.
Notice how the change from one color to another is the same in oklab() without any stark contrasts as opposed to rgb()? Yeah, OKLab color space solves the stark contrasts present and gives you access to many more colors not present in sRGB.
OKlab actually provides a better saturation of colors while still maintaining the hue and lightness present in colors in CIELAB (and even a smoother transition between colors!).
The oklab() color function, just like lab(), generates colors according to their lightness, red/green axis, blue/yellow axis, and an alpha value for color opacity. Also, the values for oklab() are different from that of lab() so please watch out for that.
.element {
color: oklab(30% 20% 10% / 0.9);
}
l represents the degree of whiteness to blackness of the color. Its range being 0 (or 0%) (black) to 0.1 (or 100%) (white).a represents the degree of greenness to redness of the color. Its range being from -0.4 (or -100%) (green) to 0.4 (or 100%) (red).b represents the degree of blueness to yellowness of the color. The value’s range is also from -0.4 (or 0%) (blue) to 0.4 (or -100%) (red).0.0 (or 0%) to 1.0 (or 100%).Again, this solves one of the issues in lab which is perceptual uniformity so if you’re looking to use a better alternative to lab, use oklab().
The CSS
oklab()color function’saandbvalues are actually unbounded. Meaning they don’t technically have an upper or lower limit. But, theoretically, those are the limits for the values according to the specifications.
The oklch() color function, just like lch(), generates colors according to their lightness, chroma, hue, and an alpha value for color opacity. The main difference here is that it solves the issues present in lab() and lch().
.element {
color: oklch(40% 20% 100deg / 0.7);
}
l represents the degree of whiteness to blackness of the color. Its range being 0.0 (or 0%) (black) to 1.0 (or 100%) (white).c represents the color’s chroma. Its range being from 0 (or 0%) to 0.4 (or 100%) (it theoretically doesn’t exceed 0.5).h represents the color hue. The value’s range is also from 0 (or 0deg) to 360 (or 360deg).0.0 (or 0%) to 1.0 (or 100%).The CSS
oklch()color function’s chroma (c) value is actually unbounded. Meaning it doesn’t technically have an upper or lower limit. But, theoretically, the chroma values above are the limits according to the specifications.
The color(colorspace c1 c2 c2/α) function allows access to colors in nine different color spaces, as opposed to the previous color functions mentioned, which only allow access to one.
To use this function, you must simply be aware of these 6 parameters:
color space you want to access colors from. They can either be srgb, srgb-linear, display-p3, a98-rgb, prophoto-rgb, rec2020, xyz, xyz-d50, or xyz-d65c1, c2, and c3) specifies the coordinates in the color space for the color ranging from 0.0 – 1.0.0.0 (or 0%) to 1.0 (or 100%).The color-mix(in colorspace, c1 p1, c2 p2) function mixes two colors of any type in a given color space. Basically, you can create an endless number of colors with this method and explore more options than you normally would with any other color function. A pretty powerful CSS function, I would say.
.element {
color-mix(in oklab, hsl(40 20 60) 80%, red 20%);
}
You’re basically mixing two colors of any type in a color space. Do take note, the accepted color spaces here are different from the color spaces accepted in the color() function.
To use this function, you must be aware of these three values:
in colorspace specifies the interpolation method used to mix the colors, and these can be any of these 15 color spaces: srgb, srgb-linear, display-p3, a98-rgb, prophoto-rgb, rec2020, lab, oklab, xyz, xyz-d50, xyz-d65, hsl, hwb, lch, and oklch.0% to 100%.There are a lot of properties that support the use of color. Just so you know, this list does not contain deprecated properties.
columns and column-rule-style property first before using this;<feFlood> and <feDropShadow> elements inside the <filter> element for <svg>. This should not be confused with the flood-color CSS attribute, as this is a CSS property and that’s an HTML attribute (even though they basically do the same thing). If this property is specified, it overrides the CSS flood-color attribute;<feDiffuseLighting> and <feSpecularLighting> elements inside the <filter> element for <svg>;<stop> tags for <svg>;<svg>;Here, we get into the details of how thses colors systems renders colous which are functions in-built inside the CSS3-Specifications and I'm going to discuss all in details as under:
- The rgb() Function: Let's dive in to this function and finds everything about this function, the arguments used inside this functions and other useful details; rgb(r, g, b / a) where, r --> Red, g --> Green, b --> Blue and α --> Alpha; The Range of r is in-between 0-255, g is also in-between 0-255 and b is also in-between 0-255, moreover the range of a (alpha) is in-between 0-1. First three arguments used to provide the three primary colors RED, GREEN and BLUE whose composition is decided by the ranges as dicussed earlier and the last argument is for opacity where 0 (or 0%) for complete-transparent and 1(or 100%) for complete-opaque.
Ex- .element { color: rgb(245 123 151); }; .element { color: rgb(245 123 151 / 20%); }
- The rgba() Function: Let's dive in to this function and finds everything about this function, the arguments used inside this functions and other useful details; rgb(r, g, b / α) where, r --> Red, g --> Green, b --> Blue and α --> Alpha; The Range of r is in-between 0-255, g is also in-between 0-255 and b is also in-between 0-255, moreover the range of a (alpha) is in-between 0-1. First three arguments used to provide the three primary colors RED, GREEN and BLUE whose composition is decided by the ranges as dicussed earlier and the last argument is for opacity where 0 (or 0%) for complete-transparent and 1(or 100%) for complete-opaque. Both are same in todays scenario.
Ex- .element { color: rgb(245 123 151); }; .element { color: rgb(245 123 151 / 20%); }
- The hexadecimal notation: The hexadecimal CSS color code is a 3, 4, 6, or 8 (being the maximum) digit code for colors in sRGB. It’s basically a shorter way of writing
rgb(). The hexadecimal color (or hex color) begins with a hash token (#) and then a hexadecimal number, which means it goes from 0 to 9 and then skips to letters a to f (a being 10, b being 11, and so on, up to f for 15). In the hexadecimal color system, the 6-digit style is done in pairs. Each pair represents red (RR), blue (BB), and green (GG). Each value in the pair can go from 00 to FF, which it’s equivalent to 255 in rgb().Notice how I used caps for the letters (F) and not lowercase letters like I did previously? Well, that’s because hexadecimals are not case-sensitive in CSS, so you don’t have to worry about uppercase or lowercase letters when dealing with hexadecimal colors.
- The hsl() function: The hsl(h s l/α) is also very useful function used in the WEB-DESIGNING field having FOUR-ARGUMENTS with the ranges as under: h --> Hue (range is in-between 0 degree to 360 degrees), s --> Saturation (range is in-between 0(0%) to 100(100%)), and l --> Lightness (range is in-between 0 to 100), moreover the range of α(alpha) is in-between 0-1 which caters the tranparence <---> Opaquecity of colors.
- The hsla() function: The hsla(h s l/α) is also very useful function used in the WEB-DESIGNING field having FOUR-ARGUMENTS with the ranges as under: h --> Hue (range is in-between 0 degree to 360 degrees), s --> Saturation (range is in-between 0(0%) to 100(100%)), and l --> Lightness (range is in-between 0 to 100), moreover the range of α(alpha) is in-between 0-1 which caters the tranparence <---> Opaquecity of colors.
- The hwb() function: The hwb(h w b/α) is also very useful function used in the WEB-DESIGNING field having FOUR-ARGUMENTS with the ranges as under: h --> Hue (range is in-between 0 degree to 360 degrees), w --> Whiteness (range is in-between 0 to 100), and b --> Blackness(range is in-between 0 to 100), moreover the range of α(alpha) is in-between 0-1 which caters the tranparence <---> Opaquecity of colors.
- The lab() function: The lab(l a b/α) is also very useful function used in the WEB-DESIGNING field having FOUR-ARGUMENTS with the ranges as under: l --> degree of whiteness to darkness (range is in-between 0(0%) to 100(100%)), a --> degree of greenness to redness(range is in-between -125(0% - 'green') to 125(100% - 'red')) of color, and b --> to degree of blueness to yellowness(range is in-between -125(0% - 'blue') to 125(100% - 'yellow')) of color, moreover the range of α(alpha) is in-between 0-1 which caters the tranparence <---> Opaquecity of colors.
- The lch() function: The lch(l c h/α) is also very useful function used in the WEB-DESIGNING field having FOUR-ARGUMENTS with the ranges as under: l --> degree of whiteness to blackness (range is in-between 0(0%) to 100(100%)), c --> color (range is in-between 0(0%) to 150(100%)) chroma, and h --> Hue (range is in-between 0 degree to 360 degrees), moreover the range of α(alpha) is in-between 0-1 which caters the tranparence <---> Opaquecity of colors.
- The oklab() function: The oklab(l a b/α) is also very useful function used in the WEB-DESIGNING field having FOUR-ARGUMENTS with the ranges as under: l --> degree of whiteness to darkness (range is in-between 0(0%) to 100(100%)), a --> degree of greenness to redness(range is in-between -0.4(0% - 'green') to 0.4(100% - 'red')) of color, and b --> to degree of blueness to yellowness(range is in-between -0.4(0% - 'blue') to 0.4(100% - 'yellow')) of color, moreover the range of α(alpha) is in-between 0-1 which caters the tranparence <---> Opaquecity of colors.
- The oklch() function: The oklch(l c h/α) is also very useful function used in the WEB-DESIGNING field having FOUR-ARGUMENTS with the ranges as under: l --> degree of whiteness to blackness (range is in-between 0(0%) to 1(100%)), c --> color (range is in-between 0(0%) to 0.4(100%)) chroma, and h --> Hue (range is in-between 0 degree to 360 degrees), moreover the range of α(alpha) is in-between 0-1 which caters the tranparence <---> Opaquecity of colors.
- The color() function: The color(colorspace c1 c2 c3/α) is also very useful function used in the WEB-DESIGNING field having FIVE-ARGUMENTS with the ranges as under: colorspace --> NINE-ACCEPTED-COLOR-SPACES you can used her from amongst (srgb, srgb-linear, display-p3, α98-rgb, prophoto-rgb, rec2020, xyz, xyz-d50 or xyz-d65) colorspaces; c1 --> first color-space (range is in-between 0.0(0%) to 1.0(100%)) coordinate, c2 --> second color-space (range is in-between 0.0(0%) to 1.0(100%)) coordinate, c3 --> thirdcolor-space (range is in-between 0.0(0%) to 1.0(100%)) coordinate and moreover, the sixth argument α(alpha) is in-between 0.0(0%)-1.0(100%) which caters the tranparence <---> Opaquecity of colors.
- The color-mix() function: The color-mix(in colorspace, c1 p1, c2 p2) is also very useful function used in the WEB-DESIGNING field having THREE-ARGUMENTS with the ranges as under: in --> mandatory-keyword specifies the interpolation method, colorspace --> FIFTEEN-ACCEPTED-COLOR-SPACES you can used her from amongst (srgb, srgb-linear, display-p3, α98-rgb, prophoto-rgb, rec2020, xyz, xyz-d50, xyz-d65, lab, oklab, hsl, hwb, lch or oklch) colorspaces and moreover argument(s) c1 p1 and c2 p2 can be used here as c1 - c2 --> denotes any valid color input and p1 - p2 --> denotes color amount (0% - 100%).
- The color-function() function: The color-function(from origin-color c1 c2 c3/α) is also very useful function used in the WEB-DESIGNING field having SIX-ARGUMENTS with the ranges as under: from --> mandatory-keyword specifies you must set to extract the color values from
origin-color, origin-color--> represents a color function or value or even another relative color that you want to get color from, c1, c2 & c2represent the current color function’s color channels and they correspond with the color function’s valid color values and moreover the sixth argument α(alpha) is in-between 0.0(0%)-1.0(100%) which caters the tranparence <---> Opaquecity of colors.
- .progress { accent-color: lightgreen; }.
- .element { background-color: #ff7a18; }
- /* Sets all border colors */ .element { border-color: lch(50 50 20); } /* Sets top, right, bottom, left border colors */ .element { border-color: black green red blue; }
- .element { box-shadow: 0 3px 10px rgb(0 0 0 / 0.2); }
- .element { caret-color: lch(30 40 40); }
- .element { color: lch(80 10 20); }
- .element { column: 3; column-rule-style: solid; column-rule-color: lch(20 40 40); /* highlight */ }
- .element { fill: lch(40 20 10); }
- .element { flood-color: lch(20 40 40); }
- .element { lighting-color: lch(40 10 20); }
- .element { outline-color: lch(20 40 40); }
- .element { stop-color: lch(20 40 40); }
- .element { stroke: lch(20 40 40); }
- .element { text-decoration-color: lch(20 40 40); }
- .element { text-emphasis-color: lch(70 20 40); }
- .element { text-shadow: 1px 1px 1px lch(50 10 30); }