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.

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:

RoleExampleWhere to use it
Primary #1D4ED8Links, main buttons, section headers, brand elements
Secondary #0F766ESupporting areas, icons, badges, visual variety between sections
Accent #F97316One 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:

Semantic colors communicate status and should stay consistent across the whole site:

StateShade for text and icons (light background)Contrast with white
Success #15803D5.02:1
Error #B91C1C6.47:1
Warning #B453095.02:1
Info #0369A15.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:

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.

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

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: