Color is the loudest decision you make on a screen. Before a visitor reads a headline or clicks anything, they have already absorbed a mood from your palette. This guide moves from the physics of color through harmony, accessibility, and psychology, then ends with the practical CSS that turns a palette into a maintainable system.
How Color Actually Works: HSV
Designers think in three properties, not in hex strings. The HSL and HSV models map far more closely to how we reason about color than RGB or hex.
- Hue is the color itself, measured as an angle on a 360-degree wheel: red at 0, green at 120, blue at 240.
- Saturation is intensity, from a flat grey at 0% to a vivid, fully chromatic color at 100%.
- Value (or lightness) is how light or dark the color is, from black to white.
The practical payoff is enormous. If you want a darker version of your brand blue for a hover state, you keep hue and saturation fixed and drop the value. If a screen feels garish, you almost always need to lower saturation, not change hues. Working in hsl() directly in CSS makes these moves explicit instead of guesswork with a color picker.
Why designers avoid pure saturation
Fully saturated colors (100%) rarely look professional on screen. They vibrate, clash, and fatigue the eye. Mature palettes usually live in the 30-70% saturation range, reserving the punchiest values for a single accent. The same restraint applies to value: pure black and pure white are harsh, so many designers anchor a palette at near-black #111 and off-white #fafafa.
Color Harmonies
Harmonies are repeatable relationships on the color wheel that reliably look intentional. You do not need all of them, but you should know the three workhorses.
- Complementary colors sit opposite each other (blue and orange). High contrast and energetic, ideal for making a call-to-action pop against a calm background.
- Analogous colors sit next to each other (blue, blue-green, green). Low tension and serene, good for content-heavy interfaces and gradients.
- Triadic colors are evenly spaced by 120 degrees. Vibrant and balanced, but harder to control, so let one dominate.
Two more deserve a mention: split-complementary softens the clash of a true complement by using the two colors adjacent to it, and monochromatic schemes vary only value and saturation of a single hue for a clean, unified look.
A palette does not need many colors to feel rich; it needs a clear hierarchy among the few colors it has.
The 60-30-10 Rule
Borrowed from interior design, the 60-30-10 rule is the fastest way to keep a palette from feeling chaotic. Allocate your colors by proportion:
- 60% is your dominant color, usually a neutral background that the eye rests on.
- 30% is your secondary color, used for surfaces, sections, and supporting elements.
- 10% is your accent, reserved for the things you want clicked: buttons, links, key highlights.
The rule works because scarcity creates emphasis. When the accent appears on only a tenth of the screen, every instance reads as important. Spray that accent everywhere and it stops meaning anything. This is the single most common reason an amateur palette underperforms a professional one.
Contrast and Accessibility
A palette that ignores contrast is not a design choice; it is a defect. The WCAG 2.1 guidelines define minimum contrast ratios between text and its background:
- 4.5:1 for normal body text (AA).
- 3:1 for large text, roughly 24px or 18.66px bold (AA).
- 7:1 for normal text if you are targeting the stricter AAA level.
- 3:1 for meaningful non-text elements like icons, form borders, and focus rings.
Contrast is calculated from relative luminance, which is why a mid-grey can fail against both white and a slightly darker grey. Tools that show the live ratio as you adjust value are indispensable. A critical rule: never encode information with color alone. A red error state must also carry an icon or text, because color-blind users and screen readers will miss the hue. Roughly one in twelve men has some form of color vision deficiency, so this is a mainstream concern, not an edge case.
Color Psychology, Used Honestly
Color carries associations, but those associations are cultural and contextual rather than universal laws. Treat psychology as a nudge, not a formula.
- Blue reads as trust, stability, and calm, which is why finance, healthcare, and SaaS lean on it.
- Green signals growth, nature, money, and success or confirmation states.
- Red demands attention: urgency, errors, sales, and appetite, but also danger.
- Yellow and orange feel energetic, friendly, and affordable, often used for warmth and CTAs.
- Purple suggests luxury, creativity, and the premium tier.
Context overrides the dictionary. Red means “buy now” on an e-commerce sale banner and “delete forever” on a destructive button. The job is to make sure your color’s meaning matches its function on the page.
Building a Palette with CSS Custom Properties
A palette is only useful if it is consistent, and consistency on the web comes from CSS custom properties (variables). Define your tokens once at the root and reference them everywhere.
Start with semantic naming rather than literal color names, so --color-accent can change from blue to teal without renaming a hundred declarations. A minimal token set looks like this:
:root {
--color-bg: hsl(0 0% 98%);
--color-surface: hsl(220 14% 96%);
--color-text: hsl(220 13% 13%);
--color-accent: hsl(231 77% 56%);
--color-accent-hover: hsl(231 77% 46%);
}
Because the values live in hsl(), generating a hover state is just lowering the lightness. You can go further and store raw channels, then compose them with hsl(var(--accent-h) var(--accent-s) var(--accent-l)), which lets you tweak a single dimension globally. Reference a token with color: var(--color-text) and your whole site stays in sync. This token layer is also the foundation for theming, because a dark theme is mostly a matter of overriding these same variables.
Color in Dark UI
A dark theme is not an inverted light theme, and color behaves differently against a dark canvas.
- Desaturate your accents. A vivid accent that looks crisp on white will glow and vibrate on near-black. Lower its saturation and nudge its lightness up.
- Use elevation instead of shadows. Shadows barely register on dark backgrounds, so signal depth by making elevated surfaces slightly lighter, not by adding drop shadows.
- Avoid pure black. A
#000base offers no room to layer surfaces and worsens halation on light text. Start aroundhsl(220 13% 10%)and build lighter surfaces from there. - Re-check contrast. Light-on-dark contrast ratios are not the inverse of dark-on-light, so every text token needs verifying again against WCAG thresholds.
Because your colors are already tokens, the dark theme is a second :root[data-theme="dark"] block that overrides the same variable names. The components never change; only the values do.
The Takeaway
Strong web color is systematic, not lucky. Reason in hue, saturation, and value so every adjustment is deliberate; pick a harmony and enforce the 60-30-10 proportions so one accent does the heavy lifting; verify every pairing against WCAG contrast and never lean on color alone to convey meaning. Use psychology to align a color’s mood with its job, then lock the whole thing into CSS custom properties so the palette scales and themes cleanly. A palette that converts is just a small set of well-chosen colors, applied with discipline.
