Journal

WCAG compliant color palette in SwiftUI: a practical build

A compliant palette meets the contrast ratios and never lets color be the only signal. In SwiftUI that means semantic asset colors and a second cue.

WCAG compliant color palette in SwiftUI: a practical build: the App Store logo as a glossy glass icon on a purple and blue gradient with floating bubbles

TL;DR

A WCAG compliant color palette in SwiftUI comes down to two things: every text and interface color meets the required contrast ratio against its background, and color is never the only way you communicate meaning. In practice that means semantic colors in the asset catalog with light, dark, and high-contrast variants, leaning on Apple's tuned system colors, and pairing color with text or symbols. For a regulated app where accessibility is a requirement, the fastest reliable path is to start from a free VP0 design and let Claude Code or Cursor read its source page, then verify the contrast rather than inventing colors and hoping they pass.

A WCAG compliant color palette in SwiftUI comes down to two things: every text and interface color meets the required contrast ratio against its background, and color is never the only way you communicate meaning. In practice that means defining semantic colors in your asset catalog with light, dark, and high-contrast variants, leaning on the system colors Apple already tuned, and pairing color with text or symbols for anything important. For a regulated or enterprise app where accessibility is a requirement rather than a nicety, the fastest reliable path is to start from a free VP0 design and let Claude Code or Cursor read its source page, then verify the contrast rather than inventing colors and hoping they pass.

Contrast failures are the most common accessibility issue in shipped apps, and they are entirely preventable with a palette built to the numbers from the start. The sections below cover the ratios, the SwiftUI implementation, and what compliance needs beyond contrast.

What makes a color palette WCAG compliant?

A palette is WCAG compliant when its color pairings, text on background, interface elements against their surroundings, meet the contrast ratios in the guidelines, and when meaning never depends on color alone. Those are two separate requirements, and a palette that nails contrast can still fail by using red and green as the only signal of error and success.

Contrast is measured as a ratio between the lighter and darker color, and the WebAIM contrast checker is the standard tool for computing it. The guidelines set minimum ratios by text size and conformance level, which the next section lays out. The second requirement, not relying on color alone, matters because color vision deficiency affects around 8% of men, so a palette that distinguishes states only by hue is unreadable for a meaningful share of users. Apple’s accessibility guidance treats both as baseline expectations.

The contrast ratios you actually need

WCAG sets contrast minimums by conformance level and text size, and knowing the four numbers that matter saves a lot of guesswork.

WhatAA minimumAAA minimum
Normal text4.5:17:1
Large text (about 18pt, or 14pt bold)3:14.5:1
UI components and graphical objects3:13:1

Most apps target AA, where body text needs 4.5:1 and large text or interface elements need 3:1. AAA, at 7:1 for body text, is a higher bar that some regulated and government contexts require but that constrains the palette significantly. The practical approach is to design to AA across the board and reach for AAA only where a requirement or a specific readability need calls for it. The components side of this is covered in UI components that pass WCAG AAA on iOS, and a fuller kit in a WCAG compliant mobile app UI kit.

Building the palette in SwiftUI

In SwiftUI, a compliant palette is best built from named colors in the asset catalog rather than hardcoded values scattered through the code. You define each semantic color, a primary text color, a background, an accent, once in the catalog, give it light and dark appearance variants, and reference it by name everywhere.

extension Color {
    static let textPrimary = Color("TextPrimary")
    static let surface = Color("Surface")
    static let accent = Color("AccentPrimary")
}

Text("Balance")
    .foregroundStyle(.textPrimary)
    .background(.surface)

Two things make this hold up well. First, lean on Apple’s semantic system colors like .primary, .secondary, and the label and fill colors where you can, because Apple already tuned them to meet contrast in both light and dark mode, so you inherit compliance instead of re-deriving it. Apple documents these in its color guidance, and the system semantic colors are the safest starting point because their contrast is already handled across appearances. Second, give your custom colors a High Contrast variant in the asset catalog, which the system uses automatically when a user turns on Increase Contrast, so the palette strengthens for those who need it without any code branching. Defining colors semantically also means dark mode is a matter of filling in the dark variant rather than rewriting views, the same approach as a Telegram-style dark mode color palette in SwiftUI.

