Gray out disabled and finished states with desaturate(by:). It takes color out at the same lightness, 20% by default and all of it at 1, so labels keep their contrast, and returns a Color:
let fill = brand.invertedBackgroundTertiary // _650 in light mode, _250 in dark
VStack(alignment: .leading) {
Text("Book now").padding(12).background(fill)
Text("Sold out").padding(12).background(fill.desaturate(by: 1)) // gray, as light as fill
}
.font(.headline)
.foregroundStyle(brand.invertedForegroundPrimary)Sold out keeps the label's contrast in both modes, so people can still read what the button would do. Slide the amount below:
Color.proBlue._600.toColor().desaturate()Partly muted#
Take out half the color for things that are over but still worth showing, like a past trip next to an upcoming one:
HStack {
Text("Lisbon").padding(12).background(brand.backgroundTertiary)
Text("Kyoto").padding(12).background(brand.backgroundTertiary.desaturate(by: 0.5))
}
.foregroundStyle(brand.foregroundPrimary)Disabled text#
Soften text until it's far from the page, then take its color out:
// Gray, 9 stops from the page
let disabled = brand.foregroundPrimary.soften(by: 10).desaturate(by: 1)
Text("Sold out").foregroundStyle(disabled)The text stays at 3:1 or more in any hue. Building for interaction states shows it in a full row.
How it behaves#
At 1, the result is a gray with the color's lightness, so it has the same contrast as the color did. It isn't a proGray stop, because gray's stops sit on a ladder of their own. An amount above 1 gives the same gray, and a negative amount adds color, like saturate(by:).
Gray, white and black come back unchanged. The result keeps the color's opacity, and each mode desaturates that mode's color.
API#
public extension Color {
func desaturate(by amount: Double = 0.2) -> Color
}| Parameter | Type | Default | What other values do |
|---|---|---|---|
amount | Double | 0.2 | The fraction of chroma to take out: 1 leaves a gray. A negative value adds color. |
It returns a new Color at the same lightness and hue, with less chroma, in each appearance.
Sources#
- Color+Adjustments.swift, which defines
desaturate(by:)assaturate(by: -amount). - ColorAdjustment.swift, which scales chroma at the same lightness.
See also
- .darken(by:) Make hover and pressed fills from any color, an exact stop darker in both modes.
- .soften(by:) Make quieter text and fills from any token: lighter in light mode, darker in dark mode.
- How accessible is it? What the palette guarantees for contrast in every hue and mode, and what you still need to check.
- Understanding semantic tokens Learn the 20 tokens by the job each one does, and get dark mode and passing contrast with them.