3.10 — Color psychology & palette design
Opening scenario
You’re a solo dev building a finance app. You pick “vibrant orange and electric purple” because they’re your favorite colors. You ship. Users on Reddit say it looks “like a toy” and “unprofessional.” Your install-to-account-link conversion is 12%. You repaint to charcoal + navy blue. Conversion jumps to 31%. Same product, same code — the color told users to trust you.
Color is not decoration. It’s a signal of category, trustworthiness, and emotional register. This chapter teaches the patterns: which palettes work for which app categories, how to compose them with the 60-30-10 rule, how to verify them with contrast tools, and how to ship them to iOS with light/dark variants.
| Color | Common associations |
|---|---|
| Blue | Trust, stability, professionalism (banks, healthcare, productivity) |
| Green | Growth, money, health (finance, fitness, nature) |
| Red | Urgency, food, passion, danger (food delivery, sports, alerts) |
| Orange | Energy, friendliness, retail (consumer, kids, coupons) |
| Purple | Creativity, luxury, beauty (cosmetics, music, kids) |
| Pink | Femininity, youth, dating (dating, gen-Z, kids) |
| Black/Gray | Premium, minimalist, fashion (luxury, photography, dev tools) |
| Yellow | Optimism, attention, warning (children’s, alerts) |
These are not universal — culture matters — but they’re the dominant Western/US/EU mappings, which is what App Review and the App Store algorithm evaluate.
Concept → Why → How → Code
Color by app category
A defensible palette starts with category research. Open the App Store, search your category, screenshot the top 20 apps. Note their primary brand color. Patterns:
- Finance / Banking: Cash App (green), Robinhood (green), Chase (blue), Bank of America (red+blue), Venmo (blue), PayPal (blue), Stripe (purple). Trust signals dominate; saturated brand color over neutral surface.
- Health / Fitness: MyFitnessPal (blue), Strava (orange), Apple Fitness (red+orange gradient), Headspace (orange + gradient). Energy and warmth; often a vibrant secondary accent.
- Entertainment / Streaming: Netflix (red), Spotify (green), HBO (purple/black), Disney+ (blue-black), Twitch (purple). Dark backgrounds + saturated brand — let content shine, frame it with confidence.
- Productivity: Notion (white+black), Linear (purple+black), Things (blue), Reminders (orange), Asana (orange+pink). Restrained, often near-monochrome with one accent.
- Social / Dating: Tinder (red/pink), Bumble (yellow), Hinge (red), Instagram (gradient pink-purple-orange). Saturated, warm, playful.
- Kids / Education: Duolingo (green), Khan Academy (green), Lego (yellow+red+blue), Toca Boca (rainbow). Primary colors, high saturation, playful.
- Travel / Maps: Airbnb (coral-red), Booking (blue+yellow), Google Maps (white+colored), Lyft (pink). Welcoming, often with one bold accent.
The exercise: when designing a new app, list 5 competitors → screenshot → find the median palette. Stay within ±20° on the color wheel from the median unless you have a defensible reason. Going too far signals “different” but often reads as “wrong category.”
The 60-30-10 rule
A balanced palette uses:
- 60% — dominant color (usually a neutral or background)
- 30% — secondary color (supporting surfaces, secondary actions)
- 10% — accent color (primary actions, brand emphasis)
In a SwiftUI app:
ZStack {
Color(.systemBackground) // 60% — most of the screen
VStack {
Card() // 30% — secondary surface
.background(Color(.secondarySystemBackground))
Button("Buy") { … } // 10% — accent (the brand color)
.buttonStyle(.borderedProminent)
}
}
Apps that violate this — 60% bright brand color — feel exhausting after 30 seconds. The eye needs rest space.
Apple’s semantic system as the 60-30
Color(.systemBackground), Color(.secondarySystemBackground), Color(.tertiarySystemBackground) already do the 60-30 for you. You just add the 10% accent:
// In your App or root view
.tint(.brandPrimary)
// Now every Button, Toggle, NavigationLink, ProgressView, etc.
// uses brandPrimary as its accent — automatically.
.tint(_:) is the single most powerful color modifier in SwiftUI. Set it once at the top, get a consistent accent everywhere. No per-component overrides.
Tools for picking palettes
When you’re not just picking a single brand color, use these:
- Coolors.co — generate 5-color palettes, lock favorites, regenerate. Free, fast, great for ideation.
- Adobe Color — color wheel with harmony rules (analogous, triadic, complementary, monochrome). Best for understanding why a palette feels balanced.
- Color Hunt — curated palettes by category. Browse the “Pastel” or “Dark” sections to spark ideas.
- Contrast — Mac app, menu bar utility, pick two pixels → instant WCAG ratio.
- Realtime Colors — paste a palette, see it applied to a sample app UI live. Best “does this work?” tool.
- uiGradients — for picking gradient pairs.
The workflow: Coolors for ideation → Adobe Color to verify harmony → Realtime Colors to preview on a UI → Contrast to verify WCAG.
The Apple semantic palette (re-stated as a checklist)
Every app needs at least these semantic colors, defined in Asset Catalog with Light/Dark variants:
// Brand
brandPrimary // your main brand color
brandSecondary // accent / link
// Surfaces (or use Apple's systemBackground family)
surfacePrimary // page background
surfaceSecondary // card / sheet background
surfaceTertiary // inset / grouped background
// Content (or use Apple's label family)
contentPrimary // body text
contentSecondary // secondary text
contentTertiary // disabled / placeholder
// Semantic state
success // green
warning // yellow / orange
error // red
info // blue
That’s 10-12 colors. Most successful apps fit in this set. More colors = more drift, more inconsistency, more decisions per screen.
Dark mode color pairs
Dark mode is not “invert” — it’s a separate palette tuned for low-light viewing. Rules of thumb:
| Light mode | Dark mode equivalent |
|---|---|
Pure white background #FFFFFF | Near-black #0A0A0A or #1C1C1E (Apple’s systemBackground) — never pure black |
Pure black text #000000 | Off-white #F2F2F7 (Apple’s label) |
| Vibrant accent at 100% saturation | Same accent at ~85% saturation (less aggressive on dark) |
| Subtle shadow | Subtle glow / lighter border |
Why not pure black in dark mode? OLED display “smearing” — pure black next to bright content causes ghosting. Apple uses #1C1C1E.
Setup in Asset Catalog:
- New Color Set → “brandPrimary”
- Attributes Inspector → Appearances: Any, Dark
- Set Any (light mode) to your brand color
- Set Dark to the desaturated/adjusted version
- Reference:
Color(.brandPrimary)or generatedColor.brandPrimary
Same color name, two values, automatic adaptation.
WCAG contrast checking
For every text-on-background combination in your palette:
- Body text on background: ≥ 4.5:1
- Large text (≥ 18pt or ≥14pt bold) on background: ≥ 3:1
- UI components (icons, borders): ≥ 3:1
If your brand color is light (yellow, light blue, pink), it likely fails 4.5:1 against white. Solutions:
- Use brand color only on large or bold text
- Use brand color only as background with white/dark text on top
- Use brand color only for icons and accents, never body text
Check every pair. The Contrast app makes this 2-second work.
App icon color strategy
Your app icon is your most-viewed color choice. Patterns:
- Solid brand color, white glyph: Spotify, Cash App, Robinhood. Simple, recognizable at every size.
- Gradient brand: Instagram, Apple Health, Apple Fitness. Eye-catching but harder to read at 60×60.
- Photographic / detailed: Bear, Threes! Art-feels but loses detail at small sizes.
- Two-color flat: Apple Notes (yellow lines on white), Apple Music (pink-red gradient). Tested at every size.
Test your icon at the actual sizes: 60×60 (iPhone home screen), 76×76 (iPad), 120×120 (retina spotlight), 1024×1024 (App Store). Apple’s Icon Composer tool renders all sizes; preview before shipping.
For iOS 26 Liquid Glass (introduced WWDC25), provide a “Tinted” icon variant (system tints your icon to user’s wallpaper) and a “Dark” variant.
Avoiding palette drift in code
Once defined, the palette must not drift. Enforce:
- SwiftLint custom rule: ban
Color(red:),Color(hex:),Color(white:). ForceColor.brandPrimarystyle. - Generated asset symbols (Xcode 15+):
Color(.brandPrimary)is compile-checked. - PR template checkbox: “If you added a color, did you add it to the design tokens module?”
- Quarterly audit:
grep -rn "Color(" --include="*.swift"— anything not from tokens gets fixed.
In the wild
- Stripe uses purple as a distinctive payment-app color (in a sea of blue) — successful differentiation within finance. Their site and docs are case studies in restrained palette use.
- Linear’s palette: near-monochrome (gray scale) plus one signature purple. 95% of the UI is neutral; the purple is reserved for active states. Studied widely in design system communities.
- Duolingo’s green: paired with a friendly typography and Duo’s voice, it signals “approachable learning.” If they’d picked navy, it would feel like an enterprise LMS. Brand color choice is product strategy.
- Cash App rebranded from green to black-and-green in 2018; conversion analytics showed the more sophisticated palette converted higher-value users.
- Twitter → X color change from blue to black caused widespread brand-recognition drop (well documented in marketing case studies). Color is brand equity.
Common misconceptions
- “My favorite color should be the brand color.” Rarely. Brand color should match category and target user, not founder preference.
- “More colors = more energy = better.” No. Restraint is a marker of professional design. Most great apps use 3-5 colors total.
- “Dark mode is just inverted light mode.” Wrong — different colors entirely, especially desaturated accents.
- “WCAG AA contrast is too restrictive for cool designs.” Inaccessible cool designs are bad designs. AA is achievable with creativity; AAA is hard but AA is non-negotiable.
- “Color psychology is pseudoscience.” The cultural mappings (red = food, blue = trust in Western markets) are empirically supported in consumer behavior research. The individual-mood claims (yellow makes you happy) are weak. Use the category data, ignore the rest.
Seasoned engineer’s take
For new apps, the order of operations on color:
- Category research — screenshot 20 competitors, find the median palette
- Pick a primary brand color — within ±30° of category median unless you have a defensible reason
- Generate complementary palette — Coolors or Adobe Color for harmony
- Verify WCAG contrast — Contrast app
- Define dark mode pairs — desaturate brand, near-black bg
- Define semantic tokens —
brandPrimary,surface,content,success/warning/error/info - Wire
.tint()at App root — every accent gets the brand color for free - Test in Realtime Colors — paste palette, preview on mock UI
- Add to Asset Catalog with light/dark + high-contrast appearances
- Lint to prevent drift
Spend a day on this once. The palette will outlive 90% of your code.
TIP: Always build a “color audit” screen in your app — a screen that shows every color token, light + dark, with WCAG ratios. Saves you from a future “is this still our brand color?” question. Keep it in a
#if DEBUGbuild configuration.
WARNING: Don’t ship colors directly from Coolors palettes without WCAG checking. Trendy palettes from Coolors often fail contrast for body text. Verify.
Interview corner
Junior-level: “How would you pick a color palette for a new app?”
Research competitors in the category. Pick a primary brand color aligned with category expectations. Use a tool like Coolors or Adobe Color to generate complementary supporting colors. Verify WCAG contrast for all text/background pairs. Define light and dark variants in Asset Catalog.
Mid-level: “What’s the 60-30-10 rule and why does it matter?”
60% dominant (usually neutral background), 30% secondary (surfaces), 10% accent (brand). It prevents palette overload, gives the eye rest space, and ensures the brand color is special (not exhausting). Apple’s semantic colors already do the 60-30; you add the 10% via .tint().
Senior-level: “Design a color system that works across iOS, watchOS, macOS, and Apple Vision Pro for a single brand.”
Define brand primary + 2-3 brand accents at the primitive token level. Define semantic tokens (surface, content, state) per platform — watchOS uses near-black backgrounds, macOS supports more saturated colors (larger surface area), visionOS prefers translucent materials. Single source in Figma variables → Tokens Studio export → Style Dictionary → generates platform-specific token files. CI ensures sync. App-level .tint() ties it all together. Verify WCAG on every platform; visionOS has unique contrast requirements with translucent backgrounds.
Red flag in candidates: Picking colors based purely on aesthetics with no category research, no contrast check, no semantic naming. Tells you they haven’t shipped a product that needed to convert.
Lab preview
You’ll derive a full palette from a one-sentence brief in Lab 3.3 — Palette from Brief.
Phase 3 chapters complete. The labs apply everything: