 May 25, 2023

# Calculating Color Contrast Ratio in Swift

When it comes to dynamic templating of apps sometimes it’s helpful to determine at runtime if two colos have enough constrast so that one can be the background color and the other the foreground color. The W3C defines in it’s Web Content Accessibility Guidelines (WCAG) 2.0 a formula to find this ratio. Let’s take a look on how to implement this calculation using a Swift UIColor extension.

We start by creating a simple UIColor extension:

``````import UIKit

extension UIColor {

}
``````

We then implement the logic that returns the relative luminance of a color, according to the W3C specification. This is in practice a Swift computed varibale in the UIColor class:

``````var luminance: CGFloat {
let RED: CGFloat = 0.2126
let GREEN: CGFloat = 0.7152
let BLUE: CGFloat = 0.0722
let GAMMA: CGFloat = 2.4

var r: CGFloat = 0
var g: CGFloat = 0
var b: CGFloat = 0
self.getRed(&r, green: &g, blue: &b, alpha: nil)

let a = [r, g, b]
.map({ v in
return v <= 0.03928 ? v / 12.92 : pow((v + 0.055) / 1.055, GAMMA)
})

return a * RED + a * GREEN + a * BLUE
}
``````

With the luminance property calculated we can then implement the function that calculates the contrast ratio:

``````func contrastTo(color: UIColor) -> CGFloat {
let lum1 = self.luminance
let lum2 = color.luminance
let brightest = max(lum1, lum2)
let darkest = min(lum1, lum2)
return (brightest + 0.05) / (darkest + 0.05)
}
``````

So what is the threshold that is considered “having enough” contrast ratio? Well according to W3C it actually depends on the font size of the foreground color. So we can add a nice last little helper function that returns a boolean for a given color and font style.

``````func hasEnoughContrast(toColor color: UIColor, forTextSize size: CGFloat = 16, weight: UIFont.Weight = .regular) -> Bool {
let contrast = self.contrastTo(color: color)
switch weight {
case .medium, .semibold, .bold, .heavy, .black:
return size >= 14 && contrast >= 3
default:
return contrast >= 4.5
}
}
``````

## Examples

``````let red = UIColor.red
print(red.luminance)

// print(red.luminance) --> 0.2126
``````
``````let white = UIColor.white
let alto = UIColor(red: 214.0/255.0, green: 214.0/255.0, blue: 214.0/255.0, alpha: 1.0)

let ratio = white.contrastTo(color: alto)
print(ratio)

// print(ratio) --> 1.453401544312084
``````
``````let black = UIColor.black;
let hasEnoughContrast = black.hasEnoughContrast(toColor: .white, forTextSize: 20, weight: .bold);
print(hasEnoughContrast)

// print(hasEnoughContrast) --> true
``````