Name colors by role, keep dark and high-contrast variants in one color set, use the symbols Xcode generates and check contrast with numbers — plus the grey that failed our own accessibility tests.
Colors look like the easiest part of an app until you add dark mode, a second platform and a designer who renames "blue" for the third time. A little structure in the asset catalog saves a lot of hex codes scattered through the code.
Name colors by role, not by value
A color called blue500 tells you what it looks like; a color called textSecondary or statusSuccess tells you what it is for. Role-based names survive redesigns: when the brand blue changes, you edit one color set and every screen follows. Start with a small set — background, elevated surface, primary and secondary text, accent, success, warning, error — and add more only when a real need appears.
One color set, several appearances
A Color Set in the asset catalog can hold different values for different conditions:
- Any and Dark appearances, so the system switches automatically with dark mode;
- High Contrast variants for people who turn on Increase Contrast in Accessibility settings;
- a color space per value — sRGB for most colors, Display P3 when you need more saturated tones on modern screens.
Before reaching for custom colors, check the system ones: label, secondaryLabel, systemBackground and friends already adapt to dark mode and contrast settings.
Use the generated symbols
Since Xcode 15, every color and image in an asset catalog gets a generated Swift symbol. No more string names that break silently after a rename:
// SwiftUI: Xcode generates .statusSuccess and .surfaceElevated from the asset catalog
Text("Connected")
.foregroundStyle(Color(.statusSuccess))
.padding()
.background(Color(.surfaceElevated))
// UIKit uses the same generated resources
let tint = UIColor(resource: .brandAccent)If a color is renamed or deleted, the build fails instead of the app quietly showing nothing.
Check contrast with numbers
Readable text needs a contrast ratio of at least 4.5:1 against its background, or 3:1 for large text (WCAG AA). Eyeballing is not enough. While building our own console we picked a warm grey, #6F6B63, for secondary text on a #111111 card. It looked fine on our screens, but the measured contrast was only 3.56:1, and our automated accessibility tests rejected it. Switching to #A39F95 raised it to about 5.6:1 without changing the look of the design.
| Check | Target |
|---|---|
| Body text vs background | 4.5:1 or more |
| Large text (about 18 pt and up) | 3:1 or more |
| Dark mode | Measure again — dark variants fail differently |
| High Contrast | Provide variants for key text and controls |
Keep palettes consistent across apps
When one studio ships many apps, consistency becomes its own problem: every project starts with someone picking colors by hand. We built ColorsGen for exactly that moment. It builds a harmonious palette from a base color you choose — or generates one at random — and prepares it for Xcode in a few taps, on iPhone, iPad and Mac. For icons and image assets, see our guide to app icon sizes in 2026.
Tip
Add a contrast check to your UI tests or design review. It is the cheapest accessibility fix there is — and it catches problems before users do.