Skip to content
ColorTokensKitby Penguin Design Ventures
Contents
Function

.darken(by:)

Make hover and pressed fills from any color, an exact stop darker in both modes.

Updated View as Markdown

Make hover and pressed fills from any color with darken(by:). It moves the color down the palette by whole stops, one by default, and returns a Color that still follows dark mode:

swift
let fill = brand.invertedBackgroundTertiary   // _650 in light mode, _250 in dark

VStack(alignment: .leading) {
    Text("Book now").padding(12).background(fill)
    Text("Book now").padding(12).background(fill.darken())        // hovered: _700 and _300
    Text("Book now").padding(12).background(fill.darken(by: 2))   // pressed: _750 and _350
}
.font(.headline)
.foregroundStyle(brand.invertedForegroundPrimary)
light mode
Book nowBook nowBook now

Rest, hover and pressed are exact stops a design spec can name, and the label stays above 7:1 when pressed, in both modes and every hue. Pick a color and a number of stops below:

Try it / darken()
Color
light mode
Before
#4176b8proBlue._600
After
#3a69a5proBlue._650
dark mode
Before
#4176b8proBlue._600
After
#3a69a5proBlue._650
swift
Color.proBlue._600.toColor().darken()

With the defaults, proBlue._600 darkens to exactly proBlue._650.

Darken or strengthen?#

A pressed button should look darker in both modes. strengthen(by:) moves a color away from the page instead, so in dark mode it goes lighter, toward a light label:

swift
let fill = brand.backgroundTertiary   // _200 in light mode, _700 in dark

HStack {
    Text("Book now").padding(12).background(fill.strengthen(by: 2))   // _300 and _600
    Text("Book now").padding(12).background(fill.darken(by: 2))       // _300 and _800
}
.foregroundStyle(brand.foregroundPrimary)
light mode
Book nowBook now

In dark mode, the strengthened fill drops the label to 4.29:1, and the darkened one keeps it at 8.72:1. Building for interaction states puts these fills in a ButtonStyle.

How it behaves#

A palette color lands on an exact stop of its family, so it has that stop's contrast in every hue. Past _1000 it stays at _1000, and black stays black. Gray moves along gray's own stops.

A color off the palette, such as a system color or your own hex, moves by the same step in lightness and keeps its hue and chroma, as how colors land on exact stops shows. The result keeps the color's opacity, and each mode darkens that mode's color.

API#

swift
public extension Color {
    func darken(by stops: Int = 1) -> Color
}
ParameterTypeDefaultWhat other values do
stopsInt1A negative value goes lighter, so darken(by: -2) is lighten(by: 2).

It returns a new Color that resolves in each appearance, so it follows light and dark mode like the color you started from.

Sources#

See also

  • .lighten(by:) Add highlights and lighter steps to any color, landing on an exact palette stop.
  • .desaturate(by:) Gray out disabled and finished states, and every label stays just as readable.
  • .soften(by:) Make quieter text and fills from any token: lighter in light mode, darker in dark mode.
  • Stops: ._50 to ._1000 Twenty fixed colors per theme, lightest to darkest, with the same contrast in every hue.