Make quieter text, icons and fills from any token with soften(by:). It moves the color toward the page by whole stops, lighter in light mode and darker in dark mode, and returns a Color:
VStack(alignment: .leading, spacing: 4) {
Text("Lisbon")
.font(.headline)
.foregroundStyle(brand.foregroundPrimary)
Text("3 nights from October 12")
.font(.subheadline)
.foregroundStyle(brand.foregroundSecondary) // _800 in light, _200 in dark
Text("Prices checked 2 min ago")
.font(.footnote)
.foregroundStyle(brand.foregroundSecondary.soften(by: 2)) // _700 in light, _300 in dark
}Two stops toward the page, foregroundSecondary lands on the stops of foregroundTertiary, so the footnote still passes WCAG AA on the page in both modes. Pick a color and a number of stops below:
Color.proBlue._600.toColor().soften()With the defaults, proBlue._600 softens to proBlue._550 in light mode and proBlue._650 in dark.
Quieter fills#
A fill softens toward the page too. Here the tag you haven't picked sits one stop closer to the page than the one you have:
HStack {
Text("Beach").padding(8).background(brand.backgroundTertiary) // _200 and _700
Text("City").padding(8).background(brand.backgroundTertiary.soften()) // _150 and _750
}
.font(.subheadline)
.foregroundStyle(brand.foregroundPrimary)Why not lighten()?#
lighten(by:) goes lighter in both modes, so lighter text is quieter on a white page and louder on a black one. soften(by:) picks the direction from the mode, so it's quieter in both.
How it behaves#
The direction comes from the appearance, not from what's behind the color. On an inverted surface, such as a dark banner in light mode, softening still goes lighter, which makes text on it louder. Use strengthen(by:) there, or the invertedForeground tokens. On watchOS, which always draws dark, softening always goes darker.
A palette color lands on an exact stop of its family, and stops at _50 or _1000. A color off the palette moves by the same step in lightness and keeps its hue. The result keeps the color's opacity.
API#
public extension Color {
func soften(by stops: Int = 1) -> Color
}| Parameter | Type | Default | What other values do |
|---|---|---|---|
stops | Int | 1 | A negative value moves away from the page, so soften(by: -1) is strengthen(). |
It returns a new Color that resolves in each appearance: lighter in light mode and darker in dark mode.
Sources#
- Color+Adjustments.swift, which defines
soften(by:)and picks the direction from the color scheme. - ColorAdjustment.swift, which moves a color along the stops.
See also
- Understanding semantic tokens Learn the 20 tokens by the job each one does, and get dark mode and passing contrast with them.
- Building for interaction states Give buttons and rows hover, pressed, selected and disabled colors that stay readable in both modes.
- .darken(by:) Make hover and pressed fills from any color, an exact stop darker in both modes.
- Managing dark mode Get dark mode without extra code: tokens switch stops for you, and your own colors can too.