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.
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.
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.
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.
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.
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.
btn-success, btn-info, and btn-warning, ask: when is someone supposed to use each, and can you explain the distinction in one sentence?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.
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.
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.
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.
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 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.
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:
And here is the same test across yellow, where the flip happens much earlier because yellow's inherent luminance is so high:
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.
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.
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.
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:
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.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.
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.
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.
Primary (Blue)
Neutral (Gray)
Danger (Red)
Primary (Blue)
Neutral (Gray)
Danger (Red)
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.
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.
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.
| Step | Decision | Constrained 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) |
Part III of the Design Primitive Reference series · Continues from Part I (Shape, Typography, Proportion) and Part II (Contrast & Construction) · April 2026