Color theory beyond palette generation: color harmony rules (complementary, analogous, triadic, split-complementary), color psychology by industry, dark mode color strategy (perceptual lightness, not inversion), simultaneous contrast, color blindness design patterns, and HSL/OKLCH color space decisions. The reasoning behind color choices, not just the output.
invert() filterHarmony rules predict which colors feel intentional together vs. random.
Based on hue (0–360°):
0° Red
30° Orange
60° Yellow
90° Yellow-green
120° Green
150° Blue-green
180° Cyan
210° Blue-cyan
240° Blue
270° Violet
300° Magenta
330° Rose
360° Red (same as 0°)
| Harmony | Formula | Character |
|---|---|---|
| Monochromatic | Same hue, vary lightness/saturation | Cohesive, calm, can feel flat |
| Analogous | 3 hues within 30-60° of each other | Natural, comfortable, low tension |
| Complementary | Hues 180° apart | High contrast, energetic, can vibrate |
| Split-complementary | Base + two hues 150° from base | Contrast without vibration |
| Triadic | 3 hues 120° apart | Vivid, balanced, complex |
| Tetradic / Square | 4 hues 90° apart | Rich palette, hard to balance |
SaaS / productivity tool → Monochromatic (calm) + semantic accent colors
Marketing / landing page → Split-complementary (energetic but controlled)
Brand identity → Analogous (memorable cohesion) + 1 complementary accent
Data visualization → Triadic or tetradic (maximum differentiation)
Emergency / health UI → Complementary avoided — red+green is color-blind problematic
60% — Dominant color (usually neutral)
30% — Secondary color (brand or supporting)
10% — Accent color (CTA, highlights, alerts)
Never flip this ratio. More than 10% accent creates visual noise.
Colors carry cultural weight. Use this as a starting point — always validate with your target audience.
| Color | Positive associations | Negative associations | Industry fit |
|---|---|---|---|
| Blue | Trust, calm, competence | Cold, corporate, distance | Finance, healthcare, SaaS, B2B |
| Green | Growth, nature, success | Envy, naive | Fintech, sustainability, health |
| Orange | Energy, warmth, approachable | Cheap, aggressive | Consumer apps, food, e-commerce |
| Red | Urgency, passion, strength | Danger, debt, aggression | Sales CTAs, alerts, food |
| Purple | Luxury, creativity, wisdom | Pretentious, distant | Beauty, premium, creative tools |
| Yellow | Optimism, attention, warmth | Caution, cheap | Children, food, attention-seeking |
| Black | Sophistication, power, premium | Cold, inaccessible | Luxury, fashion, premium tech |
| White | Clean, minimal, honest | Empty, clinical | Healthcare, minimal design, Apple-influenced |
Dark mode is not filter: invert(1). It requires a separate color system.
HSL lightness is not perceptual. A yellow at L50 looks much brighter than a blue at L50.
Use OKLCH (perceptually uniform) for dark mode tokens:
/* Light mode */
:root {
--surface-default: oklch(98% 0.005 260); /* near-white, slightly cool */
--surface-raised: oklch(96% 0.005 260);
--surface-overlay: oklch(93% 0.005 260);
--text-primary: oklch(18% 0.010 260); /* near-black */
--text-secondary: oklch(42% 0.010 260);
--text-disabled: oklch(65% 0.005 260);
}
/* Dark mode */
[data-theme="dark"] {
--surface-default: oklch(14% 0.010 260); /* NOT black — dark indigo-tinted */
--surface-raised: oklch(18% 0.010 260); /* slightly lighter = "raised" */
--surface-overlay: oklch(22% 0.010 260);
--text-primary: oklch(92% 0.005 260); /* NOT white — slightly warm */
--text-secondary: oklch(65% 0.005 260);
--text-disabled: oklch(40% 0.005 260);
}
| Element | Light → Dark strategy |
|---|---|
| Surfaces | Light grey → Dark grey (not black) |
| Text | Dark grey → Light grey (not white) |
| Brand/accent color | Often stays same hue, reduce saturation slightly |
| Semantic colors | Success/warning/error lighten 15-20% (need contrast on dark bg) |
| Shadows | Reduce opacity or replace with elevation via color (lighter surface = higher elevation) |
| Borders | Often removed on dark (use surface color contrast instead) |
Light mode: elevation = shadow depth
Dark mode: elevation = surface lightness (higher = lighter)
--surface-level-0: oklch(14% 0.010 260) /* base */
--surface-level-1: oklch(18% 0.010 260) /* cards */
--surface-level-2: oklch(22% 0.010 260) /* floating panels */
--surface-level-3: oklch(26% 0.010 260) /* modals */
--surface-level-4: oklch(30% 0.010 260) /* tooltips */
WRONG: background: #000000 → Harsh, unnatural
RIGHT: background: ~#0f1117 → Slightly off-black
WRONG: color: #ffffff → Harsh against dark bg
RIGHT: color: #e8eaf0 → Warm off-white
WRONG: Same saturation as light mode → Neon, glaring
RIGHT: Desaturate brand color 10-15% in dark mode
Colors look different depending on what surrounds them. This affects UI decisions.
The same grey looks lighter on dark background and darker on light background:
[light grey on black] looks almost white
[light grey on white] looks almost mid-grey
The eye adapts to the ambient light color. In dark mode, slightly warm neutrals (hue ~250-280) feel neutral. Pure grey (hue 0) looks cold and clinical in dark mode.
~8% of males have color vision deficiency. Do not rely on color alone.
| Type | Description | Prevalence (males) |
|---|---|---|
| Deuteranopia | Cannot distinguish red/green | 5% |
| Protanopia | Red appears dark/black | 2.5% |
| Tritanopia | Cannot distinguish blue/yellow | 0.003% |
| Achromatopsia | No color perception | 0.003% |
Never use red+green as the only distinguisher (success vs. error)
WRONG: green check vs. red X — looks identical to deuteranopes
RIGHT: green check vs. red X + different shapes + label text
Add a secondary cue: icon, pattern, label, or position
Test with simulation tools:
Use colorblind-safe palettes for data visualization:
Okabe-Ito palette (colorblind safe):
#E69F00 Orange
#56B4E9 Sky Blue
#009E73 Bluish Green
#F0E442 Yellow
#0072B2 Blue
#D55E00 Vermillion
#CC79A7 Reddish Purple
#000000 Black
| Space | Pros | Cons | Use for |
|---|---|---|---|
| HSL | Simple, widely understood | Not perceptually uniform | Quick palette building, compatibility |
| HSLuv | Perceptually uniform lightness | Less browser support | Design tools, color scales |
| OKLCH | Best perceptual uniformity, CSS native | Learning curve | Production design tokens (modern browsers) |
| sRGB (Hex) | Universal compatibility | No meaningful mental model | Final output only |
/* Format: oklch(lightness% chroma hue) */
oklch(75% 0.15 160) /* green-ish */
oklch(55% 0.25 30) /* warm orange */
oklch(40% 0.18 270) /* violet */
/* Compared to HSL, OKLCH ensures:
oklch(75% 0.15 160) and oklch(75% 0.15 30)
have the same PERCEIVED lightness — HSL does not guarantee this */
Token definition: OKLCH (perceptual precision)
Figma work: HSL (tool compatibility)
Code output: oklch() with fallback hex
Tailwind config: oklch() via CSS custom properties
invert or simple negation