Skip to content

This site describes solve-engine 2.44.0, the version npm installs.

Colours

Package: COLOUR_PACKAGE. Registered by createEngine(); for a slimmer engine, register it explicitly (see choosing packages).

Colours are values. Write one as a hex literal, an rgb()/hsl() call, or a CSS name, then lighten it, rotate its hue, mix two together, read a channel out as a number, or check a contrast ratio against WCAG. Every colour result carries its channels and a ready CSS string, so an editor can render an inline swatch beside the answer, as this page does.

Function names are matched case-insensitively, so isDark, ISDARK and isdark are the same function; the examples here use camelCase for the multi-word ones.

All four CSS hex forms are literals. #rgb and #rgba expand each nibble the way a browser does (#f00 is #ff0000), and the four- and eight-digit forms carry an alpha channel:

#ff0000 // #ff0000
#f00 // #ff0000
#ff000080 // #ff000080

rgb() and hsl() build a colour from channels; their rgba()/hsla() forms add an alpha of 0 to 1. Saturation and lightness may be written with or without a %:

rgb(255, 128, 0) // rgb(255, 128, 0)
rgba(255, 0, 0, 0.5) // rgba(255, 0, 0, 0.5)
hsl(210, 50, 40) // hsl(210, 50%, 40%)

Two more colour wheels are available: hsv (also hsb, the value/brightness wheel most colour pickers show) and hwb (the CSS Color 4 hue, whiteness and blackness model). A colour built this way is the same as any other and prints as hex:

hsv(120, 100, 100) // #00ff00
hwb(0, 50, 0) // #ff8080

hsva is hsv with a fourth value, the opacity (how see-through the colour is, from 0 for invisible to 1 for solid), which the hex form carries as two more digits:

hsva(200, 50%, 60%, 0.5) // #4d7f9980

Every CSS colour name is available through color("...") (also spelled colour), including transparent and rebeccapurple:

color("rebeccapurple") // rebeccapurple
color("transparent") // transparent

A bare #ff0000 on its own line is a colour, not a markdown heading: a heading always has a space after its # (# Total), and a hex colour never does. A # followed by anything that is not exactly 3, 4, 6 or 8 hex digits (a tag like #todo, a reference like #42) is left alone.

Pull any channel out as a number. red, green and blue read the 0 to 255 sRGB channels; hue (0 to 360), saturation and lightness (0 to 100) read the HSL channels; and alpha, given a single argument, reads the alpha rather than setting it:

red(#3366cc) // 51
green(#3366cc) // 102
blue(#3366cc) // 204
hue(#ff0000) // 0
saturation(#ff0000) // 100
lightness(#3366cc) // 50
alpha(rgba(255, 0, 0, 0.5)) // 0.50

lighten and darken move a colour along its HSL lightness; saturate and desaturate (also desat) along its saturation. rotate (also spin, adjustHue) turns the hue by an angle in degrees, and complement turns it a half-turn to the colour opposite on the wheel:

lighten(#3366cc, 20%) // #85a3e0
darken(#ff0000, 20%) // #990000
saturate(#8899aa, 30%) // #6999c9
desaturate(#6999c9, 30%) // #8899aa
rotate(#ff0000, 120) // #00ff00
complement(#ff0000) // #00ffff

The amount an adjuster takes reads the same whether written as a fraction, a percent, or a bare number, so 0.2, 20% and 20 all mean a fifth.

invert (also negate) flips every channel, and grayscale (also greyscale) drops a colour to grey by its perceived brightness:

invert(#ff0000) // #00ffff
grayscale(#3366cc) // #626262

mix blends two colours; a third argument weights the blend toward the second, defaulting to the midpoint. tint, shade and tone are the common special cases: mixing toward white, black and mid-grey:

mix(#ff0000, #0000ff) // #800080
tint(#ff0000, 50%) // #ff8080
shade(#ff0000, 50%) // #800000
tone(#ff0000, 50%) // #c04040

alpha (also opacity, fade), given two arguments, sets transparency. A hex colour keeps hex display and shows the alpha as #rrggbbaa:

alpha(#ff0000, 0.5) // #ff000080

contrast returns the WCAG contrast ratio between two colours (1 to 21), and luminance returns a single colour’s relative luminance (0 to 1):

contrast(#ffffff, #000000) // 21
contrast(#ffffff, #767676) // 4.54
luminance(#ffffff) // 1

isDark and isLight classify a background by which of black or white text reads better on it, and readable (also contrastColor, or contrastColour) returns that better text colour directly:

isDark(#3366cc) // true
isLight(#ffffff) // true
readable(#3366cc) // #ffffff
contrastColour(#eeeeee) // #000000

isContrastCompliant answers whether two colours meet a WCAG contrast bar. With no third argument it tests the AA rule for normal text (4.5:1); a level name ("AA", "AAA", "AA large", "AAA large") or a plain number overrides that:

isContrastCompliant(#ffffff, #000000) // true
isContrastCompliant(#ffffff, #949494, "AA large") // true
isContrastCompliant(#ffffff, #767676, "AAA") // false

wcagLevel (also wcag) reports the best rating a pair reaches for normal text, one of AAA (7:1 or better), AA (4.5), AA Large (3, which only meets AA for large text or UI), or Fail:

wcagLevel(#ffffff, #000000) // AAA
wcagLevel(#ffffff, #767676) // AA
wcagLevel(#ffffff, #949494) // AA Large
wcagLevel(#ffffff, #cccccc) // Fail

as rgb, as rgba, as hsl, as hsla and as hex change how a colour prints without touching its channels:

#ff0000 as hsl // hsl(0, 100%, 50%)
#ff0000 as rgb // rgb(255, 0, 0)

Two colours are equal when their channels match, regardless of how each was written, so #ff0000 and rgb(255, 0, 0) are the same colour:

#ff0000 == rgb(255, 0, 0) // true

A colour is three channels (how much red, green and blue it has) plus its opacity, so it has no single number to add, round or put in order. Arithmetic on a colour, a numeric function given one, an order such as <, and a conversion to a form of a number (as %, as number, in binary) are refused by name rather than answered as though the colour were zero. The colour functions above are the way to change a colour, and a channel read out as a number takes arithmetic like any other number:

#ff0000 + 2 // A colour cannot be added: it is three channels (red, green and blue), not one number. To use one channel as a number, read it out first, as in red(#3366cc).
sqrt(#ff0000) // A colour cannot be given to sqrt: it is three channels (red, green and blue), not one number. To use one channel as a number, read it out first, as in red(#3366cc).
#ff0000 < 3 // A colour cannot be put in order: it is three channels (red, green and blue), not one number. To use one channel as a number, read it out first, as in red(#3366cc).
red(#3366cc) + 1 // 52
lighten(#3366cc, 20%) // #85a3e0

Equality is the one comparison a colour has: == and != compare the channels, as above, and a colour is never equal to a number, so #000000 == 0 is false.