Guide · CSS & Frontend

How to Build a UI Color Palette

Updated 2026-08-12 · 4 min read

A UI palette is a short list of named colors with jobs: surface, text, primary action, danger, border. It is not a 64-stop gradient dump from a generator. You build it by picking one primary, deriving tints that still contrast, and stopping when components can be painted without new hex codes.

Color Studio is the scratch pad: pick, tint, copy CSS variables. CSS Pattern is where those tokens become a background without exporting a PNG. Contrast that already shipped belongs in WCAG Checker.

Start with one primary and a surface

Surface is the page (--bg) and the card (--bg-elevated if you need a step). Text is the default body color on that surface. If body text fails 4.5:1 on the surface, nothing else matters yet.

Primary is the one brand hue you will use on buttons, links, and focus. Pick it from the logo or the product, then immediately test:

  • primary as background with white or near-white label text,
  • primary as text on the surface (links),
  • primary at 10–20% tint as a selected-row background with dark text.

If the saturated logo blue fails as a button fill, you do not “add a drop shadow and hope.” You darken the fill or change the label color until the ratio clears. Color Studio is for that trial; Figma is where you already lost the argument if nobody checked.

A second accent is optional. A second accent that is also a button color is how you get two competing CTAs.

Tints and shades that still contrast

Generate a scale (50–900 or primary-100primary-700) from one hue so hover, active, and disabled are relatives, not random neighbors.

Rules that keep you honest:

  • Disabled can drop contrast; it should still look like the same family, not gray from another palette.
  • Hover needs a visible step (roughly 1.5–2× darker or lighter in lightness, not a 2% hex nudge).
  • Tinted backgrounds (primary-50) must keep body text at 4.5:1. Pale blue plus #666 text is a common fail.

Do this in HSL or OKLCH if you want even steps. If you only copy hex out of Color Studio, write the names next to the values so --blue-3 means something next month.

Borders and dividers need 3:1 against the adjacent surface when they are the only way to see a control edge. #EEEEEE on #FFFFFF is decoration, not a boundary.

Semantic colors: success, danger, muted

Danger is for irreversible or invalid. It should not be the same red as the logo unless the product is that red. Test danger-on-surface for text and danger-as-fill for a delete button.

Success is confirmation, not a third brand. Keep it duller than primary so a green toast does not outshine the main button.

Warning is amber; check contrast on both light and dark surfaces. Yellow-on-white body text almost never hits 4.5:1 - use it as a background + dark text, or as a small icon plus text.

Muted is secondary labels, placeholders, captions. Placeholders are the usual AA failure. If the legal text is muted, measure it.

Do not assign semantics by hue folklore alone (“red means error”) without a non-color cue: icon, text, or position. Color is not the only channel; see A Practical WCAG Checklist.

Dark mode is not invert()

A dark theme needs new surfaces (#0B0D10#161A20 range is typical) and new text (#E8EAED, not #FFFFFF on every heading if you want less glare). The same primary hex often fails as a large fill on dark backgrounds (vibration, low contrast with white labels). Shift lightness; keep hue if you can.

Focus rings that were dark-on-light must become light-on-dark. Shadows get subtler; borders may need to lighten.

Put tokens in one place:

:root {
  --bg: #f7f5f2;
  --text: #1a1916;
  --primary: #2453d6;
}
[data-theme="dark"] {
  --bg: #12141a;
  --text: #ececec;
  --primary: #8aa4ff;
}

Recheck every pair. Inverting the light sheet is how you ship neon text on neon buttons.

How to generate and copy CSS variables

  1. Open Color Studio with no account.
  2. Set surface + text until body copy passes.
  3. Set primary; derive hover/active/disabled; copy as --primary, --primary-hover, or a numbered scale.
  4. Add danger (and success only if a component needs it).
  5. Paste into the stylesheet or theme file. Delete stray hex from components as you touch them.
  6. If a page needs a wash or grid, open CSS Pattern and feed it the same tokens so the background is not a fourth blue.

You can do this entirely in the tab. There is no reason to upload a zip of product shots to pick #2453D6.

When a palette is done

You are done when a new settings page can be built from named tokens, when dark mode is a second map of the same names, and when a contrast check of the real CSS would not surprise you.

You are not done if hover states are still filter: brightness(0.95) on a random hex, or if each feature branch adds --brandBlueNew.

Motion should use the same tokens (a pulse in primary-200, not a mystery pink). How CSS Animations Work is the next constraint: prefers-reduced-motion does not care how pretty the hue is.

Build the pairs, then stop adding swatches

Build the core pairs in Color Studio, apply them in CSS Pattern if you need a background, then verify shipped contrast with WCAG Checker. Stop adding swatches when the form can be painted.

Frequently asked questions

How many colors does a first palette need?

A surface, a text color, one primary, one danger, and a muted border is enough to ship a form. Add success, warning, and a second accent only when a component actually needs them.

What contrast ratio should text meet?

WCAG 2.1 AA: 4.5:1 for normal text, 3:1 for large text. Check the real background, not only white. Color Studio is where you trial pairs before they become tokens.

Should I use hex, HSL, or OKLCH?

Ship whatever your codebase already uses. HSL/OKLCH are easier when you need even tints. Hex is fine if you copy a finished pair. Do not mix three notations in one file without a reason.

How do I handle dark mode?

Define surfaces and text again for the dark theme. Do not invert the light palette. Recheck contrast; primary-on-dark is a different pair than primary-on-white.

When is the palette done?

When every shipped component uses a named token and you can add a page without inventing a new hex. If you are still pasting from a color picker into random CSS, it is not done.

Do I need an account?

No. Color Studio and CSS Pattern open without registration.

More reading that links back to the same tools and workflows.

RoundupCSS & Frontend

Best CSS and Frontend Design Tools

Color palettes, CSS animation previews, favicons, patterns, bundle size, and screen-reader checks - all in the browser.

4 min read