—CSS Series · Post 05: Color
Colors & Units
The Language of Visual Decisions
Every color you see on every website was written as a value in a CSS file. This post gives you full control over that — and teaches you which units to reach for, and when.
💬 "What Even Is #ff69b4?"
At some point you copy-paste a color value and it works — but you have no idea why #ff69b4 is pink, or what rgba(0,0,0,0.5) means, or why one tutorial uses px and another uses rem. It all feels like magic codes you have to look up every time.
It's not magic. CSS has multiple color systems and multiple unit systems, and each one was built for a different use case. Once you understand what each one does, you start making deliberate choices instead of guessing.
Colors and units aren't just technical values — they're design decisions. Choosing rem over px for font sizes, or rgba over hex for overlays, reflects a level of intentionality that separates beginner CSS from professional CSS.
🧱 Color Systems
HEX — The Most Common Format
HEX is the format you'll see most often. It's a six-character code that represents red, green, and blue as pairs of hexadecimal digits — base 16, where letters A–F represent 10–15.
/* Full 6-digit HEX — #RRGGBB */
.heading { color: #ff69b4; } /* pink */
.bg { background: #0a0a0f; } /* near-black */
.border { border-color: #b44aff; } /* purple */
/* Shorthand — #RGB (only when both digits match) */
.white { color: #fff; } /* = #ffffff */
.black { color: #000; } /* = #000000 */
/* HEX with alpha — 8 digits, last two = opacity */
.ghost { background: #ff69b480; } /* 50% transparent pink */RGB and RGBA — Red, Green, Blue (+ Alpha)
RGB uses three numbers 0–255 for red, green, and blue. RGBA adds a fourth value for transparency — alpha — from 0 (invisible) to 1 (fully opaque). This is the most readable format for colors where opacity matters.
/* rgb(red, green, blue) — each 0–255 */
.heading { color: rgb(255, 105, 180); }
/* rgba — same, plus alpha 0–1 */
.overlay { background: rgba(0, 0, 0, 0.5); } /* dark overlay */
.card { border: 1px solid rgba(255,255,255,0.08); } /* subtle border */
.callout { background: rgba(180,74,255,0.06); } /* very faint tint */
/* Modern syntax — spaces, no commas */
.modern { color: rgb(255 105 180 / 80%); }Low-opacity colors for borders, backgrounds, and overlays make designs feel layered and polished without requiring exact color matching. rgba(255,255,255,0.08) on a dark background gives you a barely-there border that works with any accent color. This trick is used on virtually every modern dark UI.
HSL — Hue, Saturation, Lightness
HSL is the most human-readable color system. Instead of thinking in RGB values, you describe a color the way a person would: what hue it is, how vivid it is, and how light or dark it is.
/* hsl(hue, saturation%, lightness%) */
/* hue: 0–360° on the color wheel */
/* saturation: 0% = gray, 100% = full color */
/* lightness: 0% = black, 50% = pure, 100% = white */
.pink { color: hsl(330, 100%, 71%); }
.purple { color: hsl(277, 100%, 64%); }
/* HSL makes color variations easy — just adjust L */
.pink-light { background: hsl(330, 100%, 90%); } /* very light */
.pink-dark { background: hsl(330, 100%, 30%); } /* deep */
.pink-muted { background: hsl(330, 30%, 71%); } /* desaturated */
/* HSLA — add alpha */
.glow { box-shadow: 0 0 24px hsla(330, 100%, 71%, 0.3); }CSS also has 140+ built-in named colors: hotpink, rebeccapurple, dodgerblue, tomato. They work everywhere a color value is accepted, but they give you no control over opacity or variation. Good for quick prototyping, not production design systems.
🧱 Units
CSS has two categories of units: absolute (fixed regardless of context) and relative (calculated based on something else). Knowing which to reach for is one of the clearest signs of CSS maturity.
- px — pixels. The most common absolute unit. Use for borders, shadows, fine details.
- Other absolutes (cm, mm, in, pt) exist but are almost never used in web CSS.
- rem — relative to root font size. Best for typography and spacing.
- em — relative to the parent's font size. Useful but can compound unexpectedly.
- % — relative to the parent element's size. Great for widths and fluid layouts.
- vh / vw — percentage of the viewport height/width. Hero sections, full-screen layouts.
px — Pixels
/* px is best for things that should NEVER scale */
.card {
border: 1px solid rgba(255,255,255,0.08); /* fine line */
border-radius: 12px;
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
}
/* Avoid px for font-size — it ignores browser zoom preferences */
.avoid-this { font-size: 16px; } /* won't scale if user increases browser font size */rem — Root Em
/* Browser default: 1rem = 16px */
/* You can change the base by setting html font-size */
html { font-size: 16px; } /* 1rem = 16px */
body { font-size: 1rem; } /* 16px */
h1 { font-size: 3rem; } /* 48px */
h2 { font-size: 1.75rem; } /* 28px */
small { font-size: 0.875rem;} /* 14px */
/* rem also works for spacing */
.section { padding: 4rem 0; } /* 64px top+bottom */
.gap { margin-bottom: 1.5rem; } /* 24px */% — Percentage
/* % is relative to the PARENT element's size */
.container { width: 860px; }
.sidebar {
width: 30%; /* 30% of 860px = 258px */
}
.main {
width: 70%; /* 70% of 860px = 602px */
}
/* % heights are tricky — parent needs an explicit height */
.hero-img {
width: 100%; /* fills its container — very common */
height: auto; /* maintains aspect ratio */
}vh and vw — Viewport Units
/* 1vh = 1% of the viewport height */
/* 1vw = 1% of the viewport width */
/* Full-screen hero section */
.hero {
min-height: 100vh; /* full screen height */
width: 100vw; /* full screen width */
}
/* Fluid font scaling with clamp() */
h1 {
font-size: clamp(32px, 5vw, 64px);
/* min 32px, scales with viewport, max 64px */
}Quick Reference: When to Use What
| Unit | Use It For | Avoid It For |
|---|---|---|
| px | Borders, shadows, border-radius, fine details | Font sizes, spacing systems |
| rem | Font sizes, spacing, padding, margins | Borders (overkill) |
| em | Sizing relative to local font (icons, buttons) | Large-scale layout (compounding is confusing) |
| % | Widths, fluid containers, responsive columns | Heights (unless parent has fixed height) |
| vh / vw | Full-screen sections, viewport-aware sizing | Font sizes (can cause tiny text on mobile) |
Mini Build: A Consistent Color Theme
You're going to build a simple color system using CSS variables, then apply it using the right color format for each job. This is exactly how real design systems are built — define once, use everywhere.
Define your color palette as CSS variables on :root. We'll cover variables in depth later — for now, treat them as named colors you define once and reuse everywhere.
:root {
/* Base colors */
--bg: #0a0a0f;
--surface: #12121a;
--text: #e0e0e8;
--muted: #9595a8;
/* Accent — HEX for the pure color */
--accent: #ff69b4;
/* Accent with transparency — RGBA for layers */
--accent-glow: rgba(255, 105, 180, 0.15);
--accent-border: rgba(255, 105, 180, 0.25);
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: var(--bg);
color: var(--text);
font-family: sans-serif;
font-size: 1rem; /* rem — scales with browser */
line-height: 1.75;
padding: 3rem 2rem; /* rem — consistent rhythm */
}Style a card using your color system. Notice which format goes where: HEX for solid colors, RGBA for transparent layers, px for fine structural details, rem for spacing and type.
.card {
background: var(--surface);
border: 1px solid var(--accent-border); /* RGBA — subtle */
border-radius: 12px; /* px — fine detail */
padding: 1.5rem; /* rem — spacing */
max-width: 380px;
}
.card:hover {
background: var(--accent-glow); /* RGBA tint on hover */
border-color: var(--accent); /* HEX — full accent */
}
.card-title {
color: var(--accent); /* HEX */
font-size: 1.25rem; /* rem */
margin-bottom: 0.5rem;
}
.card-body {
color: var(--muted);
font-size: 0.9rem;
}Change your whole theme by editing just two lines. Swap --accent to #69d2e7 (sky blue) and --accent-border to rgba(105, 210, 231, 0.25). Every element that references those variables updates instantly. That's the power of a color system.
💛 You Just Learned to Think in Color Systems
Most beginners copy-paste colors without knowing what format they're looking at. Now you know what each format is for — HEX for solid accent colors, RGBA when you need transparency, HSL when you want to reason about lightness and saturation directly.
- HEX: the standard format for solid colors — 6 digits, R/G/B
- RGBA: your go-to for overlays, borders, tints — anything where opacity matters
- HSL: the most human-readable format, great for generating color families
- px for precise details. rem for everything type and spacing related.
- % for fluid widths. vh/vw for full-viewport layouts.
- CSS variables tie it all together into a theme you can change in one place
Next: Display & Positioning — how CSS decides where elements sit, how big they are, and how they stack. This is the post that makes position: absolute finally make sense.
I used HEX for everything for way too long. Even for transparent borders. My code was full of things like border: 1px solid #ffffff14 — which I had to look up every single time because HEX alpha is not intuitive at all. (#14 in hex is 20 in decimal, which is... roughly 8% opacity. You're welcome.)
The day I switched transparent UI colors to RGBA, my CSS became immediately more readable. rgba(255,255,255,0.08) — I can just read that. 8% white. Done. I don't need a hex-to-decimal converter to understand my own stylesheet anymore.
Use the right format for the job. Your future self will be very grateful.