- CTA
- Color Contrast
- UI Design
- Buttons
CTA Button Colors: Contrast, States, and “Is It Loud Enough?”
Design primary CTA colors that pass WCAG, stay on brand, and remain clear in resting, hover, disabled, and focus states—with invented before/after hex examples.
Sherotree
·6 min read
A call-to-action can fail in two opposite ways: it is so soft it disappears into the page, or so loud that the label becomes unreadable. Designers usually notice the first problem in critique. Users notice the second when they cannot parse “Start free trial” on a neon fill.
This article focuses on button label contrast, state styling, and the tension between brand saturation and WCAG thresholds. All checks can be done with free tools on Uwarp—start with the Colour Contrast Checker.
What “loud enough” should mean
Loud enough means:
- The control is easy to find among secondary actions (hierarchy).
- The label meets contrast requirements on the fill (legibility).
- States still communicate without relying on color alone.
It does not mean maximum saturation. A slightly deeper brand fill with crisp white type often outperforms a brighter fill with grayish white overlays or thin letter-spacing tricks.
Targets for CTA labels
Button labels are usually normal text for WCAG purposes unless the type is truly large/bold by the WCAG definition (~18pt+ or 14pt+ bold). Most product CTAs at 14–16px regular should aim for:
- AA: ≥ 4.5:1 label vs fill
- AAA (optional): ≥ 7:1 for high-stakes or long-session products
Large display CTAs on marketing heroes may qualify for the large-text threshold (≥3:1 at AA), but many teams still keep 4.5:1 so the same component token works in denser UI.
Resting, hover, disabled, focus
Design four states as separate contrast pairs—not as opacity afterthoughts.
| State | Color job | Contrast note |
|---|---|---|
| Resting | Primary fill + label | Label ≥4.5:1 on fill |
| Hover / pressed | Darken or lighten fill; keep label | Re-check label on the new fill; do not assume resting pass carries over |
| Focus | Visible focus ring vs surrounding page | Ring should be visible against both the button and the background behind it (non-text contrast thinking: aim for a clear 3:1-style separation) |
| Disabled | Muted fill + muted label or clearer “unavailable” pattern | Avoid 2:1 gray-on-gray that looks broken; explain why nearby if the action matters |
Focus rings that survive brand colors
A white button on a white page needs a dark ring (or a dual ring). A purple button on a purple wash needs a ring that is not also purple. Test focus on the real section background, including hero images—pair decorative backdrops with the habits from SVG/background work and still verify text/button contrast with the contrast checker.
Brand constraints: keep the hue, move the role
Marketing wants the logo color on the CTA. Engineering wants one token. Accessibility wants a readable label. Resolve this with roles:
cta-fill— brand-forward, may be the seedcta-label— white or near-white or a near-black if the fill is pastelcta-fill-hover— systematically darker (light theme) or lighter (dark theme)cta-fill-disabled— neutral, not “seed at 30% opacity” by default
If white-on-seed fails 4.5:1, you have three honest fixes:
- Darken the fill until white passes.
- Switch the label to near-black if the fill is intentionally light (common for secondary “soft” CTAs).
- Split tokens: keep the bright seed for large icons/illustrations; use a darker sibling for button fills.
Generate darker/lighter siblings from the seed with Accessible Brand Colors, then confirm the button pair in the contrast checker.
Before / after hex examples (invented)
These pairs are teaching examples—not a brand kit. Always re-check in the tool; do not ship from memory.
Example A — Vivid coral that fights white labels
Before
- Fill:
#FF6B4A - Label:
#FFFFFF - Risk: vivid warm fills often sit near the AA edge for small type; hover at
#FF8166can make it worse.
After
- Fill resting:
#E2573A - Fill hover:
#C9472F - Label:
#FFFFFF - Soft marketing accent (non-text): keep
#FF6B4Afor illustrations only
Result: same hue family, safer label pair, hover still darker for affordance.
Example B — Pastel “friendly” CTA that disappears
Before
- Fill:
#DBEafe - Label:
#60A5FA - Hierarchy fails and contrast fails: light blue on lighter blue.
After
- Fill:
#2563EB - Label:
#FFFFFF - Optional secondary soft button: fill
#EFF6FF, label#1D4ED8(check ≥4.5:1)
Result: primary CTA is findable; soft variant remains for low-emphasis actions.
Example C — Dark theme primary
Before
- Fill:
#818CF8 - Label:
#0F172A— may pass, but hover to#A5B4FCcan drop label contrast.
After
- Fill resting:
#6366F1 - Fill hover:
#818CF8with label#0F172Aonly if that pair still passes; otherwise keep label#FFFFFFon a darker fill#4F46E5 - Focus ring:
#F8FAFCouter +#0F172Agap, or a light ring tested on the dark page background
Lock whichever pair you choose by measuring both resting and hover in the colour contrast checker.
Outline and ghost CTAs
Ghost buttons (transparent fill + border + colored label) create three relationships: label vs page, border vs page, and hover fill vs label. Check the label against the page (resting) and against the hover fill (hover). Many ghost CTAs pass at rest and fail on hover when a soft tint appears behind a mid-tone label.
Secondary vs primary hierarchy without contrast debt
- One solid primary per view for the forward path.
- Secondary as outline or neutral solid with its own passing label pair.
- Tertiary as text button using
primary-text-on-surface, not a third neon fill.
If everything is saturated, nothing is a CTA—and contrast bugs multiply.
CTA contrast checklist
- Resting label vs fill ≥ 4.5:1 (or documented large-text exception).
- Hover/pressed label vs fill re-checked.
- Focus ring visible on the real section background.
- Disabled state is unmistakable without relying on ~2:1 text.
- Ghost/outline hover state checked as its own pair.
- Soft brand tints are not used as primary fills unless labels are dark and passing.
- Dark theme CTAs validated separately.
- Hex pairs recorded next to tokens; verified in Colour Contrast Checker.
- If the seed cannot work as a fill, a darker sibling exists via Accessible Brand Colors.
Quick build recipe
- Drop the brand seed into Accessible Brand Colors.
- Candidate a fill that supports white (or near-black) labels.
- Confirm resting + hover in the contrast checker.
- Add focus and disabled as explicit styles.
- Browse related utilities under color tools if you need supporting scales.
Related tools
Suggested next article
Return to the WCAG Color Contrast Guide for body text, UI chrome, and the failure cases that show up outside buttons—then keep CTAs as the strictest pair in your file.


