If you have ever opened a design file, copied #3B82F6, and wondered why the same blue also shows up as rgb(59, 130, 246) and hsl(217, 91%, 60%), you have run into the three color formats that every web designer and front-end developer eventually has to be fluent in. They all describe the exact same pixel. They just describe it in different languages, and each language is good at a different job.
I have shipped design systems where picking the wrong format cost hours. Hand a developer a wall of hex codes for a button's hover state and you get guesswork. Hand them an HSL scale and the relationship between the colors is obvious. So this is less a dry spec dump and more the working mental model I wish someone had given me early: what each format actually encodes, and when to reach for it.
HEX: the format you copy, not the one you reason in
A hex color code is just RGB wearing a different outfit. #RRGGBB is six hexadecimal digits split into three pairs — red, green, blue — and each pair is one byte. Hexadecimal (base 16) runs 0 through 9 then A through F, so two hex digits cover 00 to FF, which is 0 to 255 in decimal. That is exactly one 8-bit channel per color. Three channels, three bytes, and the familiar 16,777,216 possible colors (256 × 256 × 256).
That is the whole reason it is six digits: one byte each for R, G, and B. Take #3B82F6. Split it: 3B, 82, F6. Convert each pair from hex to decimal — 3B is 59, 82 is 130, F6 is 246 — and you have just done a manual convert hex to RGB. It is rgb(59, 130, 246). Same color, no magic.
There is a shorthand: #RGB expands by doubling each digit, so #F00 becomes #FF0000 (pure red) and #0AF becomes #00AAFF. It only works when each channel happens to be a repeated digit, which is why you mostly see it for round values like #FFF, #000, or #333. Handy in handwritten CSS, useless for the arbitrary blues a brand actually ships.
Hex also handles transparency, which surprises people. CSS Color Module Level 4 added eight-digit hex, #RRGGBBAA, where the last pair is alpha: 00 fully transparent, FF fully opaque. So #3B82F680 is that same blue at roughly 50% opacity. There is a four-digit shorthand too (#RGBA), where #3B8 style rules apply and each digit doubles. It is supported in every current major browser; only ancient Internet Explorer misses it, in which case rgba() is the fallback.
Hex's strength is that it is compact and unambiguous — one token, easy to copy, easy to paste into a design tool or a CSS variable. Its weakness is that it tells your brain almost nothing. Is #3B82F6 lighter or darker than #2563EB? You genuinely cannot tell by reading them. That is the cliff hex falls off.
RGB: how the screen actually thinks
RGB is the additive color model your monitor runs on. Every pixel is three tiny lights — red, green, blue — and the values say how bright each light is, from 0 to 255. All three at 0 is black (every light off). All three at 255 is white (every light blasting). Crank red and green, leave blue off, and you get yellow, which feels backwards if you grew up mixing paint, where red and green make mud. Screens add light; paint subtracts it. That is the core idea of additive color, and it is worth holding onto because it explains a lot of "why is this color doing that" moments.
In CSS you write rgb(59 130 246) (modern space-separated syntax) or rgb(59, 130, 246) (legacy commas); both are fine. Add alpha with a slash — rgb(59 130 246 / 50%) — which has quietly replaced the old separate rgba() function, though rgba() still works everywhere.
RGB is more readable than hex because the numbers are decimal and the channels are explicit. If you see rgb(250, 250, 250) you immediately know it is a near-white, because all three are pegged high. But it shares hex's real flaw: it is terrible for adjusting a color. To make rgb(59, 130, 246) 10% darker, you cannot just nudge one number — you have to move all three channels in concert, and getting the ratios right by hand is a fool's errand. Which brings us to the format that actually solves this.
HSL: the one you should reason in
HSL — hue, saturation, lightness — is the format I do almost all my thinking in, and it is the one the color palette generator shows for every color it produces, alongside RGB. Instead of describing a color by how much of three lights to mix, it describes it the way a human would.
- Hue is an angle on the color wheel, 0 to 360 degrees. 0 (and 360) is red, 120 is green, 240 is blue. Rotate the angle and you walk around the rainbow.
- Saturation is how vivid the color is, 0% to 100%. At 0% you get gray regardless of hue; at 100% it is as intense as the screen allows.
- Lightness is how light or dark, 0% to 100%. 0% is always black, 100% is always white, and 50% is the "pure" version of the hue.
Our blue is hsl(217, 91%, 60%). Now watch what HSL makes trivial. Want a darker shade for a pressed-button state? Drop the lightness: hsl(217, 91%, 45%). A lighter tint for a hover background? Raise it: hsl(217, 91%, 92%). A muted, dustier version for a disabled control? Pull the saturation down: hsl(217, 40%, 60%). The hue never moved, so it unmistakably reads as the same blue family the whole time. Try doing any of that by editing RGB channels and you will understand why HSL exists.
This is also why HSL is the natural language for building scales and tints and shades. Lock the hue, march the lightness in steps — 95%, 85%, 70%, 55%, 40%, 25% — and you have a coherent ramp from a pale background tint to a deep text color, all provably related. That is the backbone of how I build scalable color tokens for a design system, and it is the mechanic behind tints, shades and tones. HSL even makes color harmony arithmetic: a complementary color is just your hue plus 180 degrees, a triad is three hues 120 degrees apart. If you want the deeper version of that, understanding color harmony in UI design walks through it.
CSS syntax mirrors the rest: hsl(217 91% 60%) or the legacy hsl(217, 91%, 60%), with alpha via hsl(217 91% 60% / 50%). The hue can carry a deg unit but does not need one. One honest caveat: HSL's lightness is not the same as perceived brightness. A fully saturated yellow at 50% lightness looks far brighter to your eye than a blue at the identical 50%. That is a limitation of the model, not a bug in your eyes, and it is exactly why a contrast checker exists rather than trusting the L number — more on that below.
So which one do you actually use?
In practice you use all three, for different reasons, and the trick is matching the format to the moment.
- HEX for storing and sharing a single color — brand guidelines, a CSS variable, pasting a value to a teammate. It is the lingua franca; everything reads it.
- RGB when you are working channel-by-channel or interfacing with code that expects numeric channels — canvas, image processing, or any time you need raw 0–255 values. Also when you want straightforward alpha and would rather not think in hex pairs.
- HSL the second you need to modify or relate colors: building hover and active states, generating a tint scale, finding a harmonious accent, or tuning a dark mode palette where you are constantly nudging lightness and dialing saturation back so colors don't vibrate on a dark background.
My actual workflow: I reason and explore in HSL, then ship the final values as hex variables. Pick the hue, find the lightness ramp, sanity-check vividness with saturation — then freeze each step as a hex token the rest of the team consumes.
One thing no format saves you from is contrast. None of HEX, RGB, or HSL tells you whether text will be legible on a background; that takes a real luminance calculation against the WCAG ratios. The official W3C contrast guidance asks for at least 4.5:1 for normal body text. This is why the generator pairs its HSL and RGB readouts with a built-in contrast checker — so you can adjust the lightness slider and watch the ratio update live, instead of shipping a pretty blue that nobody can read.
Once these three formats stop looking like cryptic strings and start looking like three views of one color — a copyable token, the screen's raw lights, and a human-friendly set of dials — you stop fighting your colors and start steering them. Open up the color palette generator, generate something, and watch how the HSL numbers move as you adjust a swatch. That live feedback is the fastest way to make any of this click.
Frequently Asked Questions
What is a hex color code, in plain terms?
A hex color code is a six-digit code like #3B82F6 that stores a color as three bytes — one each for red, green, and blue. Each pair of digits is a hexadecimal number from 00 to FF, which is 0 to 255 in decimal. So #3B82F6 is simply rgb(59, 130, 246) written more compactly. An optional eighth and seventh digit (#RRGGBBAA) adds an alpha channel for transparency.
How do I convert a hex code to RGB by hand?
Split the six digits into three pairs and convert each pair from hexadecimal to decimal. For #3B82F6: 3B is 59, 82 is 130, and F6 is 246, giving rgb(59, 130, 246). The shortcut for one digit is that A=10, B=11, up to F=15, and the first digit is multiplied by 16. Any color tool — including the palette generator — does this instantly and shows RGB next to every color.
RGB vs HSL — which should I use?
Use RGB (or hex) for storing and sharing a fixed color, since they map directly to how a screen mixes light. Use HSL whenever you need to adjust or relate colors: making a shade darker, building a tint scale, or finding a harmonious accent. HSL separates hue, saturation, and lightness into independent dials, so you can darken a color by lowering one number instead of juggling three RGB channels.
Why is HSL better for tints and shades?
Because lightness is its own independent value. Lock the hue and saturation, then step the lightness up for tints (lighter) and down for shades (darker), and every step provably belongs to the same color family. Doing the same in RGB means moving all three channels in the right ratio, which is impractical by hand. That is why HSL is the natural language for color scales and design-system tokens.
Can hex codes include transparency?
Yes. Eight-digit hex (#RRGGBBAA) adds an alpha channel as the final pair, where 00 is fully transparent and FF is fully opaque, so #3B82F680 is that blue at about 50% opacity. There is a four-digit shorthand (#RGBA) too. It works in all current major browsers; for very old environments like Internet Explorer, use rgba() as a fallback instead.
Want to experiment with colors?
Try our free color palette generator to find your perfect harmony — with a built-in WCAG contrast checker.
Open the Generator