Beyond contrast: color is never the only signal

Meeting contrast ratios is necessary but not sufficient. The other half of a compliant palette is that color never carries meaning by itself, because users with color vision deficiency, or anyone glancing quickly, will miss it. An error shown only in red, or a required field marked only by a colored outline, fails this even if the contrast is perfect.

The fix is to pair color with a second signal: an icon, a label, a shape, or text. An error field gets a warning symbol and a message, not just a red border; a status gets a word or an icon alongside its color. SwiftUI exposes the user’s preference through the accessibilityDifferentiateWithoutColor environment value, so you can add shape or text cues specifically when that setting is on. This is also where Dynamic Type intersects the palette, since text has to stay legible as it scales, and a contrast ratio that holds at one size should hold at all of them; the Dynamic Type scaling text bug fix covers keeping text readable as it grows.

Testing your palette for compliance

A palette is only compliant if you verify it, not if it looks fine. The core check is running every text-and-background and element-and-surround pairing through a contrast checker and confirming it meets at least 3:1 for large elements and 4.5:1 for body text. Do this for both light and dark mode, because a pairing that passes in light can fail in dark, and for the high-contrast variants too.

Beyond the numbers, test the real conditions: turn on Increase Contrast and confirm the palette strengthens, enable a color filter or grayscale to confirm nothing relies on hue alone, and crank Dynamic Type to the largest size to confirm text stays legible at contrast. Doing this during design, rather than after a compliance audit flags it, is far cheaper, and it catches the pairings that look fine to a designer with full color vision but fail the measurement. The high-contrast direction specifically is explored in a high contrast mode iOS UI kit.

Making it compliant with AI and a real design

AI builders pick colors that look good and fail contrast. Ask Claude Code or Cursor for “a nice color palette” and you get tasteful hex values that have never been measured, with body text at maybe 3:1 against its background, well short of the 4.5:1 it needs. The result looks polished and fails an audit, which is the worst combination because it is not obviously wrong.

A real, already-compliant design plus an explicit contrast rule fixes most of it. When the palette is defined as semantic colors that meet the ratios, the model applies them correctly instead of inventing values, and you verify rather than design from scratch. Starting from a free VP0 design gives that structure, since each design has a machine-readable source page Claude Code, Cursor, or Rork read from a pasted link. Always tell the model the target is WCAG AA and to use semantic asset colors, not inline hex, and verify the result, because “make it accessible” is not something an AI builder reliably gets right on its own.

Common WCAG palette mistakes

A few mistakes recur. Hardcoding hex values throughout the code is the first, which makes the palette impossible to adjust or give dark and high-contrast variants; define semantic colors in the asset catalog instead. Designing only for light mode is the second, then discovering the dark variant fails contrast because it was an afterthought.

Relying on color alone for status, errors, or required fields is the third, and it fails compliance regardless of contrast; pair color with text or an icon. Skipping the High Contrast variant is the fourth, so users with Increase Contrast on get no benefit. The fifth is never measuring, shipping a palette that looks fine but was never run through a contrast checker, which is how most contrast failures reach production.

When AAA is overkill

Not every app needs WCAG AAA. The 7:1 body-text ratio of AAA constrains a palette heavily, pushing toward near-black text on near-white backgrounds and limiting the use of color, which can work against a brand’s identity for little real-world gain over AA. For most consumer apps, well-implemented AA is both compliant and readable, and chasing AAA everywhere is effort spent past the point of benefit.

AAA earns its place where a requirement demands it, certain government, healthcare, or accessibility-first products, or where a specific audience genuinely needs the extra contrast. The honest approach is to build solidly to AA, then raise specific surfaces to AAA where it is required or clearly helps, rather than forcing the whole palette to the highest bar by default. Match the conformance target to the app’s actual obligations and audience.

