Skip to content
ColorTokensKitby Penguin Design Ventures
Contents
Function

.desaturate(by:)

Gray out disabled and finished states, and every label stays just as readable.

Updated View as Markdown

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:

swift
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)
light mode
Book nowSold out

Sold out keeps the label's contrast in both modes, so people can still read what the button would do. Slide the amount below:

Try it / desaturate()
Color
light mode
Before
#4176b8proBlue._600
After
#4d76aaoff the palette
dark mode
Before
#4176b8proBlue._600
After
#4d76aaoff the palette
swift
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:

swift
HStack {
    Text("Lisbon").padding(12).background(brand.backgroundTertiary)
    Text("Kyoto").padding(12).background(brand.backgroundTertiary.desaturate(by: 0.5))
}
.foregroundStyle(brand.foregroundPrimary)
light mode
LisbonKyoto

Disabled text#

Soften text until it's far from the page, then take its color out:

swift
// Gray, 9 stops from the page
let disabled = brand.foregroundPrimary.soften(by: 10).desaturate(by: 1)

Text("Sold out").foregroundStyle(disabled)
light mode
Sold out

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#

swift
public extension Color {
    func desaturate(by amount: Double = 0.2) -> Color
}
ParameterTypeDefaultWhat other values do
amountDouble0.2The 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#

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.