Colors & Units
Master every way CSS lets you describe color — from simple names to powerful HSL — and learn which unit (px, em, rem, %, vw/vh) to reach for in any situation.
Two questions come up in virtually every CSS rule you will ever write: what color is this? and how big is this? CSS gives you a surprisingly rich toolkit for both. You can describe a color as a human-readable name, a six-digit hex code, an RGB triplet, or a hue-saturation-lightness formula. For sizing, CSS offers absolute units like pixels and a family of relative units that scale with the screen, the user's font preferences, or the parent element. Understanding both well is the difference between a layout that breaks on a phone and one that feels right everywhere.
#Four Ways to Write a Color
Named colors — CSS knows 140+ words like coral, steelblue, and tomato. Great for learning; impossible to fine-tune.
Hex codes — The workhorse of the web. #3a86ff encodes red, green, and blue in pairs: 3a is red, 86 is green, ff is blue. Design tools always output hex, so you will copy these constantly.
RGB / RGBA — rgb(58, 134, 255) spells out the same channels in plain decimals (0–255). Add a fourth argument for transparency: rgba(0, 0, 0, 0.6) is 60% opaque black — the classic modal backdrop.
HSL — The most human-friendly format. Hue (0–360°, a color wheel position), Saturation (0% grey → 100% vivid), Lightness (0% black → 100% white). The killer feature: lock the hue and vary lightness to build an instant palette of related shades.
HSL: Think Like a Painter
RGB asks how much red, green, and blue light? — a question for a monitor engineer. HSL asks what color is it, how vivid is it, and how bright is it? — the questions a painter asks. When you want a button and its hover state to share the same hue but differ in darkness, HSL makes that trivial: just change the lightness value.
:root {
/* One hue, five shades — a full tonal palette */
--blue-50: hsl(217, 90%, 95%);
--blue-300: hsl(217, 80%, 65%);
--blue-500: hsl(217, 75%, 50%);
--blue-700: hsl(217, 70%, 35%);
--blue-900: hsl(217, 65%, 15%);
}
.modal-backdrop {
background-color: rgba(0, 0, 0, 0.6); /* semi-transparent overlay */
}
.tag {
background-color: var(--blue-50);
color: var(--blue-700);
border: 1px solid var(--blue-300);
}HSL Lightness 50% Is the Full Color, Not a Mid-Grey
The HSL lightness scale goes black (0%) → vivid color (50%) → white (100%). The middle point is actually the most saturated version of your hue. If you want a muted, washed-out shade, drop the saturation — not the lightness.
#Absolute vs. Relative Units
Absolute units are fixed. 24px means exactly 24 pixels, regardless of the screen size or the user's settings. Use px for things that should never flex: borders, box shadows, icon sizes.
Relative units are defined in relation to something else — and they are what make a design adapt to different screens and accessibility preferences.
- `rem` — relative to the root (
<html>) font size. Browser default is 16px, so1rem = 16px. Think ofremas every element syncing to a single lobby clock — your type scale stays predictable no matter how deeply things are nested. Use it for font sizes and most spacing. - `em` — relative to the current element's font size. It compounds when nested (a child inside a parent, both set to
1.2em, ends up at1.44em). Best for things that should scale with a component's own text, likeletter-spacing. - `%` — relative to the parent element's matching dimension.
width: 50%means half the parent's width. - `vw` / `vh` — 1% of the viewport width or height.
100vhfills the full window height — great for hero sections.
/* Browser default: html font-size = 16px */
body { font-size: 1rem; } /* 16px, scales with user prefs */
h1 { font-size: 2.5rem; } /* 40px */
.caption{ font-size: 0.875rem; } /* 14px */
.hero {
min-height: 100vh; /* fills the full window */
padding: 4rem 2rem; /* generous breathing room */
}
.hero h1 {
/* scales between 32px and 72px based on viewport width */
font-size: clamp(2rem, 5vw, 4.5rem);
}
.card {
border: 1px solid #ddd; /* px: always sharp and thin */
border-radius: 8px;
padding: 1.5rem;
}Quick Reference: Which Unit When?
| Situation | Unit | |---|---| | Font sizes | rem | | Component padding / gap | rem | | Borders, shadows | px | | Fluid widths | % or fr | | Full-screen sections | vh | | Fluid headlines | vw inside clamp() | | Letter-spacing | em (scales with its own font) |
#Putting It Together: A Teaser of Typography
Color and units are the raw material of CSS typography. Every property — font-size, line-height, letter-spacing, color — uses what you just learned. A solid baseline: set font-size in rem, use a unitless line-height (like 1.6) so it always multiplies cleanly against the current font size, and define your text colors in HSL so you can create hover states and disabled states just by nudging the lightness. The next lesson goes deep on typefaces and font pairing — the concepts here carry you all the way there.
A user increases their browser's base font size from 16px to 20px for accessibility. Which font-size declaration automatically scales up with that change?
Define Your Palette Once in :root
Put all your colors as custom properties at the top of your stylesheet: ``css :root { --color-primary: hsl(217, 75%, 50%); --color-text: hsl(220, 15%, 20%); } ` Then use var(--color-primary)` everywhere. When the brand color changes, you update one line.
Key takeaways
- CSS has four color formats — named, hex, rgb/rgba, and hsl. Use HSL when building tonal palettes since you can shift darkness without touching the hue.
- rgba() and hsla() add an alpha transparency channel — essential for overlays, frosted-glass effects, and shadows with color.
- Use rem for font sizes and most spacing so your layout respects the user's browser accessibility settings; px is best for borders and fine decorative details.
- vw and vh tie dimensions to the viewport — pair them with clamp() to set safe minimum and maximum bounds for fluid text.
- Defining colors as CSS custom properties in :root gives you a single source of truth, making palette-wide updates trivial.
The user bumps their browser's base font size from 16px to 20px for accessibility. With the CSS below, what happens to the two headings on the page?
html { /* browser default: font-size 16px, user set to 20px */ }
.title { font-size: 2rem; }
.subtitle { font-size: 32px; }This code has a bug — the designer wanted a soft, washed-out (muted) blue, but instead they got a fully vivid blue. What's wrong?
.badge {
/* wanted: a muted, pastel-ish blue */
background-color: hsl(217, 100%, 50%);
}Complete the unit so this hero section fills the entire window height, no matter how tall the browser window is.
.hero { min-height: 100; }
Order these lines to build a working blue color scale: first open the :root block, define shades from lightest to darkest by only changing the HSL lightness, then close the block. Top line = first, bottom line = last.
--blue-100: hsl(217, 80%, 90%);
}
--blue-900: hsl(217, 80%, 15%);
:root {--blue-500: hsl(217, 80%, 50%);
Build a small profile card that demonstrates what you learned. The card should use: an HSL background color, a heading with a hex color in rem, a muted subtitle using rgba(), a px border, and rem padding. Bonus: add a .card--alt variant with a different background using a CSS custom property.
Try it live — edit the code and hit Run to see it rendered: