Mix two colors into one that follows dark mode with blend(with:by:). It mixes in OKLab, halfway by default, and returns a Color. Mix a little of your brand into a gray card, and the card picks up its hue in both modes:
// Halfway from gray to your brand
let card = Color.backgroundSecondary.blend(with: brand.backgroundSecondary)
Text("Your flight to Lisbon boards at 14:30.")
.foregroundStyle(brand.foregroundPrimary)
.padding()
.frame(maxWidth: .infinity, alignment: .leading)
.background(card)Each mode mixes its own two colors, so the card is light in light mode and dark in dark mode. Pick two colors and slide the fraction below: 0 is your color and 1 is the other:
Color.proBlue._600.toColor().blend(with: Color.proPink._500.toColor())Fading out#
Blend with .clear to fade a color without changing it. The mix keeps the color and only lowers its opacity, so brand.backgroundTertiary.blend(with: .clear) is the same color at half opacity, not a grayer one.
Blend or mix(with:by:in:)?#
blend(with:by:) runs on every system the library supports, while SwiftUI's own mix(with:by:in:) needs iOS 18 or macOS 15. We haven't compared their results.
How it behaves#
The result usually lands off the palette, and its contrast depends on the two colors you mix, so check text on it with contrastRatio(to:method:). A mix too vivid for Display P3 gets its chroma lowered at the same lightness.
The fraction is clamped from 0 to 1. Opacity mixes along with the color.
API#
public extension Color {
func blend(with other: Color, by fraction: Double = 0.5) -> Color
}| Parameter | Type | Default | What other values do |
|---|---|---|---|
other | Color | none | The color to mix toward. With .clear, the mix only fades. |
fraction | Double | 0.5 | How far toward other, from 0 to 1. Values outside are clamped. |
It returns a new Color that mixes the two colors in each appearance.
Sources#
- Color+Adjustments.swift, which defines
blend(with:by:). - ColorAdjustment.swift, which mixes in OKLab and handles
.clear. - Björn Ottosson, A perceptual color space for image processing (2020), which introduces OKLab.
See also
- .proGradient() Fill cards and buttons with gradients that stay vivid end to end, in light and dark mode.
- How gradients blend and ease Why plain gradients turn gray, how each blend travels between colors, and where easing puts them.
- .saturate(by:) Make one color more vivid for an accent, at the same lightness and contrast.
- Setting up themes Recolor a view, a screen or your whole app from one value, with the same contrast in every hue.