Color Formats: Named, HEX, RGB, and HSL
Introduction
CSS can represent a color with a predefined name, hexadecimal digits, red-green-blue channels, or hue-saturation-lightness components. These formats often produce the same visible result, but they communicate the value differently. A named color is immediately readable, HEX is compact, RGB maps directly to screen-light channels, and HSL gives authors controls that resemble hue and tonal adjustment.
Choosing a format does not change the underlying importance of the color. A blue written as blue, #0000ff, rgb(0 0 255), or hsl(240 100% 50%) can represent the same sRGB color. What changes is how easily a developer can recognize, edit, calculate, document, and add transparency to it.
Production CSS also needs more than valid syntax. Colors must maintain contrast, survive light and dark themes, communicate states without relying on hue alone, and remain consistent across components. This tutorial explains the four common formats, their modern syntax, their relationship to one another, and the design-system practices that make color maintainable.
Why CSS Offers Multiple Color Formats
No notation is ideal for every task. Tutorials use familiar names because they keep examples short. Design tools frequently export HEX. APIs, JavaScript, and image tooling often expose RGB channels. HSL can make a hue family easier to discuss because hue, saturation, and lightness are separated.
The browser ultimately resolves a valid value into a color in a color space and paints it according to the property, compositing context, and display pipeline. The source notation is primarily an authoring choice. Teams should choose conventions that improve understanding and consistency rather than converting values only for appearance.
Named Colors
Named colors are predefined CSS keywords such as red, blue, black, white, orange, purple, and rebeccapurple. Names are ASCII case-insensitive, although lowercase is the common style.
.error-message {
color: red;
}
.demo-panel {
background-color: lightyellow;
border-color: darkslategray;
}
They are easy to read and excellent for teaching, prototypes, tests, and simple examples. Their weakness is precision. A product palette usually needs exact branded shades and semantic relationships that names cannot express clearly. Some names are also difficult to visualize without a color picker.
Named Colors Are Exact Values
A named color is not a vague instruction such as "choose any red." Each recognized keyword maps to a defined color. For example, red maps to full red in sRGB, while tomato maps to the same value commonly written as #ff6347.
This precision does not make named colors a complete design language. Names such as lightgray and gray do not form a systematically balanced scale, and similarly worded colors can have surprising relative brightness. Use them when the name itself improves communication, not as an improvised production palette.
The transparent Keyword
The keyword transparent represents a fully transparent color. It is often used for initial borders, backgrounds, and gradient stops where a painted color should contribute no visible opacity.
.button {
border: 2px solid transparent;
}
.button:hover {
border-color: currentColor;
}
Transparent does not mean the property is absent. A transparent border still occupies border space, which is useful for preventing hover-state movement. During color interpolation and compositing, the transparent color's underlying channel behavior can matter, so test gradients and animations rather than treating transparency as empty layout.
HEX Colors
HEX notation encodes red, green, and blue channels with hexadecimal digits. The six-digit form is #RRGGBB. Each pair ranges from 00, representing zero channel intensity, to ff, representing decimal 255.
.primary-button {
background-color: #2563eb;
color: #ffffff;
}
The value #ff0000 has maximum red and no green or blue. #00ff00 is full green, #0000ff is full blue, #000000 is black, and #ffffff is white. Uppercase and lowercase hexadecimal digits represent the same values; projects should choose one style for consistency.
How Hexadecimal Channels Work
Hexadecimal uses sixteen symbols: 0 through 9 and A through F. A two-digit channel has 256 possible values. The first digit represents groups of sixteen and the second represents units. For example, 80 equals 8 times 16 plus 0, or decimal 128. eb equals 14 times 16 plus 11, or decimal 235.
#25 63 eb
| | |
R G B
0x25 = 37
0x63 = 99
0xeb = 235
Therefore #2563eb is equivalent to rgb(37 99 235). Understanding this conversion is useful for debugging, but routine authoring does not require manual arithmetic because browser DevTools and design tools can convert formats.
Three-Digit Short HEX
The three-digit form #RGB expands by duplicating each digit. #fc0 becomes #ffcc00, and #fff becomes #ffffff. It is not a general compression algorithm; shortening is valid only when each full channel contains the same digit twice.
A value such as #2563eb cannot be represented exactly with three digits. Avoid replacing it with a visually similar shorthand unless changing the color is intentional.
Eight-Digit HEX and Alpha
The eight-digit form is #RRGGBBAA, where the final pair stores alpha. An alpha of 00 is fully transparent and ff is fully opaque. #ff000080 is red with alpha 128 out of 255, approximately 50.2 percent opacity.
.overlay {
background: #00000099;
}
.subtle-border {
border-color: #0f172a26;
}
Because hexadecimal alpha is not immediately readable to everyone, RGB or HSL with percentage alpha may communicate intent better. Eight-digit HEX is still useful when design tokens arrive in that form and the team understands the convention.
Four-Digit Short HEX
The shorthand #RGBA duplicates every digit, including alpha. #f008 expands to #ff000088. Since 88 is 136 out of 255, the result is approximately 53.3 percent opacity, not exactly 50 percent.
Four-digit shorthand is compact but easy to misread. Prefer clarity over saving four characters when alpha precision matters or when developers are unfamiliar with the notation.
Strengths and Limits of HEX
HEX is compact, widely recognized, and common in design handoff. It represents RGB channels exactly and supports alpha in modern CSS. It also works cleanly as a custom-property value.
Its weakness is human adjustment. Increasing brightness or changing saturation is not obvious from the digits, and alpha values require hexadecimal interpretation. HEX is an encoding of RGB, not a separate color model with different visual capabilities.
RGB Colors
RGB notation writes the red, green, and blue channels directly. Modern space-separated syntax uses rgb(255 0 0), while legacy comma syntax uses rgb(255, 0, 0). Both represent full red.
.red { color: rgb(255 0 0); }
.green { color: rgb(0 255 0); }
.blue { color: rgb(0 0 255); }
.white { color: rgb(255 255 255); }
.black { color: rgb(0 0 0); }
RGB aligns with additive light. Zero in all channels produces black, while maximum red, green, and blue together produce white. Intermediate combinations produce the familiar sRGB palette used by traditional HEX values.
RGB Percentages
Channels can also be expressed as percentages. rgb(100% 0% 0%) represents the same full red as rgb(255 0 0). Percentage channels can be useful when values are calculated conceptually as proportions, although integer channels remain more common in design handoff.
Keep channel styles consistent within a token set. Mixing percentages and numbers without a reason makes comparison harder even when the results are valid.
RGB Alpha Transparency
Modern syntax adds alpha after a slash. Alpha can be a number from zero to one or a percentage. The older rgba() comma syntax remains widely encountered, but modern CSS does not require a separate function name to express alpha.
.backdrop {
background: rgb(0 0 0 / 60%);
}
.highlight {
background: rgb(37 99 235 / 0.15);
}
/* Legacy equivalent form */
.legacy {
background: rgba(37, 99, 235, 0.15);
}
Alpha zero is fully transparent and one or 100 percent is fully opaque. The visible result depends on the backdrop because the source color is composited with colors behind it.
Why RGB Is Useful
RGB makes channel values explicit and maps naturally to digital image data, canvas APIs, and many JavaScript libraries. Alpha syntax is readable, particularly with percentages. It is a practical format for overlays, translucent borders, shadows, and programmatically generated colors.
RGB is less intuitive when a designer wants to preserve hue while creating lighter or darker variants. Changing channels independently can shift both hue and saturation. Tools can perform the conversion, but HSL may describe the authoring intention more clearly.
HSL Colors
HSL represents hue, saturation, and lightness. Hue is an angle around a color wheel. Saturation describes color intensity, and lightness describes the position between black and white within the HSL model.
.red { color: hsl(0 100% 50%); }
.green { color: hsl(120 100% 50%); }
.blue { color: hsl(240 100% 50%); }
Saturation and lightness require percentages. A saturation of zero produces an achromatic gray regardless of hue. A lightness of zero produces black, while 100 percent produces white. At those extremes, hue and saturation do not change the visible result.
Understanding Hue
Hue commonly appears as a degree value from zero through 360, with zero and 360 representing the same direction. Values wrap around, so 420 degrees corresponds to 60 degrees. CSS also accepts supported angle units such as turn, rad, and grad.
.yellow-a { color: hsl(60deg 100% 50%); }
.yellow-b { color: hsl(0.1667turn 100% 50%); }
Hue is not a percentage. Omitting the angle unit for a plain number is interpreted as degrees in this context, but writing deg can make teaching examples explicit.
Understanding Saturation
Saturation controls how colorful an HSL color is. At zero percent, the result is gray. Raising saturation increases the distance from that gray toward the most saturated color available at the selected hue and lightness.
.muted-blue { color: hsl(220 20% 50%); }
.vivid-blue { color: hsl(220 90% 50%); }
Saturation is not opacity and does not describe brightness by itself. A fully saturated color can still be very dark or very light depending on the lightness component.
Understanding Lightness
Lightness controls the HSL axis from black at zero percent to white at 100 percent. A mid-point of 50 percent exposes the strongest form of many fully saturated hues.
.blue-dark { color: hsl(220 90% 25%); }
.blue-base { color: hsl(220 90% 50%); }
.blue-light { color: hsl(220 90% 80%); }
Equal HSL lightness does not guarantee equal perceived brightness across hues. Human vision is more sensitive to some wavelengths, so accessibility contrast must be measured from the resulting colors rather than inferred from the lightness number.
HSL Alpha Transparency
HSL uses the same modern slash notation for alpha. Legacy hsla() syntax may appear in older stylesheets, but hsl() can include alpha directly.
.blue-wash {
background: hsl(220 90% 50% / 15%);
}
.blue-overlay {
background: hsl(220 90% 50% / 0.5);
}
Changing alpha does not change HSL lightness. It changes how strongly the color is composited over its backdrop. A 20-percent opaque dark blue over white may appear light, but that appearance comes from blending, not from a higher HSL lightness value.
Why HSL Is Useful for Authoring
HSL separates a recognizable hue from saturation and lightness, making manual variations easier to discuss. A team can keep hue near 220 while creating lighter surfaces and darker interaction states. This can be clearer than editing three RGB channels.
:root {
--blue-100: hsl(220 90% 92%);
--blue-500: hsl(220 90% 55%);
--blue-700: hsl(220 85% 38%);
--blue-900: hsl(220 70% 18%);
}
Changing HSL lightness by equal steps does not automatically create a perceptually uniform scale or accessible state set. Use visual review and contrast measurement rather than assuming mathematically tidy values produce balanced design.
The Same Color in Four Formats
| Format | Full red | Authoring idea |
|---|---|---|
| Named | red | Predefined readable keyword |
| HEX | #ff0000 | Compact hexadecimal RGB |
| RGB | rgb(255 0 0) | Direct channel values |
| HSL | hsl(0 100% 50%) | Hue, saturation, and lightness |
These declarations are equivalent for this example. Browser DevTools may display a computed value in a different notation from the authored source, but that conversion does not mean the color changed.
Transparency Across Formats
HEX, RGB, and HSL can all represent alpha. Approximately half-opaque red can be written as #ff000080, rgb(255 0 0 / 50%), or hsl(0 100% 50% / 50%). The HEX value uses 128/255, so it is slightly above exactly 50 percent, while the percentage forms express 50 percent directly.
Named colors do not generally accept an alpha modifier attached to the name. Use a numeric format or a color-processing feature when transparency is required.
Alpha Color Versus opacity
Alpha inside a color affects that one paint value. The opacity property composites the entire element after its content, background, borders, and descendants are rendered. This difference is critical for readable overlays.
.card-wrong {
opacity: 0.5;
/* Text and children also fade. */
}
.card-better {
background: rgb(255 255 255 / 50%);
/* Only the background color is translucent. */
}
Use element opacity when fading the complete rendered subtree is truly intended, such as a transition. Use color alpha when only a background, border, shadow, or text color needs transparency.
Alpha Compositing Depends on the Backdrop
A translucent value has no single final visible color independent of context. rgb(0 0 0 / 50%) appears as middle gray over white, darker over gray, and nearly black over black. Stacking multiple translucent layers changes the result again.
Contrast testing must therefore use the final composited foreground and background in the actual state. Checking only the source RGB channels before alpha is applied can produce an incorrect accessibility conclusion.
HEX and RGB Conversion
To convert HEX to RGB, split six digits into three pairs and convert each pair from base 16 to decimal. To convert RGB to HEX, convert each decimal channel from zero through 255 into a two-digit hexadecimal pair.
#ff6347
ff = 255
63 = 99
47 = 71
Equivalent: rgb(255 99 71)
Manual conversion is useful for learning and code-review verification. In normal work, let tested tools handle conversion to avoid transcription and rounding errors.
RGB and HSL Conversion
RGB and HSL describe the same sRGB color through different coordinates, so conversion requires formulas involving channel minimums, maximums, and differences. Conversions can introduce rounding in displayed values. Converting a color to HSL and back may produce nearby channel numbers even when the visual result is effectively unchanged.
Avoid repeated format conversion as a design workflow. Keep canonical tokens in one reviewed form and let developer tools display alternatives when needed.
Use currentColor for Coordinated Components
The currentColor keyword resolves to the element's computed color value. It lets borders, icons, outlines, and decorations follow text color without duplicating a literal.
.icon-link {
color: #2563eb;
border-block-end: 1px solid currentColor;
}
.icon-link svg {
fill: currentColor;
}
This improves theme and state consistency. Changing the component's text color updates dependent decorations automatically.
Use CSS Custom Properties for Color Tokens
Components should usually consume semantic custom properties rather than repeat literal colors. A name such as --color-action expresses purpose; --blue describes appearance and becomes misleading if a theme changes the action color.
:root {
--color-text: #111827;
--color-surface: #ffffff;
--color-action: #2563eb;
--color-danger: hsl(0 72% 45%);
--color-border: rgb(15 23 42 / 18%);
}
.button {
color: var(--color-surface);
background: var(--color-action);
}
Tokens can legitimately use different formats when each improves maintainability. Consistency of semantic meaning is more important than converting every literal to one notation.
Separate Primitive and Semantic Tokens
A larger design system may define primitive palette values such as blue-600 and gray-900, then map them to semantic roles such as action, text, surface, border, success, and danger. Components reference semantic roles, allowing themes to remap appearance without rewriting component CSS.
:root {
--blue-600: #2563eb;
--gray-900: #111827;
--color-action: var(--blue-600);
--color-text: var(--gray-900);
}
Not every small project needs two layers, but the distinction prevents visual names from leaking into component contracts as the product grows.
Light and Dark Theme Considerations
A dark theme is not created by inverting every HEX digit or lowering HSL lightness mechanically. Surfaces, text, borders, shadows, focus rings, images, and status colors need coordinated values and contrast testing.
:root {
--color-surface: #ffffff;
--color-text: #111827;
}
@media (prefers-color-scheme: dark) {
:root {
--color-surface: #111827;
--color-text: #f9fafb;
}
}
Semantic tokens make this change local. Components continue using surface and text roles rather than embedding assumptions about white and black.
Accessibility and Contrast
A syntactically valid color can still make content unreadable. Contrast depends on the final foreground and background colors, including alpha blending, gradients, images, hover states, disabled states, and themes. Test actual combinations against the accessibility requirements for the content and interface.
Do not infer contrast from HEX digit distance, RGB channel totals, or HSL lightness alone. Use a contrast tool based on the applicable standard and inspect the rendered state. Large text, normal text, non-text controls, and focus indicators can have different requirements.
Do Not Communicate Meaning With Color Alone
Red can reinforce an error, but the interface should also provide an icon, text, structure, or programmatic state. Green and red status indicators are difficult for many users to distinguish and may become indistinguishable in grayscale.
Color formats do not solve this issue. A perfectly selected HSL hue still needs redundant communication. Use labels such as "Failed" and "Passed," visible focus treatment, and semantic HTML or ARIA where needed.
State Colors Need a Complete System
A button needs normal, hover, focus, active, disabled, and sometimes loading colors. A form field needs normal, invalid, focused-invalid, disabled, and read-only states. Designing only a base color leads to ad hoc adjustments that may lose contrast.
Review states together on every supported theme. HSL can make authoring variations understandable, but each final value still needs testing. Avoid reducing disabled-state opacity so far that text becomes unreadable.
Real-World Button Example
A semantic token set separates component code from literal color choices. The hover token can be reviewed independently rather than generated blindly in the component.
:root {
--button-bg: hsl(220 90% 52%);
--button-bg-hover: hsl(220 90% 42%);
--button-text: #fff;
--focus-ring: hsl(45 100% 50%);
}
.button {
color: var(--button-text);
background: var(--button-bg);
}
.button:hover {
background: var(--button-bg-hover);
}
.button:focus-visible {
outline: 3px solid var(--focus-ring);
outline-offset: 2px;
}
Real-World Overlay and Border Examples
RGB percentage alpha is readable for a backdrop because the intent is direct: use black at 60 percent opacity. A subtle border can use a low-alpha text-like color, but its actual appearance changes with the surface behind it.
.dialog-backdrop {
background: rgb(0 0 0 / 60%);
}
.card {
border: 1px solid rgb(15 23 42 / 15%);
}
.card--dark {
border-color: rgb(255 255 255 / 18%);
}
Separate theme tokens are often more predictable than placing one translucent border over every possible background.
Common Syntax Mistakes
HEX accepts only digits zero through nine and letters A through F. #gg0000 is invalid. A shorthand is valid only with three or four digits; five- and seven-digit forms are invalid. In HSL, saturation and lightness require percentages, while hue is an angle or angle-like number.
/* Invalid */
color: #gg0000;
color: hsl(240 100 50);
/* Valid */
color: #ff0000;
color: hsl(240 100% 50%);
Do not mix comma-separated legacy channels with the modern slash form incorrectly. Browser DevTools usually marks invalid declarations and omits them from computed styles.
Common Conceptual Mistakes
Alpha is not HSL lightness. Saturation is not brightness. Element opacity is not the same as color alpha. HEX is not inherently more precise than RGB when both encode the same 8-bit sRGB channels. A named color is not approximate.
Another mistake is assuming that changing HSL lightness by ten percentage points guarantees an accessible hover color. Perceptual response and contrast are not uniform across hues. Measure the actual result.
Debugging Colors in DevTools
Browser color swatches open pickers that show channels, alpha, and often contrast information. Many tools can cycle among HEX, RGB, and HSL without changing the rendered color. Computed styles reveal the final value after custom properties, inheritance, cascade, and state rules.
Inspect the active pseudo-class when debugging hover or focus. Check parent opacity, blend modes, filters, overlays, and background images if a correct source value appears different on screen. The color literal may not be the only operation affecting the final pixels.
Choosing a Format for a Project
| Format | Useful for | Main caution |
|---|---|---|
| Named | Tutorials, demos, simple known values | Limited systematic palette control |
| HEX | Compact tokens and design handoff | Manual adjustment and alpha are less intuitive |
| RGB | Channels, overlays, programmatic values | Hue variations are harder to reason about |
| HSL | Human-readable hue and tonal authoring | Equal numeric steps are not perceptually uniform |
A project can standardize one preferred format without forbidding every exception. RGB alpha may describe an overlay more clearly even when opaque tokens use HEX. Semantic token names and reviewed results matter more than mechanical uniformity.
Modern Color Features Beyond These Formats
Modern CSS also includes formats and functions such as HWB, Lab, LCH, OKLab, OKLCH, color(), and relative or mixed color operations in supporting browsers. They can provide wider gamuts or more perceptually useful adjustments than HSL.
This page focuses on named, HEX, RGB, and HSL because they remain common and foundational. When adopting newer features, verify browser support, fallback strategy, gamut behavior, design-tool compatibility, and accessibility using the rendered result.
A Practical Color Workflow
Begin with the semantic roles the interface needs: text, muted text, surface, border, action, focus, success, warning, and danger. Choose reviewed values and record them as custom properties. Build themes by remapping roles rather than scattering replacements through components.
Then test every foreground-background pair, alpha layer, interaction state, and theme. Use DevTools to inspect computed colors and compositing context. Keep the source format that communicates intent clearly, and convert only when tooling or integration requires it.
Define semantic roles
-> choose reviewed color values
-> store them as tokens
-> map tokens to components
-> design every state
-> test contrast and themes
-> inspect computed rendering
-> document the convention
Fallbacks, Forced Colors, and Print
When a newer color feature is introduced, place a broadly supported declaration first and the enhanced value afterward. Browsers that do not understand the later declaration ignore it and retain the earlier valid color. This progressive pattern is more dependable than assuming every browser, embedded webview, email renderer, or document exporter supports the same color syntax.
High-contrast and forced-color modes may replace authored colors with system colors so controls and text remain usable. Do not fight that behavior by forcing decorative values everywhere. Test focus indicators, borders, icons, and status messages in forced colors, and use semantic structure so meaning survives when the original palette is unavailable.
Print can also change backgrounds or omit them according to browser settings. Essential information must remain understandable without a colored surface. Add print-specific rules where necessary, preserve readable text and borders, and avoid relying on a translucent overlay whose backdrop is absent on paper. These environments reinforce the central rule: color notation is an implementation detail, while readable hierarchy and redundant communication are product requirements.
Interview Questions and Answers
What do RGB and HSL stand for? RGB means red, green, and blue. HSL means hue, saturation, and lightness.
What does #ffffff represent? Maximum red, green, and blue channels, which produce white in sRGB. #000000 represents black.
Can HEX include transparency? Yes. Eight-digit HEX uses the last pair for alpha, and four-digit shorthand duplicates the alpha digit.
What is modern RGB alpha syntax? A slash separates alpha, as in rgb(255 0 0 / 50%).
Which format is easiest for manually adjusting lightness? HSL often makes that intention clearest, although final contrast and perceptual balance must still be tested.
What is the difference between color alpha and opacity? Color alpha affects one color value. Opacity composites the complete rendered element and its descendants.
Are HEX and RGB different color models? Traditional six-digit HEX is a compact hexadecimal encoding of RGB channels, so equivalent HEX and RGB values represent the same sRGB color.
Practical Review Checklist
- Choose the format that communicates authoring intent clearly.
- Keep formatting conventions consistent within a token set.
- Use semantic custom properties instead of repeated literals.
- Understand three-, four-, six-, and eight-digit HEX expansion.
- Use modern slash syntax when RGB or HSL needs alpha.
- Remember that transparent results depend on the backdrop.
- Use color alpha instead of opacity when children must stay opaque.
- Use currentColor when borders or icons should follow text.
- Test final contrast in every state and theme.
- Do not communicate status or interaction through color alone.
- Verify hover, focus, active, disabled, and error colors together.
- Inspect computed colors, parent opacity, filters, and overlays in DevTools.
Key Takeaway
Named colors, HEX, RGB, and HSL are different authoring representations of color. Names provide readable predefined keywords. HEX compactly encodes RGB channels. RGB exposes the red, green, and blue components directly. HSL separates hue, saturation, and lightness for human-oriented adjustment. Numeric formats can also include alpha transparency.
No format guarantees a good design or accessible contrast. Use semantic tokens, select notation that makes intent clear, test composited results, and provide complete state and theme palettes. The best color system is not the one with the shortest literals; it is the one whose meanings remain understandable and whose rendered combinations remain usable.