How to choose a color palette for your website
To choose a color palette for your website, start from the brand identity (or a reference image), define a primary, a secondary and an accent color, build a neutral scale and your status colors, distribute everything with the 60-30-10 rule, and only then check contrast, color blindness and dark mode. The end result isn't a list of five pretty hex codes. It's a small system of tokens that covers backgrounds, text, borders, buttons and messages.
This step-by-step guide follows one concrete example from start to finish: a company website with blue as its main color. Swap in your own brand values and follow the same steps.
1. Start from the brand or an image
If the brand already has a style guide, use the official colors as your starting point and write down the exact codes. If it doesn't exist yet, or all you have is a logo, pick a visual reference that captures the mood you want: a product photo, a shot of the space, a moodboard.
- With an image in hand, use the color extractor to get its dominant colors as hex values. It gets you from "sort of a greenish blue" to a concrete value.
- With a base color picked, generate combinations in the color palette generator, trying analogous, complementary or triadic schemes.
This is also the moment to think about brand personality. The color psychology guide explains why fit with your brand and audience matters more than any list of meanings.
2. Define the primary, secondary and accent colors
Three roles are enough for most websites:
| Role | Example | Where to use it |
|---|---|---|
| Primary | #1D4ED8 | Links, main buttons, section headers, brand elements |
| Secondary | #0F766E | Supporting areas, icons, badges, visual variety between sections |
| Accent | #F97316 | One very important action, highlights, occasional notices |
The primary in this example has a 6.7:1 contrast ratio against white, so it works as link text and as a button background with white text. The secondary, a dark teal, reaches 5.47:1. The orange accent, however, only manages 2.8:1 against white: it works as a button background with dark text (#0F172A on #F97316 is about 6.4:1), but not as a text color on a light background.
Keep the accent rare. If it shows up everywhere, it stops drawing attention.
3. Build the neutrals and semantic colors
Most of what visitors see on a website is neutral: background, body text, borders, dividers. Instead of pure grays, use grays slightly tinted with your primary hue. They look more cohesive and less lifeless. An example scale of bluish grays:
- #F8FAFC: page background
- #E2E8F0: dividers and card backgrounds
- #64748B: secondary text and input borders (4.76:1 on white)
- #334155: supporting text
- #0F172A: body text (17.85:1 on white)
Semantic colors communicate status and should stay consistent across the whole site:
| State | Shade for text and icons (light background) | Contrast with white |
|---|---|---|
| Success | #15803D | 5.02:1 |
| Error | #B91C1C | 6.47:1 |
| Warning | #B45309 | 5.02:1 |
| Info | #0369A1 | 5.93:1 |
Notice that the brighter, more popular shades, such as a #16A34A green (3.3:1) or a #D97706 amber (3.19:1), don't reach 4.5:1 against white. They're fine for large icons or backgrounds, but text needs the darker shade.
Soft backgrounds for messages
Alerts and form messages usually pair a very light background with text and an icon in the dark shade of the same state. For info, for example, a #EFF6FF background with #1D4ED8 text works well. Define these pairs once per state and reuse them everywhere, so an error alert always looks the same whether it appears at checkout or on the contact page.
4. Apply the 60-30-10 rule and build scales
The 60-30-10 rule, borrowed from interior design, is a starting point for distributing visual weight:
- 60% for the dominant color, usually the light neutrals of the background;
- 30% for the secondary color, or the primary in supporting areas such as the header, footer and cards;
- 10% for the accent, on buttons and elements that need attention.
It isn't a pixel-exact measurement. It's a reminder about hierarchy: a few strong colors and plenty of neutral space.
In our example, that means a #F8FAFC background and white cards filling most of the screen, the #1D4ED8 blue in the header, links and main buttons, and the #F97316 orange only on the "Request a quote" button. A data-heavy admin dashboard may push the share of neutrals even higher; a campaign landing page may give the brand color more room.
Scales from 50 to 900
Every brand color needs variations for hover states, soft backgrounds, borders and dark mode. The most common convention is a scale from 50 (almost white) to 900 (very dark). For the example primary:
50 #EFF6FF · 100 #DBEAFE · 200 #BFDBFE · 300 #93C5FD · 400 #60A5FA · 500 #3B82F6 · 600 #2563EB · 700 #1D4ED8 · 800 #1E40AF · 900 #1E3A8A
You can create the light tints by mixing the base color with white and the dark shades by mixing it with black (or with a very dark blue, to keep saturation) in the color mixer. Then adjust by eye: the steps should look visually even, not be mathematically identical.
5. Plan for dark mode
Dark mode isn't color inversion. A straight inversion turns #0F172A text into near-pure white on black, which causes glare, and leaves your brand colors oversaturated against the dark background.
- Avoid pure black. Use a very dark, slightly tinted background such as #0F172A. Raised surfaces (cards, menus) get a bit lighter, such as #1E293B.
- Avoid pure white text. A light gray like #E2E8F0 scores 14.48:1 on #0F172A, which is plenty and easier on the eyes.
- Desaturate and lighten brand colors. The #1D4ED8 blue is too dark on a dark background; in dark mode, switch to the 400 step, #60A5FA, which reaches 7.02:1 on #0F172A.
- Recalculate status colors. Semantic shades move up the scale too: a #4ADE80 green or a #F87171 red works well on #0F172A.
6. Check contrast and color blindness
Once your palette is drafted, test every real text-and-background pair, not just the colors on their own. WCAG asks for 4.5:1 for normal text, 3:1 for large text and 3:1 for input borders, icons and other interface components. Measure each pair with the contrast checker.
Next, simulate how the interface looks to people with color blindness in the color blindness simulator. Pairs like success green and error red can look nearly identical with deuteranopia and protanopia, so back up color with icons, text or patterns. Links inside body text deserve the same care: if the only difference between a link and the surrounding text is color, keep the underline. The color contrast and accessibility guide covers the criteria in detail.
7. Turn the palette into CSS tokens
Split tokens into two layers: primitive tokens that hold the scale values, and semantic tokens that describe what each color is for. Components only use the semantic ones, so switching themes means changing a handful of lines.
:root {
/* primitives */
--blue-400: #60A5FA;
--blue-700: #1D4ED8;
--slate-50: #F8FAFC;
--slate-200: #E2E8F0;
--slate-500: #64748B;
--slate-800: #1E293B;
--slate-900: #0F172A;
--orange-500: #F97316;
/* semantic */
--color-bg: var(--slate-50);
--color-surface: #FFFFFF;
--color-text: var(--slate-900);
--color-text-muted: var(--slate-500);
--color-primary: var(--blue-700);
--color-accent: var(--orange-500);
--color-success: #15803D;
--color-danger: #B91C1C;
}
.button-primary { background: var(--color-primary); color: #FFFFFF; }
For dark mode, the light-dark() function sets both values in the same token. It requires the color-scheme property:
:root {
color-scheme: light dark;
--color-bg: light-dark(#F8FAFC, #0F172A);
--color-surface: light-dark(#FFFFFF, #1E293B);
--color-text: light-dark(#0F172A, #E2E8F0);
--color-primary: light-dark(#1D4ED8, #60A5FA);
}
If you need to support older browsers, the classic alternative is a media query:
@media (prefers-color-scheme: dark) {
:root {
--color-bg: #0F172A;
--color-surface: #1E293B;
--color-text: #E2E8F0;
--color-primary: #60A5FA;
}
}
Common mistakes
- Too many colors with no defined role. If you can't explain what a color is for, you probably don't need it.
- Pure neutral grays next to a highly saturated brand color, which makes the interface feel cold and disconnected.
- Nearly invisible input borders, such as #E2E8F0 on white (1.23:1), which fail the non-text contrast criterion.
- White text on light brand colors like orange, yellow or bright green.
- Dark mode by inversion, with pure black, pure white and saturated colors that seem to vibrate.
- Loose hex values scattered through the code instead of tokens, which turns any brand update into a find-and-replace hunt.
Next steps
Review your palette in this order: brand and reference, color roles, neutrals and states, 60-30-10 distribution and scales, dark mode, contrast and color blindness, tokens. To put it into practice:
- Generate and compare combinations in the color palette generator.
- Validate every text-and-background pair with the contrast checker.
- Build your 50–900 scales in the color mixer.
- Go deeper into schemes with the color harmony guide, and browse references such as navy blue and charcoal gray.