What contrast ratio and touch-target size should you meet, and how do you design for colour blindness?
Predict first
Compute the contrast ratio of three greys and blues on white. Which pass the AA text threshold of 4.5 and the large-text threshold of 3?
import Foundation
func luminance(_ hex: UInt32) -> Double {
func channel(_ v: UInt32) -> Double { let c = Double(v) / 255; return c <= 0.03928 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4) }
return 0.2126 * channel((hex >> 16) & 255) + 0.7152 * channel((hex >> 8) & 255) + 0.0722 * channel(hex & 255)
}
func ratio(_ a: UInt32, _ b: UInt32) -> Double {
let x = luminance(a), y = luminance(b)
return (max(x, y) + 0.05) / (min(x, y) + 0.05)
}
for (name, color) in [("#767676", UInt32(0x767676)), ("#777777", 0x777777), ("#0A84FF", 0x0A84FF)] {
let r = ratio(color, 0xFFFFFF)
print(name, String(format: "%.2f", r), "passes AA text:", r >= 4.5, "passes AA large text:", r >= 3)
}
- #767676 passes both; #777777 passes only large text; #0A84FF passes only large text
- All three pass AA text
- None pass any threshold
Mental model. Contrast is the difference between a road sign in daylight and the same sign in fog. The ratio tells you how foggy a person’s eyes make your screen.
Contrast is measured as a ratio between the relative luminance of the text and its background, from 1:1 (no difference) to 21:1 (black on white). The common standard (WCAG level AA) asks for 4.5:1 for normal text and 3:1 for large text and for meaningful UI parts such as icons and control borders (large is roughly 18 points, or 14 bold). The numbers are easy to check: the check below implements the formula and shows that #767676 on white is 4.54 (it just passes) while #777777 is 4.48 (it just fails), and that the system blue #0A84FF on white is only 3.65, so it is fine for large text and icons but not for small body text. Check contrast for both light and dark appearance, with the translucent materials the text really sits on, and for disabled and placeholder text where it is often forgotten (disabled controls are exempt, but low-contrast placeholder text is still hard to read).
Colour blindness affects roughly one in twelve men, most often red-green confusion, so never use colour as the only signal: pair it with an icon, a label, a pattern or position (a green tick and a red cross, not two coloured dots), and use colour palettes that remain distinguishable (the simulator and Accessibility Inspector can simulate colour deficiencies; use them on charts and status indicators). For touch targets, Apple’s guidance is at least 44 by 44 points; WCAG 2.2 has a smaller minimum, so treat 44 as your bar. A small icon can have a larger hit area by padding or overriding point(inside:with:) (UIKit) or .contentShape with padding (SwiftUI), and adjacent targets need spacing so a tap lands on the intended one. Larger targets also help everyone using a phone one-handed on a bus.
The cut-off is tight: a one-step change in grey flips the result, which is why you measure instead of guessing by eye.
Common traps
- Light grey text on white “because it looks refined”. A ratio under 4.5:1 is hard to read for many people.
- Red and green as the only difference between success and error. It disappears for many users; add an icon or text.
They might follow up with
- How do you make a small icon easy to tap? Keep the visible size but extend the hit area to at least 44 points, with spacing from neighbours.
- Where do you check contrast? The Accessibility Inspector’s colour contrast calculator, design tools, and the audit in tests.
Related questions
- 24.2.2 How do you respect Reduce Motion, Increase Contrast, Bold Text, Reduce Transparency, Differentiate Without…
- 24.2.1 How does Dynamic Type work, how do you support it in UIKit and SwiftUI, and how do layouts adapt at…
- 24.5.1 How do you test accessibility: the Accessibility Inspector, automated audits in XCUITest, identifiers versus…
← How do you respect Reduce Motion, Increase Contrast, Bold Text…