CSS3 & Responsive Design

Colors: Names, HEX, RGB, HSL & Alpha

25 min Lesson 12 of 60

Introduction to CSS Colors

Color is one of the most fundamental aspects of web design. It sets the mood, guides the user's eye, establishes visual hierarchy, and communicates brand identity. CSS provides multiple color systems, each with its own strengths and use cases. Understanding these systems deeply will allow you to choose colors precisely, create accessible designs, and build flexible color schemes that are easy to maintain. In this lesson, we will explore every color format available in CSS -- from simple named colors to advanced functions like color-mix() -- and learn how to use them effectively in real projects.

Every CSS property that accepts a color value -- color, background-color, border-color, box-shadow, text-shadow, outline-color, and many more -- can use any of the color formats we will discuss. This means you can freely mix and match formats across your stylesheet, choosing whichever is most convenient or readable for each situation.

Named Colors (Color Keywords)

CSS defines 148 named color keywords that you can use directly in your stylesheets. These are plain English words that the browser translates to specific color values. Named colors are the simplest way to specify a color and are great for prototyping, learning, and quick styling.

Common Named Colors

The most frequently used named colors include basic colors that everyone recognizes:

  • Basic colors: red, green, blue, yellow, orange, purple, pink, cyan, magenta, white, black
  • Grays: gray, darkgray, dimgray, lightgray, silver, gainsboro, whitesmoke
  • Blues: navy, darkblue, mediumblue, royalblue, cornflowerblue, dodgerblue, deepskyblue, skyblue, lightblue, steelblue, cadetblue, slateblue
  • Greens: darkgreen, forestgreen, seagreen, limegreen, springgreen, mediumseagreen, olivedrab, olive, teal
  • Reds: darkred, firebrick, crimson, indianred, tomato, orangered, coral, salmon, lightsalmon
  • Others: gold, khaki, plum, orchid, violet, turquoise, sienna, peru, chocolate, tan, wheat, linen, ivory, lavender, honeydew, mintcream, aliceblue

Example: Using Named Colors

/* Named colors in various properties */
body {
    background-color: whitesmoke;
    color: dimgray;
}

h1 {
    color: navy;
}

.alert-danger {
    background-color: mistyrose;
    border: 1px solid crimson;
    color: darkred;
}

.alert-success {
    background-color: honeydew;
    border: 1px solid mediumseagreen;
    color: darkgreen;
}

.btn-primary {
    background-color: dodgerblue;
    color: white;
    border: 2px solid royalblue;
}

a {
    color: steelblue;
}
a:hover {
    color: tomato;
}

Special Color Keywords

CSS also defines several special color keywords that serve specific purposes:

  • transparent -- A fully transparent color (equivalent to rgba(0, 0, 0, 0)). Commonly used for invisible borders, backgrounds, or as a gradient stop.
  • currentColor -- A dynamic keyword that inherits the current value of the element's color property. Extremely useful for creating components where decorative elements automatically match the text color.
  • inherit -- Not a color itself, but forces the property to inherit its value from the parent element.

Example: The Power of currentColor

/* currentColor makes borders, shadows, and decorations
   automatically match the text color */
.icon-button {
    color: #3498db;
    border: 2px solid currentColor;     /* Blue border */
    box-shadow: 0 2px 4px currentColor; /* Blue shadow */
}

.icon-button:hover {
    color: #e74c3c;
    /* Border and shadow automatically change to red! */
}

/* SVG icons inherit currentColor */
.icon-button svg {
    fill: currentColor; /* SVG fill matches text color */
}

/* Underline decoration uses currentColor by default */
.link {
    color: #2c3e50;
    text-decoration-color: currentColor;
}

/* Use currentColor for consistent component theming */
.badge {
    color: #8e44ad;
    background-color: transparent;
    border: 1px solid currentColor;
    /* Change the color property and everything updates */
}
Tip: The currentColor keyword is one of the most underused features in CSS. It allows you to create components where you only need to change the color property to retheme the entire element -- borders, shadows, SVG fills, and decorations all update automatically. This makes your components much easier to maintain and customize.

Hexadecimal (HEX) Colors

Hexadecimal color notation is the most widely used color format in CSS. It uses a hash symbol (#) followed by hexadecimal digits (0-9 and A-F) to represent the red, green, and blue channels of a color. Each channel has a value from 00 (no intensity) to FF (full intensity), giving you access to over 16 million possible colors.

6-Digit HEX (#RRGGBB)

The standard HEX format uses six digits -- two for each color channel. The first two digits represent red, the middle two represent green, and the last two represent blue.

Example: 6-Digit HEX Colors

/* Pure colors */
.pure-red    { color: #FF0000; } /* Red: FF, Green: 00, Blue: 00 */
.pure-green  { color: #00FF00; } /* Red: 00, Green: FF, Blue: 00 */
.pure-blue   { color: #0000FF; } /* Red: 00, Green: 00, Blue: FF */

/* Mixed colors */
.yellow      { color: #FFFF00; } /* Red + Green = Yellow */
.cyan        { color: #00FFFF; } /* Green + Blue = Cyan */
.magenta     { color: #FF00FF; } /* Red + Blue = Magenta */
.white       { color: #FFFFFF; } /* All channels maximum */
.black       { color: #000000; } /* All channels zero */

/* Common web colors */
.brand-blue  { color: #3498db; }
.brand-red   { color: #e74c3c; }
.dark-text   { color: #2c3e50; }
.light-bg    { color: #ecf0f1; }
.muted-text  { color: #7f8c8d; }

/* HEX is case-insensitive */
.same-color-1 { color: #FF6600; }
.same-color-2 { color: #ff6600; } /* Identical to above */

3-Digit HEX Shorthand (#RGB)

When both digits of each channel are the same (like #AABBCC), you can use the shorthand 3-digit format where each digit is doubled. This is a convenient abbreviation but only works for a limited subset of colors.

Example: 3-Digit HEX Shorthand

/* 3-digit shorthand and their 6-digit equivalents */
.white   { color: #FFF; }  /* Same as #FFFFFF */
.black   { color: #000; }  /* Same as #000000 */
.red     { color: #F00; }  /* Same as #FF0000 */
.green   { color: #0F0; }  /* Same as #00FF00 */
.blue    { color: #00F; }  /* Same as #0000FF */
.yellow  { color: #FF0; }  /* Same as #FFFF00 */
.cyan    { color: #0FF; }  /* Same as #00FFFF */
.gray    { color: #999; }  /* Same as #999999 */
.light   { color: #EEE; }  /* Same as #EEEEEE */
.dark    { color: #333; }  /* Same as #333333 */

/* Common shorthands used in everyday CSS */
body {
    color: #333;            /* Dark gray text */
    background-color: #FFF; /* White background */
}
.border {
    border: 1px solid #DDD; /* Light gray border */
}
.muted {
    color: #999;            /* Muted gray text */
}

8-Digit HEX with Alpha (#RRGGBBAA)

Modern CSS supports an 8-digit HEX format that adds two digits for the alpha (transparency) channel. The alpha channel ranges from 00 (fully transparent) to FF (fully opaque). There is also a 4-digit shorthand (#RGBA).

Example: 8-Digit and 4-Digit HEX with Alpha

/* 8-digit HEX with alpha */
.overlay-dark   { background-color: #000000CC; } /* Black at 80% opacity */
.overlay-light  { background-color: #FFFFFF80; } /* White at 50% opacity */
.brand-faded    { color: #3498db66; }            /* Blue at 40% opacity */
.border-subtle  { border-color: #00000019; }     /* Black at 10% opacity */

/* 4-digit HEX shorthand with alpha */
.overlay  { background-color: #0008; } /* #00000088 -- Black at ~53% */
.faded    { color: #FFF8; }            /* #FFFFFF88 -- White at ~53% */
.ghost    { color: #0003; }            /* #00000033 -- Black at 20% */

/* Common alpha values in HEX */
/* FF = 100% (fully opaque) */
/* CC = 80% */
/* 99 = 60% */
/* 80 = ~50% */
/* 66 = 40% */
/* 4D = 30% */
/* 33 = 20% */
/* 1A = 10% */
/* 00 = 0% (fully transparent) */

/* Practical use: subtle shadow */
.card {
    box-shadow: 0 4px 12px #0000001A; /* Black at 10% opacity */
}
Note: While 8-digit HEX is supported in all modern browsers, many developers still prefer rgba() for transparent colors because the alpha value is more intuitive as a decimal (0.5 for 50%) than as a hexadecimal pair (80 for ~50%). Choose whichever format your team finds most readable.

RGB and RGBA Colors

The rgb() function defines colors using the Red, Green, Blue color model. Each channel accepts a value from 0 to 255 (or 0% to 100%). The rgba() function adds an alpha channel for transparency, accepting a value from 0 (fully transparent) to 1 (fully opaque).

The rgb() Function

Example: RGB Colors

/* RGB with integer values (0-255) */
.pure-red   { color: rgb(255, 0, 0); }
.pure-green { color: rgb(0, 255, 0); }
.pure-blue  { color: rgb(0, 0, 255); }
.white      { color: rgb(255, 255, 255); }
.black      { color: rgb(0, 0, 0); }
.gray       { color: rgb(128, 128, 128); }

/* RGB with percentage values (0%-100%) */
.half-red   { color: rgb(50%, 0%, 0%); }
.full-white { color: rgb(100%, 100%, 100%); }

/* Common web colors in RGB */
.brand-blue { color: rgb(52, 152, 219); }   /* #3498db */
.brand-red  { color: rgb(231, 76, 60); }    /* #e74c3c */
.dark-text  { color: rgb(44, 62, 80); }     /* #2c3e50 */

The rgba() Function

The rgba() function is identical to rgb() but adds a fourth parameter for the alpha (opacity) channel. This is one of the most commonly used color functions in CSS because it gives you fine control over transparency.

Example: RGBA Colors with Transparency

/* Alpha values range from 0 (transparent) to 1 (opaque) */
.fully-opaque      { color: rgba(255, 0, 0, 1); }    /* Same as rgb(255,0,0) */
.half-transparent   { color: rgba(255, 0, 0, 0.5); }  /* 50% transparent red */
.mostly-transparent { color: rgba(255, 0, 0, 0.1); }  /* 90% transparent red */
.invisible          { color: rgba(255, 0, 0, 0); }    /* Fully transparent */

/* Common overlay patterns */
.dark-overlay {
    background-color: rgba(0, 0, 0, 0.6); /* 60% black overlay */
}
.light-overlay {
    background-color: rgba(255, 255, 255, 0.8); /* 80% white overlay */
}

/* Subtle shadows using RGBA */
.card {
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
.card:hover {
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
}

/* Semi-transparent borders */
.glass-card {
    border: 1px solid rgba(255, 255, 255, 0.2);
    background-color: rgba(255, 255, 255, 0.1);
}

/* Color-tinted overlays */
.blue-tint {
    background-color: rgba(52, 152, 219, 0.3);
}
.red-tint {
    background-color: rgba(231, 76, 60, 0.2);
}
Tip: When creating box shadows, always use rgba() with a low alpha value rather than a solid gray color. Shadows with rgba(0, 0, 0, 0.1) look more natural than #e0e0e0 because real shadows are transparent and blend with whatever is behind them. This is especially important when elements overlap or when the page has a non-white background.

Modern Space-Separated Syntax

Modern CSS allows a newer syntax for rgb() where values are separated by spaces instead of commas, and the alpha is added with a forward slash. This syntax also works with hsl(). Modern browsers fully support this syntax, and it is the recommended approach going forward.

Example: Modern RGB Syntax

/* Modern space-separated syntax */
.modern-red   { color: rgb(255 0 0); }
.modern-blue  { color: rgb(52 152 219); }

/* Alpha with slash separator */
.modern-alpha { color: rgb(255 0 0 / 0.5); }      /* 50% transparent red */
.modern-alpha2 { color: rgb(52 152 219 / 0.75); }  /* 75% opaque blue */

/* Percentage alpha */
.modern-alpha3 { color: rgb(255 0 0 / 50%); }     /* Same as 0.5 */

/* The modern syntax replaces both rgb() and rgba() */
/* These are all equivalent: */
.old-way   { color: rgba(52, 152, 219, 0.5); }
.new-way   { color: rgb(52 152 219 / 0.5); }
.new-way-2 { color: rgb(52 152 219 / 50%); }

/* You no longer need rgba() -- just use rgb() with the slash */
.overlay {
    background-color: rgb(0 0 0 / 0.6);
}
.shadow {
    box-shadow: 0 4px 12px rgb(0 0 0 / 0.15);
}
Note: The modern space-separated syntax makes rgba() and hsla() technically redundant -- rgb() and hsl() now accept an optional alpha with the slash syntax. However, the comma-separated rgba() syntax is still perfectly valid and widely used. Both syntaxes will continue to work. Choose the one your team prefers for consistency.

HSL and HSLA Colors

The hsl() function defines colors using the Hue, Saturation, Lightness model. Many designers prefer HSL because it maps more intuitively to how humans perceive and think about color. Instead of mixing abstract channel values, you choose a color (hue), decide how vivid it should be (saturation), and how light or dark (lightness).

Understanding the HSL Color Model

  • Hue (0-360): The color itself, represented as a degree on the color wheel. Think of a rainbow wrapped into a circle: 0 (and 360) is red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, and 300 is magenta. You are essentially picking a position on the color wheel.
  • Saturation (0%-100%): How vivid or intense the color is. 100% is the purest, most vivid version of the color. 0% removes all color, resulting in a shade of gray. Think of it as the distance from the center of the color wheel to the edge.
  • Lightness (0%-100%): How light or dark the color is. 0% is always black (regardless of hue or saturation). 100% is always white. 50% gives you the pure, fully saturated version of the hue. Values below 50% darken the color; values above 50% lighten it.

Example: The HSL Color Wheel

/* The color wheel: Hue values and their colors */
.red     { color: hsl(0, 100%, 50%); }    /* 0 degrees */
.orange  { color: hsl(30, 100%, 50%); }   /* 30 degrees */
.yellow  { color: hsl(60, 100%, 50%); }   /* 60 degrees */
.lime    { color: hsl(90, 100%, 50%); }   /* 90 degrees */
.green   { color: hsl(120, 100%, 50%); }  /* 120 degrees */
.teal    { color: hsl(150, 100%, 50%); }  /* 150 degrees */
.cyan    { color: hsl(180, 100%, 50%); }  /* 180 degrees */
.azure   { color: hsl(210, 100%, 50%); }  /* 210 degrees */
.blue    { color: hsl(240, 100%, 50%); }  /* 240 degrees */
.purple  { color: hsl(270, 100%, 50%); }  /* 270 degrees */
.magenta { color: hsl(300, 100%, 50%); }  /* 300 degrees */
.rose    { color: hsl(330, 100%, 50%); }  /* 330 degrees */

/* Saturation controls vividness */
.vivid   { color: hsl(210, 100%, 50%); } /* Pure blue */
.muted   { color: hsl(210, 50%, 50%); }  /* Muted blue */
.dull    { color: hsl(210, 25%, 50%); }  /* Dull blue */
.gray    { color: hsl(210, 0%, 50%); }   /* Pure gray (no color) */

/* Lightness controls brightness */
.black   { color: hsl(210, 100%, 0%); }  /* Always black */
.dark    { color: hsl(210, 100%, 25%); } /* Dark blue */
.medium  { color: hsl(210, 100%, 50%); } /* Pure blue */
.light   { color: hsl(210, 100%, 75%); } /* Light blue */
.white   { color: hsl(210, 100%, 100%); }/* Always white */

Why HSL is Powerful for Developers

The biggest advantage of HSL is that creating color variations is intuitive. To get a lighter version of a color, increase the lightness. To get a darker version, decrease it. To make a color more muted, decrease the saturation. This is far easier than guessing which RGB channel values to adjust.

Example: Creating Color Scales with HSL

/* A complete blue color scale by varying lightness */
:root {
    --blue-50:  hsl(210, 100%, 97%);  /* Lightest tint */
    --blue-100: hsl(210, 100%, 93%);
    --blue-200: hsl(210, 100%, 85%);
    --blue-300: hsl(210, 100%, 75%);
    --blue-400: hsl(210, 100%, 65%);
    --blue-500: hsl(210, 100%, 50%);  /* Base color */
    --blue-600: hsl(210, 100%, 42%);
    --blue-700: hsl(210, 100%, 34%);
    --blue-800: hsl(210, 100%, 26%);
    --blue-900: hsl(210, 100%, 18%);  /* Darkest shade */
}

/* Creating button states by adjusting lightness */
.btn-primary {
    background-color: hsl(210, 85%, 55%);
    color: white;
}
.btn-primary:hover {
    background-color: hsl(210, 85%, 45%); /* Darker on hover */
}
.btn-primary:active {
    background-color: hsl(210, 85%, 38%); /* Even darker on press */
}
.btn-primary:disabled {
    background-color: hsl(210, 30%, 70%); /* Desaturated and lighter */
}

/* Complementary colors: add 180 to the hue */
.primary    { color: hsl(210, 85%, 55%); } /* Blue */
.complement { color: hsl(30, 85%, 55%); }  /* Orange (210 + 180 = 390 = 30) */

/* Analogous colors: shift hue by 30 degrees */
.color-1 { color: hsl(180, 70%, 45%); } /* Teal */
.color-2 { color: hsl(210, 70%, 45%); } /* Blue */
.color-3 { color: hsl(240, 70%, 45%); } /* Indigo */
Tip: HSL makes color theory easy to apply in CSS. To find the complementary color, add 180 to the hue. For triadic colors, add 120 and 240. For analogous colors, shift the hue by 30 degrees in either direction. For split-complementary colors, add 150 and 210. These mathematical relationships are trivial with HSL but nearly impossible to reason about with HEX or RGB.

HSLA and Modern HSL Syntax

Example: HSLA and Modern Syntax

/* Classic HSLA syntax */
.overlay {
    background-color: hsla(0, 0%, 0%, 0.5); /* 50% black overlay */
}
.tinted {
    background-color: hsla(210, 100%, 50%, 0.2); /* 20% blue tint */
}

/* Modern space-separated syntax (recommended) */
.overlay-modern {
    background-color: hsl(0 0% 0% / 0.5);
}
.tinted-modern {
    background-color: hsl(210 100% 50% / 0.2);
}

/* Percentage alpha values */
.subtle {
    background-color: hsl(210 100% 50% / 10%);
}

/* These are all equivalent: */
.option-1 { color: hsla(210, 100%, 50%, 0.5); }  /* Classic */
.option-2 { color: hsl(210 100% 50% / 0.5); }    /* Modern with decimal */
.option-3 { color: hsl(210 100% 50% / 50%); }     /* Modern with percent */

Opacity vs Alpha Channel

There are two ways to make elements transparent in CSS: the opacity property and alpha channel colors. Understanding the critical difference between them is essential for building layered, professional designs.

The opacity Property

The opacity property sets the transparency of the entire element and all of its children. A value of 1 is fully opaque, and 0 is fully transparent. Everything inside the element -- text, borders, backgrounds, child elements -- becomes transparent together. You cannot make just the background transparent with opacity while keeping the text fully visible.

Alpha Channel Colors

Alpha channel colors (using rgba(), hsla(), or 8-digit HEX) make only the specific color property transparent. If you set a semi-transparent background color, the text remains fully opaque. This gives you much finer control.

Example: opacity vs Alpha Channel

/* PROBLEM: opacity affects EVERYTHING including text */
.overlay-bad {
    background-color: black;
    color: white;
    opacity: 0.5;
    /* Both the background AND the white text are 50% transparent */
    /* The text is hard to read! */
}

/* SOLUTION: Alpha channel only affects the background */
.overlay-good {
    background-color: rgba(0, 0, 0, 0.5);
    color: white;
    /* Only the background is 50% transparent */
    /* The white text stays fully opaque and readable! */
}

/* Another common mistake with opacity */
.card-faded {
    opacity: 0.6;
    /* Everything is faded: text, images, borders, shadows... */
}

/* Better: use alpha only where needed */
.card-subtle {
    background-color: rgba(255, 255, 255, 0.6); /* Faded background only */
    color: #333;                                  /* Full opacity text */
    border: 1px solid rgba(0, 0, 0, 0.1);        /* Subtle border */
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);   /* Subtle shadow */
}

/* opacity IS useful for hover/transition effects */
.fade-element {
    opacity: 1;
    transition: opacity 0.3s ease;
}
.fade-element:hover {
    opacity: 0.7; /* Entire element fades on hover */
}
Warning: The opacity property creates a new stacking context, which can affect z-index behavior of child elements. Alpha channel colors do not create stacking contexts. If you experience unexpected z-index issues with overlapping elements, check whether opacity is creating an unwanted stacking context.

Color Theory Essentials for Developers

You do not need to be a designer to apply basic color theory. Understanding a few key concepts will help you choose colors that work well together and create visually pleasing designs.

The Color Wheel and Color Relationships

  • Complementary: Colors opposite each other on the wheel (180 degrees apart). They create high contrast and visual tension. Example: blue (#3498db) and orange (#e67e22). Use complementary colors for call-to-action buttons against a primary color background.
  • Analogous: Colors next to each other on the wheel (30 degrees apart). They create harmonious, cohesive designs. Example: blue, blue-green, green. Use analogous colors for a calm, unified feel.
  • Triadic: Three colors equally spaced on the wheel (120 degrees apart). They create vibrant, balanced designs. Example: red, blue, yellow. Use triadic schemes carefully -- they can be overwhelming if all three colors are fully saturated.
  • Split-Complementary: A color plus the two colors adjacent to its complement. Less tension than pure complementary but still high contrast. Example: blue, red-orange, yellow-orange.
  • Monochromatic: Different shades, tints, and tones of a single hue. Always harmonious and professional. This is the safest color scheme for beginners.

Example: Building Color Schemes with HSL

/* Monochromatic scheme: single hue, vary saturation and lightness */
:root {
    --mono-lightest: hsl(220, 60%, 95%);
    --mono-light:    hsl(220, 60%, 80%);
    --mono-base:     hsl(220, 60%, 50%);
    --mono-dark:     hsl(220, 60%, 35%);
    --mono-darkest:  hsl(220, 60%, 20%);
}

/* Complementary scheme */
:root {
    --primary:     hsl(220, 70%, 50%);  /* Blue */
    --complement:  hsl(40, 70%, 50%);   /* Orange (220 + 180 = 400 - 360 = 40) */
    --primary-bg:  hsl(220, 70%, 96%);
    --complement-bg: hsl(40, 70%, 96%);
}

/* Analogous scheme */
:root {
    --color-a: hsl(200, 65%, 50%);  /* Cyan-blue */
    --color-b: hsl(220, 65%, 50%);  /* Blue (base) */
    --color-c: hsl(240, 65%, 50%);  /* Indigo */
}

/* Triadic scheme (use sparingly) */
:root {
    --tri-1: hsl(10, 70%, 55%);   /* Red-orange */
    --tri-2: hsl(130, 70%, 40%);  /* Green */
    --tri-3: hsl(250, 70%, 55%);  /* Blue-purple */
}

/* The 60-30-10 rule: allocate colors by proportion */
body {
    background-color: var(--mono-lightest); /* 60%: dominant/neutral */
    color: var(--mono-darkest);
}
.sidebar {
    background-color: var(--primary);       /* 30%: secondary color */
    color: white;
}
.cta-button {
    background-color: var(--complement);    /* 10%: accent color */
    color: white;
}

Accessible Color Contrast (WCAG)

Color contrast is not just an aesthetic choice -- it is an accessibility requirement. The Web Content Accessibility Guidelines (WCAG) define minimum contrast ratios that ensure text is readable by people with visual impairments, including color blindness and low vision. Failing to meet these standards can make your website unusable for millions of people and may violate legal requirements in many countries.

WCAG Contrast Levels

  • WCAG AA (minimum): Normal text must have a contrast ratio of at least 4.5:1 against its background. Large text (18px bold or 24px regular and above) requires at least 3:1. This is the standard most websites should meet at minimum.
  • WCAG AAA (enhanced): Normal text must have a contrast ratio of at least 7:1. Large text requires at least 4.5:1. This is the highest level of accessibility and is recommended for body text and critical content.
  • Non-text elements: UI components like buttons, form inputs, and focus indicators must have at least a 3:1 contrast ratio against adjacent colors.

Example: Accessible Color Choices

/* GOOD: High contrast combinations */
.accessible-dark {
    background-color: #ffffff;
    color: #1a1a2e;  /* Contrast ratio: ~16:1 (passes AAA) */
}

.accessible-blue {
    background-color: #ffffff;
    color: #1a5276;  /* Contrast ratio: ~8.5:1 (passes AAA) */
}

.accessible-on-dark {
    background-color: #1a1a2e;
    color: #e8e8e8;  /* Contrast ratio: ~13:1 (passes AAA) */
}

/* BAD: Low contrast combinations -- AVOID THESE */
.inaccessible-1 {
    background-color: #ffffff;
    color: #b0b0b0;  /* Contrast ratio: ~2.3:1 (FAILS AA and AAA) */
}

.inaccessible-2 {
    background-color: #3498db;
    color: #5dade2;  /* Contrast ratio: ~1.5:1 (FAILS everything) */
}

/* Fixing low contrast while keeping the color spirit */
.fixed-gray {
    background-color: #ffffff;
    color: #666666;  /* Contrast ratio: ~5.7:1 (passes AA) */
}

.fixed-blue-on-blue {
    background-color: #ebf5fb;
    color: #1a5276;  /* Contrast ratio: ~8:1 (passes AAA) */
}

/* Accessible link colors */
a {
    color: #1a5276;  /* Dark enough for white backgrounds */
}
a:hover {
    color: #0d3b57;  /* Even darker on hover for clear feedback */
}

/* Focus indicators need 3:1 contrast */
button:focus-visible {
    outline: 3px solid #1a5276;
    outline-offset: 2px;
}
Warning: Never rely on color alone to convey information. Approximately 8% of men and 0.5% of women have some form of color blindness. Always supplement color with additional indicators: icons, text labels, patterns, or underlines. For example, do not make "red text means error" your only error indicator -- add an error icon or text prefix like "Error:" as well.
Tip: Use browser developer tools to check color contrast. In Chrome DevTools, inspect an element and look at the color picker -- it shows the contrast ratio and whether it meets WCAG AA and AAA standards. You can also use online tools like the WebAIM Contrast Checker or the Accessible Colors tool to verify your color choices.

The color-mix() Function

The color-mix() function is a modern CSS feature that lets you mix two colors together in a specified proportion. It is incredibly useful for creating color variations, hover states, and dynamic theming without manually calculating intermediate colors.

Example: Using color-mix()

/* Basic syntax: color-mix(in colorspace, color1 percentage, color2 percentage) */

/* Mix 50% blue with 50% white (lighten) */
.light-blue {
    color: color-mix(in srgb, #3498db 50%, white 50%);
}

/* Mix 70% blue with 30% black (darken) */
.dark-blue {
    color: color-mix(in srgb, #3498db 70%, black 30%);
}

/* If percentages are omitted for one color, it gets the remainder */
.tinted {
    color: color-mix(in srgb, #3498db 80%, white);
    /* white gets the remaining 20% */
}

/* Mix two brand colors */
.blended {
    background-color: color-mix(in srgb, #3498db, #e74c3c);
    /* 50/50 mix by default */
}

/* Practical: hover states without hardcoding colors */
.btn {
    --btn-color: #3498db;
    background-color: var(--btn-color);
}
.btn:hover {
    background-color: color-mix(in srgb, var(--btn-color) 85%, black);
    /* Automatically 15% darker */
}
.btn:active {
    background-color: color-mix(in srgb, var(--btn-color) 70%, black);
    /* Automatically 30% darker */
}

/* Creating tints and shades from a single base color */
:root {
    --brand: #6366f1;
    --brand-light: color-mix(in srgb, var(--brand) 30%, white);
    --brand-lighter: color-mix(in srgb, var(--brand) 15%, white);
    --brand-dark: color-mix(in srgb, var(--brand) 70%, black);
    --brand-darker: color-mix(in srgb, var(--brand) 50%, black);
}
Note: The color-mix() function is supported in all modern browsers (Chrome 111+, Firefox 113+, Safari 16.2+). It is part of CSS Color Level 5. The in srgb parameter specifies the color space for mixing. For most web work, srgb is the standard choice. Other color spaces like oklch and lab provide perceptually uniform mixing but are more advanced.

Practical Color Scheme Examples

Let us put everything together and build complete color systems that you can use in real projects.

Example: Complete Color System with CSS Custom Properties

:root {
    /* Primary color and its variations */
    --primary-h: 220;
    --primary-s: 70%;
    --primary-l: 50%;
    --primary: hsl(var(--primary-h), var(--primary-s), var(--primary-l));
    --primary-light: hsl(var(--primary-h), var(--primary-s), 90%);
    --primary-lighter: hsl(var(--primary-h), var(--primary-s), 96%);
    --primary-dark: hsl(var(--primary-h), var(--primary-s), 35%);
    --primary-darker: hsl(var(--primary-h), var(--primary-s), 20%);

    /* Semantic colors */
    --success: hsl(145, 65%, 42%);
    --success-light: hsl(145, 65%, 92%);
    --warning: hsl(45, 95%, 50%);
    --warning-light: hsl(45, 95%, 92%);
    --danger: hsl(0, 75%, 55%);
    --danger-light: hsl(0, 75%, 94%);
    --info: hsl(200, 80%, 50%);
    --info-light: hsl(200, 80%, 93%);

    /* Neutral grays using a tinted gray approach */
    --gray-50:  hsl(220, 15%, 97%);
    --gray-100: hsl(220, 15%, 93%);
    --gray-200: hsl(220, 12%, 85%);
    --gray-300: hsl(220, 10%, 75%);
    --gray-400: hsl(220, 8%, 60%);
    --gray-500: hsl(220, 8%, 46%);
    --gray-600: hsl(220, 10%, 36%);
    --gray-700: hsl(220, 12%, 26%);
    --gray-800: hsl(220, 15%, 18%);
    --gray-900: hsl(220, 18%, 10%);

    /* Applied colors */
    --text-primary: var(--gray-900);
    --text-secondary: var(--gray-600);
    --text-muted: var(--gray-400);
    --bg-primary: #ffffff;
    --bg-secondary: var(--gray-50);
    --border-color: var(--gray-200);
}

/* Usage */
body {
    color: var(--text-primary);
    background-color: var(--bg-primary);
}

.card {
    background-color: var(--bg-primary);
    border: 1px solid var(--border-color);
}

.alert-success {
    background-color: var(--success-light);
    border: 1px solid var(--success);
    color: hsl(145, 65%, 25%);
}

.alert-danger {
    background-color: var(--danger-light);
    border: 1px solid var(--danger);
    color: hsl(0, 75%, 30%);
}

Example: Dark Mode with HSL Color System

/* Light mode (default) */
:root {
    --bg: hsl(0, 0%, 100%);
    --bg-secondary: hsl(220, 15%, 97%);
    --text: hsl(220, 18%, 12%);
    --text-secondary: hsl(220, 10%, 40%);
    --border: hsl(220, 15%, 88%);
    --shadow: hsl(220 15% 50% / 0.1);
    --accent: hsl(220, 75%, 55%);
    --accent-text: hsl(0, 0%, 100%);
}

/* Dark mode */
@media (prefers-color-scheme: dark) {
    :root {
        --bg: hsl(220, 18%, 10%);
        --bg-secondary: hsl(220, 15%, 14%);
        --text: hsl(220, 10%, 90%);
        --text-secondary: hsl(220, 10%, 60%);
        --border: hsl(220, 12%, 22%);
        --shadow: hsl(0 0% 0% / 0.3);
        --accent: hsl(220, 75%, 65%);
        --accent-text: hsl(220, 18%, 10%);
    }
}

/* Components use the same variables in both modes */
body {
    background-color: var(--bg);
    color: var(--text);
}

.card {
    background-color: var(--bg-secondary);
    border: 1px solid var(--border);
    box-shadow: 0 2px 8px var(--shadow);
}

.btn-accent {
    background-color: var(--accent);
    color: var(--accent-text);
}

Exercise 1: Color Format Practice

Create a color palette using different CSS color formats. Define the following colors using the format specified:

  1. A primary blue color using 6-digit HEX notation.
  2. The same blue with 50% transparency using RGBA.
  3. A complementary orange color using HSL (hint: add 180 to the blue hue).
  4. A hover state for the blue that is 15% darker, using color-mix().
  5. A light background tint of the blue using HSLA with 10% opacity.

Exercise 2: Accessible Color Scheme

Build a complete accessible color scheme for a website:

  1. Choose a primary brand color using HSL.
  2. Create 5 lightness variations (from very light to very dark) by only changing the lightness value.
  3. Define a complementary accent color using color wheel math (hue + 180).
  4. Verify that your text colors achieve at least 4.5:1 contrast against their backgrounds (WCAG AA).
  5. Create a success, warning, and danger color, each with a light background variant.

Exercise 3: Dark Mode Toggle

Create a color system using CSS custom properties that supports both light and dark modes:

  1. Define all colors as CSS custom properties in :root for light mode.
  2. Override those properties inside a @media (prefers-color-scheme: dark) query for dark mode.
  3. Use HSL so you can easily adjust lightness between modes.
  4. Include at minimum: background, text, secondary text, border, card background, accent color, and shadow color.
  5. Ensure dark mode text still meets WCAG AA contrast requirements against the dark backgrounds.

Exercise 4: Component Theming with currentColor

Create a reusable alert component that uses currentColor for automatic theming:

  1. The alert should have a left border, a light background, an icon area, and a text area.
  2. Use currentColor for the border color and any SVG icon fills.
  3. Use alpha-channel colors for the background (10% opacity of the text color).
  4. Create four variants (info, success, warning, danger) that only change the color property.
  5. Verify that each variant automatically updates its border, background tint, and icon color.

ES
Edrees Salih
21 hours ago

We are still cooking the magic in the way!