Dustin Johnson ← Design Primitives
Design Primitive Reference · Part III · April 2026

Color Palette for Buttons

How to select, constrain, and apply color to a button system — from color budgets and semantic roles to the hues that break readability and the ones that don't.

Where We Left Off

Parts I and II built a complete button system without color. We isolated shape, proportion, typography, hover mechanics, contrast relationships, and border construction — all in monochrome. That was deliberate. Structure must survive the absence of color, because color is the most loaded, most constrained, and most misused variable in interface design.

Now we introduce it. But introducing color to buttons is not a matter of choosing a nice blue. It is a series of interlocking decisions — how many colors, which hues, which roles they play, how they interact with hierarchy, how they behave across light and dark themes, and which hue ranges are physically incapable of meeting readability requirements. Each decision constrains the next.

This document walks through every decision point in sequence. The goal is not to prescribe a single palette but to show the full range of viable options within modern best practice, so you can see the tradeoffs and make informed choices for your system.

A1. The Color Budget

Every button color you add costs cognitive overhead, accessibility testing, state derivation work, and dark mode mapping. The question is not "what colors could we use?" but "what is the minimum number that lets us communicate every necessary distinction?"

A survey of nine major design systems — Material Design 3, Carbon, Atlassian, Polaris, Spectrum, Apple HIG, Ant Design, Chakra, and Primer — reveals a striking consensus: the standard semantic button color set has converged to primary + danger. Not four. Not five. Two chromatic roles, plus neutral.

Success, info, and warning colors are absent from every post-2020 design system's button offering. These semantic meanings still exist in the system — on badges, alerts, and banners — but they have been deliberately excluded from buttons. The reason is that buttons are action triggers, and the semantic palette of actions is narrower than the semantic palette of status.

Minimal

2

Neutral + one accent. The button system is monochrome with a single call-to-action color. Maximum clarity, minimum palette overhead. Ideal for consumer products, marketing sites, and focused applications.

Standard

3

Neutral + accent + danger. Covers the full action vocabulary: do the default thing, do the important thing, do the irreversible thing. This is the modern consensus and where most systems should land.

Extended

5

Adds success and warning. Only justified in dense enterprise UIs where buttons must communicate status alongside action — admin panels, moderation queues, operational dashboards. Requires careful compositional rules to prevent rainbow effect.

The Bootstrap trap. Bootstrap popularized five-color button systems (primary, secondary, success, danger, warning, info, light, dark — eight, actually). This was a library choice, not a design recommendation. Every design system built after 2020 has systematically pruned that list. If your system still offers btn-success, btn-info, and btn-warning, ask: when is someone supposed to use each, and can you explain the distinction in one sentence?

A2. The Three Tiers in Practice

Here are the three budget tiers, showing how the same hierarchy-based variant system (filled → outlined → ghost → text) applies at each level of color investment.

Tier 1: Neutral + One Accent

Two-color system · Accent is the only chromatic element
Primary CTA
Secondary
Tertiary
Text

Tier 2: Neutral + Accent + Danger

Three-color system · Most modern systems stop here
Primary
Secondary
Danger outline
Danger text

Tier 3: Full Semantic Set

Five-color system · Enterprise dashboards, admin panels
Primary
Success
Warning
Danger
Neutral

Notice the visual noise. The five-color row creates competing focal points — the eye cannot settle on a single call to action. This is the rainbow problem. When every action has a distinct color, no action has emphasis.

The rule. At most one filled/saturated button per section or action bar. All other actions are expressed through fill mode (outlined, ghost, text) in neutral tones. If destructive and constructive actions coexist, separate them spatially — distance or a divider — rather than relying on color alone to differentiate.

B1. Which Hues Work as Button Fills

Not all hues are equally viable as button background colors. Some produce excellent contrast with white text. Some produce excellent contrast with dark text. Some produce excellent contrast with neither. The viability of a hue as a button fill depends entirely on its lightness — and lightness is not distributed evenly across the hue wheel.

The following specimens show a representative button for each major hue region at a "standard" saturation, with white text. The contrast ratio is computed against pure white (#fff). Green checkmarks indicate WCAG AA compliance (4.5:1+). Warning icons indicate marginal compliance. Red crosses indicate failure.

White text on saturated hue fills · WCAG AA threshold: 4.5:1
Red 5.9:1 ✓
Orange 3.8:1 ⚠
Amber 2.7:1 ✗
Yellow 1.8:1 ✗
Lime 3.1:1 ✗
Green 4.6:1 ✓
Teal 3.9:1 ⚠
Cyan 3.3:1 ✗
Blue 5.5:1 ✓
Violet 6.7:1 ✓
Purple 6.2:1 ✓
Pink 4.7:1 ✓

The pattern is clear. The safe zone for white text spans roughly 210°–330° on the hue wheel (blue through violet to pink) and includes darkened reds. The danger zone spans 30°–180° (amber through yellow, lime, and cyan) — these hues have inherently high perceptual luminance and cannot achieve sufficient contrast with white text at their natural saturated midpoint.

B2. The Yellow Problem

Yellow is the most perceptually luminant hue. At full saturation, it is functionally white from a contrast perspective. This creates a dead zone where neither white nor dark text achieves a comfortable reading experience at the "standard" saturation most designers reach for.

The yellow dead zone · Same background (#eab308), two text options
1.8:1 ✗
10.9:1 ✓
2.7:1 ✗
7.2:1 ✓
4.5:1 ✓
4.6:1 ✓

The solution is one of three strategies. Strategy A: always use dark text on yellow. This is the simplest rule and what most design systems do — encode a per-color text-color override in your tokens. Strategy B: darken the yellow significantly until it becomes more of a brown-gold (around #a16207), at which point white text barely passes. This works but the button no longer "reads" as yellow — it reads as brown. Strategy C: don't use yellow as a button fill at all. Restrict yellow to badges, borders, alert backgrounds, and icon accents. This is the Polaris and Carbon approach.

The same problem affects orange. A typical saturated orange (#ea580c) achieves only 3.8:1 with white text — it looks readable to most sighted users but fails WCAG AA. You must darken orange to approximately #c2410c (4.8:1) for compliance. Cyan, teal, and lime are similarly problematic. The safe approach: for any button fill color, compute the contrast ratio with both white and #1a1a1a, then assign whichever text color achieves the higher ratio.

B3. The Text Color Flip

When your system allows arbitrary or brand-derived button fills, you need an automatic rule for choosing white vs. dark text. The threshold is not at 50% lightness — human perception is nonlinear.

The switch point sits at approximately 0.36–0.38 relative luminance using the W3C sRGB formula, or an OKLCH lightness of roughly 0.62–0.70. Below this threshold, white text provides better contrast; above it, dark text wins. Here is the flip point visualized across a single hue (blue) at varying lightness:

Blue hue at increasing lightness · text color flip at ~OKLCH L 0.65
0.30
0.38
0.46
0.54
0.62
0.68
0.76
0.84
0.92

And here is the same test across yellow, where the flip happens much earlier because yellow's inherent luminance is so high:

Yellow hue · flip at ~OKLCH L 0.55 due to high perceptual luminance
0.30
0.38
0.46
0.54
0.62
0.70
0.78
0.86
0.94

The practical rule: compute relative luminance of the button fill. If it exceeds 0.36, use dark text. If it falls below 0.36, use white text. In CSS, modern relative color syntax can automate this entirely without JavaScript.

/* Automatic text color flip using OKLCH */ --button-bg: oklch(0.55 0.18 260); --button-text: oklch(from var(--button-bg) clamp(0, round(1.21 - L) 0 0)); /* For browsers without oklch: JS fallback */ /* luminance > 0.36 → #1a1a1a, else #ffffff */

C1. What Each Color Means

Color semiotics in UI are not arbitrary. They draw on physiological responses, cultural convention, and decades of interface precedent. These mappings are strong enough that violating them creates friction — a green "Delete" button or a red "Accept" button requires users to override instinct.

Primary Trust · Action · Progress
Success Confirm · Complete · Positive
Warning Caution · Review · Attention
Danger Delete · Destroy · Irreversible
Neutral Cancel · Close · Secondary

The critical mapping: blue is the safest primary color because it carries no negative semantic baggage. It does not conflict with success (green), danger (red), or warning (yellow). This is why the overwhelming majority of design systems — Material, Carbon, Spectrum, Atlassian, Ant — use blue as their primary action color. Green is viable as a primary (GitHub does this), but it creates tension with "success" semantics. A green primary button next to a green success badge is ambiguous.

The Destructive Button Escalation Pattern

Should a destructive button ever carry primary visual weight (filled, high-emphasis red)? Yes — but only in confirmation contexts. The established pattern across modern systems is a two-stage approach:

Stage 1 · On a settings page, destructive actions are understated
Primary
Danger text-only
Stage 2 · In the confirmation dialog, destructive action escalates to filled

Delete your account?

This action cannot be undone. All data will be permanently removed.

The non-destructive "Cancel" option remains the visually safer choice — understated and secondary. The destructive button gains primary weight only at the moment of commitment, when its gravity needs to be unmistakable.

D1. Hierarchy × Semantic Color

Hierarchy (filled → outlined → ghost) and semantic color (primary → neutral → danger) are orthogonal axes. They combine to form a matrix. Not every cell in this matrix is valid — and understanding which cells to leave empty is as important as filling the right ones.

The hierarchy × color matrix · Standard 3-color system
Primary
Neutral
Danger
Filled
Outlined
Ghost
Text

That is a 4×3 grid: 12 combinations. In practice, a typical application uses only 5–7 of these. The most common subset: primary filled (main CTA), neutral outlined (secondary action), neutral ghost (tertiary), neutral text (minimal action), danger text (inline destructive), and danger filled (confirmation destructive). The rest exist for edge cases.

Avoid semantic fill at the same hierarchy level. If "Deploy" is primary-filled-blue and "Approve" is success-filled-green, placed side by side, neither reads as dominant. The eye bounces between two saturated fills. The fix: only one filled-chromatic button per group. Demote the other to outlined or ghost.

E1. When Brand Color Conflicts with Semantic Color

A red brand whose primary buttons are red cannot distinguish "primary action" from "danger." A green brand whose primary buttons are green cannot distinguish "primary action" from "success." This is the brand-semantic collision, and it affects roughly 40% of brands.

The collision: brand red as primary, semantic red as danger
Red Brand ⚠ Identical appearance
Green Brand ⚠ Identical appearance

Resolution Strategies

Strategy A · Separate brand from functional — Netflix/YouTube approach
Red brand, blue actions ✓ Distinct
Green brand, blue actions ✓ Distinct
Strategy B · Neutral dominance — red brand uses black primary buttons
Neutral primary, red reserved for danger ✓ Red retains danger meaning
Strategy C · Hue shift — red brand shifts primary to adjacent warm tone
Brand red → warm coral primary △ Subtle, risky

Strategy A (separation) is the safest. Strategy B (neutral dominance) is the most aesthetically restrained and popular with mature products — Shopify's Polaris uses near-black for all primary buttons. Strategy C (hue shift) is fragile: the shifted primary and the danger red must be distinct enough to read as different colors, which limits you to a narrow adjacency window.

The 10% rule. Regardless of resolution strategy, brand color should appear on roughly 10% of the interface — logo, accent borders, selected states, an occasional highlight. When it is spread across every button, it becomes noise and loses its brand-signaling power.

F1. Deriving Hover, Active, and Disabled States

Every base button color needs four derived states: hover, active/pressed, focus, and disabled. There are two schools of thought on how to derive them, and the choice shapes how maintainable the system is as colors change.

Method 1: Overlay Approach (Material Design)

Apply the button's text color (the "on-color") as a semi-transparent overlay on the base fill. This produces consistent results across all button colors without per-color tuning.

Overlay method · White overlay on blue base
Default base
Hover +8% on-color
Focus +12% on-color
Pressed +12% on-color
Disabled 38% opacity
Same overlay percentages applied to red — no per-color tuning needed
Default base
Hover +8% on-color
Focus +12% on-color
Pressed +12% on-color
Disabled 38% opacity

Method 2: Lightness Shift in OKLCH

Adjust the L (lightness) channel directly. This is more precise and avoids the slight desaturation that overlay mixing introduces, but requires choosing shift amounts that produce good results across all hues.

OKLCH lightness shift · Same delta values, two hues
Default L: 0.55
Hover L: –0.05
Pressed L: –0.10
Disabled C: ×0.25
Default L: 0.55
Hover L: –0.05
Pressed L: –0.10
Disabled C: ×0.25
/* OKLCH state derivation — CSS relative color syntax */ --btn-bg: oklch(0.55 0.19 260); .btn { background: var(--btn-bg); } .btn:hover { background: oklch(from var(--btn-bg) calc(l - 0.05) c h); } .btn:active { background: oklch(from var(--btn-bg) calc(l - 0.10) c h); } /* Disabled: reduce chroma, increase lightness */ .btn:disabled { background: oklch(from var(--btn-bg) calc(l + 0.15) calc(c * 0.25) h); color: oklch(from var(--btn-text) l c h / 0.45); cursor: not-allowed; }

Which method to choose: the overlay approach is simpler and more forgiving — it works acceptably across any base color with zero per-color configuration. The OKLCH approach produces more precise, "designed" results but requires you to validate that your delta values work across your entire color set. For systems with 2–3 button colors, OKLCH is manageable and superior. For systems with many colors or user-customizable themes, the overlay approach is safer.

Disabled state accessibility. WCAG 2.x explicitly exempts inactive UI components from contrast requirements. However, best practice still targets 3:1 contrast between the disabled button and its background so users can see that a control exists, even if they understand they cannot interact with it. Material Design uses a uniform disabled treatment — the same muted appearance regardless of the button's enabled color — which simplifies implementation and provides a consistent visual signal.

G1. Light Mode to Dark Mode

Simply inverting lightness values or mechanically swapping light↔dark produces three predictable failures. Saturated colors vibrate against dark backgrounds, status colors lose semantic clarity, and contrast ratios near black become deceptively high while remaining hard to read. Dark mode requires a parallel palette, not a derived one.

Light mode
Dark mode

Note the shifts. The primary blue moves from a dark saturated blue (#2563eb, white text) to a lighter desaturated blue (#60a5fa, dark text). This is not inversion — it is a remapping to a different point in the same hue family that achieves the same visual weight against its respective background. The outlined buttons shift their border to a lower-opacity white rather than a mechanical inversion of the light-mode border.

The Three Dark Mode Rules

Rule 1: Desaturate. Saturated colors against dark backgrounds cause visual vibration and eye strain. Reduce chroma by 20–40% or shift toward lighter tonal variants (Material's tone-200 instead of tone-500).

Rule 2: Use dark gray (#121212), not pure black. Pure black (#000) against white text produces a 21:1 contrast ratio — higher than the 15.8:1 practical ceiling for comfortable reading. Dark gray reduces this to a more comfortable ~16:1.

Rule 3: Test text color flips. A button that safely uses white text in light mode may need dark text in dark mode if the fill color lightens significantly. Run the luminance flip test (Section B3) against every dark-mode fill.

Primary button color mapping · same hue family, different endpoints
900 bg surface
700 light fill
600 light fill
500 dark fill
400 dark fill
300 dark tint
Token architecture. The system that enables theme-switching without per-component overrides uses a three-tier hierarchy: primitive tokens (raw values like blue-500: #3b82f6), semantic tokens (intent-based like color-primary: var(--blue-600)), and component tokens (button-bg-primary: var(--color-primary)). Theme-switching redefines only the semantic-to-primitive mappings. Component code stays theme-agnostic.

H1. Color in Non-Filled Buttons

Filled buttons are the obvious case — background is chromatic, text is white or dark. But outlined, ghost, and text-only buttons introduce a subtler question: where does the color go, and how much of it?

There are three channels of color in a non-filled button: the text color, the border or container tint, and the hover/active fill. Each can be chromatic or neutral, creating a spectrum from fully monochrome to fully tinted.

Outlined button · four approaches to chromatic expression
All neutral
Color text only
Color text + border
Color text + full border
Ghost button · how much chromatic fill
Gray fill, dark text
Blue fill, dark text
Blue fill + blue text

The decision point is how much "color signal" a non-primary button should carry. More chromatic expression increases visual weight and risks competing with filled buttons for attention. Less chromatic expression keeps non-primary buttons subordinate but makes them harder to identify as belonging to a semantic role. The general rule: for neutral-role buttons, stay fully neutral. For semantic-role buttons (danger, success), tint the text at minimum. Border tinting is a design preference — it increases coherence but slightly raises visual weight.

I1. Complete System Example

Here is a complete three-color button system — primary, neutral, danger — shown across four hierarchy levels, with hover state strips and dark mode mappings. This represents the "standard" tier that covers the vast majority of application needs.

Complete three-color system · light mode

Primary (Blue)

Neutral (Gray)

Danger (Red)

Same system · dark mode

Primary (Blue)

Neutral (Gray)

Danger (Red)

J1. Choosing Your Primary Hue

If your brand color does not conflict with semantic roles, it becomes your primary button color. If it does conflict, you need to choose an alternative. Here is the full range of viable primary button hues — every option that passes WCAG AA with white text at a natural, saturated midpoint.

Viable primary button fills with white text · all ≥ 4.5:1
Crimson
Rust
Brown
Forest
Dark teal
Navy
Blue
Indigo
Purple
Magenta
Rose
Charcoal

Notable absences: no pure orange, no yellow, no lime, no cyan. These hues cannot reach 4.5:1 contrast with white text at their perceptually "natural" saturation point without darkening so far that they lose their hue identity. If your brand is one of these colors, refer to Section E1 for resolution strategies.

Blue dominates this list for a reason: it sits in the sweet spot of perceptual darkness (high contrast with white), semantic neutrality (no association with danger, success, or warning), and near-universal positive affect (trust, calm, professionalism). When in doubt, blue is the safest primary hue for a button system.

K1. The Decision Sequence

Building a button color system is a chain of dependent decisions. Each choice constrains the next. Here is the order in which to make them.

StepDecisionConstrained by
1 Choose your color budget tier (2, 3, or 5 colors) Application complexity, audience
2 Choose your primary hue (brand-derived or independent) Brand palette, semantic collision check
3 Verify WCAG AA contrast with white and dark text; assign text color Hue's relative luminance
4 Choose danger color (nearly always red; verify non-collision with primary) Primary hue choice
5 Define hierarchy variants (filled, outlined, ghost, text) for each color Color budget × hierarchy matrix (Section D1)
6 Choose state derivation method (overlay vs. OKLCH lightness shift) Number of colors, theming complexity
7 Derive hover, active, focus, and disabled states for each base color Derivation method, WCAG verification
8 Build dark mode palette (parallel, not inverted) Light mode hues, desaturation rules
9 Define token architecture (primitive → semantic → component) Theme switching requirements
10 Validate: no more than one filled/chromatic button per section Composition rules (Section A2)
The test for a complete system. Show a colleague three buttons: your primary filled, your neutral outlined, and your danger text-only. If they can immediately identify which is the main action, which is secondary, and which is destructive — without reading the labels — the system works. If any ambiguity exists, revisit steps 2–5.

Part III of the Design Primitive Reference series · Continues from Part I (Shape, Typography, Proportion) and Part II (Contrast & Construction) · April 2026