A Friendlier Approach for CSS Color Manipulation
By Chris Eppstein and Michael Parenteau
These ideas build on ones proposed by Tab Atkins but expand the concepts to apply to any color value, not just named colors.
They provide the full power of Sass's color system while preserving a more human-readable system when multiple transformations are applied because the arguments are closer to the operations.
Scaling vs Adjusting
In color manipulation there are two basic classes of operations:
- Adjustment: adds or subtracts an absolute amount and stops when it gets to the end point.
- Scaling: percent change from the current value towards the end point.
The convention used here is to use a past-tense verb for adjustment and an active tense verb for scaling.
Almost all of these operations take place in the HSL color domain (the
inverted which is RGB based.)
These operations can be composed in any order however, in some cases different ordering may result in a different resulting color.
While it's a bit tricky, this system can be implemented in Sass to elicit early feedback from users.
For named colors only, you can use an adjacent color name to transform the hue. Such operations are nonsensical for arbitrary colors.
In sass, we cannot distinguish a named color from an arbitrary color, so we'd
probably just enforce this by saying that the hue of the color modifier cannot
be more than
30deg away from the primary color's hue. This might make sense
in CSS as well.
color(green blue) // 50% between green and blue color(greenish blue) // 25% towards green from blue color(blueish green) // 25% towards blue from green color(blueish(33%) green) // 33% towards blue from green
It is handy to be able to find the compliment and inverse of a color.
color(compliment $color) // compliment color color(inverted $color) // inverted color
Other hue manipulations make colors warmer or cooler. We believe that generic degree manipulation of an arbitrary color is non-sensical from a design persepective.
Note: we're not sure what is the "hottest" color because there are two primary colors considered "hot": red and yellow. Yellow is the opposite hue of blue on the wheel but orange is the opposite hue of blue in terms of pigments. This will make a good bikeshed (every proposal needs one).
color(warmer $foo) // rotate 50% percent towards orange(or yellow?) (scaling) color(cooler $foo) // rotate 50% percent towards blue (scaling) color(warmer(25%) $foo) // rotate 25% percent towards orange (scaling) color(cooler(25%) $foo) // rotate 25% percent towards blue (scaling) color(warmed(30deg) $foo) // rotate 30deg percent towards orange (adjustment) color(cooled(30deg) $foo) // rotate 30deg percent towards blue (adjustment)
darkened will adjust the lightness of a color.
color(lightened $color) // +50% lightness adjustment color(lightened(33%) $color) // +33% lightness adjustment color(darkened $color) // -50% lightness adjustment color(darkened(33%) $color) // -33% lightness adjustment
darker will scale the lightness of a color.
color(lighter $color) // +50% lightness scaling color(lighter(33%) $color) // +33% lightness scaling color(darker $color) // -50% lightness scaling color(darker(33%) $color) // -33% lightness scaling
desaturated will adjust the saturation of a color.
color(desaturated $color) // -50% saturation adjustment color(desaturated(33%) $color) // -33% saturation adjustment color(saturated $color) // +50% saturation adjustment color(saturated(33%) $color) // +33% saturation adjustment
desaturate will scale the saturation of a color.
color(desaturate $color) // 50% saturation scaled color(desaturate(33%) $color) // 33% saturation scaled color(saturate $color) // 50% saturation scaled color(saturate(33%) $color) // 33% saturation scaled