Key takeaways: a compliant color palette in SwiftUI

Build the palette from semantic colors in the asset catalog with light, dark, and high-contrast variants, lean on Apple’s tuned system colors, and target WCAG AA: 4.5:1 for body text, 3:1 for large text and interface elements. Never let color be the only signal, pair it with text or icons and honor the differentiate-without-color setting. Verify every pairing with a contrast checker in both light and dark mode, and reach for AAA only where required. Let an AI builder apply a compliant palette from a real design, then measure the result. A commissioned accessibility audit and redesign can cost $5,000 or more, while starting from a free VP0 design gives you a structured, verifiable palette to build on for nothing.

You can browse VP0 designs to start from a real, structured screen rather than picking colors that have never been measured.

Frequently asked questions

What makes a color palette WCAG compliant in SwiftUI?

Every text-and-background and element-and-surround pairing meets the WCAG contrast ratios, 4.5:1 for body text and 3:1 for large text and interface elements at AA, and color is never the only way meaning is conveyed. In SwiftUI you achieve this by defining semantic colors in the asset catalog with light, dark, and high-contrast variants, using Apple’s tuned system colors where possible, and pairing color with text or icons. Starting from a free VP0 design gives you a structured palette to verify rather than invent.

What contrast ratio does WCAG require?

At AA, body text needs a contrast ratio of at least 4.5:1 against its background, while large text, about 18 point or 14 point bold, and interface components need at least 3:1. AAA raises body text to 7:1 and large text to 4.5:1. Most apps target AA, which is both compliant and readable; AAA is reserved for contexts that specifically require it. Verify each pairing with a contrast checker in both light and dark mode.

How do I support dark mode and high contrast in a SwiftUI palette?

Define each color as a named asset in the asset catalog and give it light, dark, and High Contrast appearance variants, then reference it semantically in code. The system automatically uses the dark variant in dark mode and the high-contrast variant when a user enables Increase Contrast, so you get all three from one definition without branching logic. Verify that the dark and high-contrast variants meet the same ratios as the light one, since a pairing that passes in light can fail in dark.

Can VP0 provide a free SwiftUI template with an accessible color palette?

Yes. VP0 is a free iOS app design library where every design has a machine-readable source page an AI builder reads from a pasted link, with SwiftUI variants. You start from a structured design and have Claude Code, Cursor, or Rork apply semantic asset colors targeting WCAG AA, then verify the contrast with a checker, rather than picking hex values that have never been measured. Always confirm the ratios, since accessibility is something to verify rather than assume.

What common errors happen when building a WCAG color palette?

The frequent ones are hardcoding hex values so the palette cannot get dark or high-contrast variants, designing only for light mode so the dark variant fails contrast, relying on color alone for errors or status, skipping the High Contrast variant, and never running pairings through a contrast checker. The fixes are semantic asset colors with all three variants, a second non-color signal for meaning, and measuring every pairing in both light and dark mode before shipping.

Other questions VP0 users ask

What makes a color palette WCAG compliant in SwiftUI?

Every text-and-background and element-and-surround pairing meets the WCAG contrast ratios, 4.5:1 for body text and 3:1 for large text and interface elements at AA, and color is never the only way meaning is conveyed. In SwiftUI you achieve this by defining semantic colors in the asset catalog with light, dark, and high-contrast variants, using Apple's tuned system colors where possible, and pairing color with text or icons. Starting from a free VP0 design gives you a structured palette to verify rather than invent.

What contrast ratio does WCAG require?

At AA, body text needs a contrast ratio of at least 4.5:1 against its background, while large text, about 18 point or 14 point bold, and interface components need at least 3:1. AAA raises body text to 7:1 and large text to 4.5:1. Most apps target AA, which is both compliant and readable; AAA is reserved for contexts that specifically require it. Verify each pairing with a contrast checker in both light and dark mode.

How do I support dark mode and high contrast in a SwiftUI palette?

Define each color as a named asset in the asset catalog and give it light, dark, and High Contrast appearance variants, then reference it semantically in code. The system automatically uses the dark variant in dark mode and the high-contrast variant when a user enables Increase Contrast, so you get all three from one definition without branching logic. Verify that the dark and high-contrast variants meet the same ratios as the light one, since a pairing that passes in light can fail in dark.

Can VP0 provide a free SwiftUI template with an accessible color palette?

Yes. VP0 is a free iOS app design library where every design has a machine-readable source page an AI builder reads from a pasted link, with SwiftUI variants. You start from a structured design and have Claude Code, Cursor, or Rork apply semantic asset colors targeting WCAG AA, then verify the contrast with a checker, rather than picking hex values that have never been measured. Always confirm the ratios, since accessibility is something to verify rather than assume.

What common errors happen when building a WCAG color palette?

The frequent ones are hardcoding hex values so the palette cannot get dark or high-contrast variants, designing only for light mode so the dark variant fails contrast, relying on color alone for errors or status, skipping the High Contrast variant, and never running pairings through a contrast checker. The fixes are semantic asset colors with all three variants, a second non-color signal for meaning, and measuring every pairing in both light and dark mode before shipping.

Part of the B2B, Enterprise, Healthcare & Industry Apps hub. Browse all VP0 topics →

Keep reading

Dynamic Type Text Scaling Bugs in SwiftUI: The Fix: a phone toggle icon surrounded by location, calendar, settings, wallet and chart app icons on a coral gradient
Workflows 5 min read

Dynamic Type Text Scaling Bugs in SwiftUI: The Fix

SwiftUI text clipping or truncating when users scale font size? Here is why Dynamic Type breaks layouts and how to make text scale gracefully.

Lawrence Arya · June 1, 2026
Face ID Biometric Login Screen in SwiftUI: Done Right: the App Store logo as a glossy glass icon on a purple and blue gradient with floating bubbles
Guides 6 min read

Face ID Biometric Login Screen in SwiftUI: Done Right

Face ID gates local access, it does not replace server auth. Here is the SwiftUI pattern: LocalAuthentication, a Keychain-stored token, and a real fallback.

Lawrence Arya · June 4, 2026
B2B Wholesale Order Matrix Grid UI in SwiftUI: a glass iPhone UI wireframe icon on a holographic purple gradient
Guides 4 min read

B2B Wholesale Order Matrix Grid UI in SwiftUI

Build a wholesale order matrix in SwiftUI: products by size and variant in a fast bulk-entry grid, account pricing, and net terms, from a free VP0 design.

Lawrence Arya · May 31, 2026
iOS UI Components That Pass WCAG AAA: What It Really Takes: a reflective 3D App Store icon on a blue and purple gradient
Guides 9 min read

iOS UI Components That Pass WCAG AAA: What It Really Takes

AAA is a per-criterion achievement, not a site-wide badge. Here is which WCAG AAA criteria iOS components can truly meet, and how to verify the claim.

Lawrence Arya · June 10, 2026
ADHD Daily Routine Planner UI in SwiftUI, Free: a vivid neon 3D App Store icon on an orange, pink and blue gradient
Guides 5 min read

ADHD Daily Routine Planner UI in SwiftUI, Free

Build an ADHD-friendly daily routine planner in SwiftUI from a free template. Visual, low-friction, time-aware design with Claude Code or Cursor.

Lawrence Arya · June 1, 2026
Fix Broken Arabic RTL Layouts in AI-Generated iOS Apps: the App Store logo as a glossy glass icon on a purple and blue gradient with floating bubbles
Guides 5 min read

Fix Broken Arabic RTL Layouts in AI-Generated iOS Apps

ChatGPT and Cursor often hardcode left-to-right layouts, so Arabic text looks broken. Here is how to make iOS layouts flip correctly, using a free VP0 design.

Lawrence Arya · May 31, 2026