Expression functions
Expressions are JavaScript, and two namespaces cover what design expressions usually need: Color transforms colors and prints them as text, and Math holds the standard JavaScript math functions plus clamp. Both are available wherever an expression runs: expression variables, Set Variable actions in expression mode, and conditions.
Completion
To open suggestions in an expression field, type . after Color or Math to list that namespace's members with a one-line description, or press Ctrl + Space to list the variables and the two namespaces. Suggestions do not open while you type a name (completion). Arrow keys move the highlight, Enter or Tab inserts the highlighted entry (functions come with their parentheses, caret inside), and Escape closes the list without leaving the field. While the caret sits inside a function's parentheses the list shows the function's typed signature with the current parameter highlighted and a line explaining it.
Color
Every function takes a color as either a color variable (or any [r, g, b, a] value with 0–1 components) or a color string in any accepted format, so Color.lighten(brand, 0.2) and Color.lighten('#336699', 0.2) are the same call. Transforms return a color; Color.format returns a string, Color.contrastRatio a number and Color.equals a boolean. Compare colors with Color.equals(a, b). Colors are [r, g, b, a] arrays, and JavaScript's === compares array references, not their channels.
Color.darken(brand, 0.2) // a slightly darker brand color
Color.format(Color.mix(brand, 'white'), 'hsl') // "hsl(211.11, 35.06%, 69.8%)" for #336699
Color.contrastPick(background, 'white', 'black')
Amounts are fractions from 0 to 1. Lightness and saturation move by a share of the remaining headroom, so already light or vivid colors still change instead of clipping. Lightness, saturation, hue and mixing work in the OKLCH color space, the same math as color adjustments on component inputs, so Color.lighten(c, 0.2) matches a Lightness adjustment of 0.2.
| Function | Effect |
|---|---|
Color.lighten(color: color, amount: number) → color | Move lightness toward white |
Color.darken(color: color, amount: number) → color | Move lightness toward black |
Color.saturate(color: color, amount: number) → color | Increase colorfulness |
Color.desaturate(color: color, amount: number) → color | Decrease colorfulness |
Color.rotateHue(color: color, degrees: number) → color | Rotate the hue |
Color.mix(color: color, target: color, ratio: number = 0.5) → color | Blend toward target |
Color.tint(color: color, ratio: number = 0.5) → color | Blend toward white |
Color.shade(color: color, ratio: number = 0.5) → color | Blend toward black |
Color.alpha(color: color, alpha: number) → color | Set the alpha |
Color.fade(color: color, factor: number) → color | Multiply the alpha |
Color.grayscale(color: color) → color | Remove all colorfulness |
Color.complement(color: color) → color | The opposite hue |
Color.invert(color: color) → color | Invert the red, green and blue channels |
Color.contrastPick(background: color, light: color, dark: color) → color | Whichever of light and dark reads better on background |
Color.contrastRatio(a: color, b: color) → number | The WCAG contrast ratio between two colors, 1 to 21 |
Color.equals(a: color, b: color) → boolean | Whether two colors are the same to 8-bit precision, alpha included |
Color.parse(text: string) → color | The [r, g, b, a] components of a color string |
Color.format(color: color, kind: string = 'display') → string | Print the color as a string |
Format kinds
Color.format(c, kind) prints a color as a string. Every kind is one the color fields accept, so a formatted string can be pasted back anywhere.
kind | Example |
|---|---|
'display' (default) | #336699, or #336699 50% when translucent |
'hex' | #336699 (alpha dropped) |
'hex8' | #336699FF |
'rgb' | rgb(51, 102, 153) or rgba(51, 102, 153, 0.5) |
'hsl' | hsl(210, 50%, 40%) or hsla(210, 50%, 40%, 0.5) |
'hwb' | hwb(210 20% 40%) or hwb(210 20% 40% / 0.5) |
'hsb' | hsb(210, 66.67, 60) or hsba(210, 66.67, 60, 0.5) |
Errors
A value that is not a color, an amount that is not a number, or an unknown format kind fails the expression, and the variable shows the message, for example Color.darken: argument 1 is not a color: "teal-ish". The variable keeps its last good value until the expression is fixed.
Color is reserved: a variable cannot be named Color, so the functions stay reachable. Lowercase color is fine.
Math
The standard JavaScript Math object, unchanged, plus Math.clamp(value, min, max) to keep a number within bounds:
Math.clamp(width / 3, 80, 240) // never below 80 or above 240
Math.round(height * 0.618)
Math.max(gap, 8)
Math.random() gives a different number on every evaluation, so a variable using it changes whenever anything it depends on changes.
| Function | Effect |
|---|---|
Math.abs(x: number) → number | The absolute value of x |
Math.acos(x: number) → number | The arccosine of x, in radians |
Math.acosh(x: number) → number | The hyperbolic arccosine of x |
Math.asin(x: number) → number | The arcsine of x, in radians |
Math.asinh(x: number) → number | The hyperbolic arcsine of x |
Math.atan(x: number) → number | The arctangent of x, in radians |
Math.atan2(y: number, x: number) → number | The angle from the x axis to the point (x, y), in radians |
Math.atanh(x: number) → number | The hyperbolic arctangent of x |
Math.cbrt(x: number) → number | The cube root of x |
Math.ceil(x: number) → number | x rounded up to an integer |
Math.clamp(value: number, min: number, max: number) → number | value limited to the range min to max |
Math.clz32(x: number) → number | The number of leading zero bits in the 32-bit integer x |
Math.cos(x: number) → number | The cosine of x radians |
Math.cosh(x: number) → number | The hyperbolic cosine of x |
Math.E: number | Euler's number, about 2.718 |
Math.exp(x: number) → number | e raised to the power x |
Math.expm1(x: number) → number | e raised to the power x, minus 1 |
Math.f16round(x: number) → number | x rounded to the nearest 16-bit float |
Math.floor(x: number) → number | x rounded down to an integer |
Math.fround(x: number) → number | x rounded to the nearest 32-bit float |
Math.hypot(...values: number) → number | The square root of the sum of squares of the arguments |
Math.imul(a: number, b: number) → number | The 32-bit integer product of a and b |
Math.LN10: number | The natural logarithm of 10, about 2.303 |
Math.LN2: number | The natural logarithm of 2, about 0.693 |
Math.log(x: number) → number | The natural logarithm of x |
Math.log10(x: number) → number | The base-10 logarithm of x |
Math.LOG10E: number | The base-10 logarithm of e, about 0.434 |
Math.log1p(x: number) → number | The natural logarithm of 1 + x |
Math.log2(x: number) → number | The base-2 logarithm of x |
Math.LOG2E: number | The base-2 logarithm of e, about 1.443 |
Math.max(...values: number) → number | The largest of the arguments |
Math.min(...values: number) → number | The smallest of the arguments |
Math.PI: number | The ratio of a circle’s circumference to its diameter, about 3.142 |
Math.pow(base: number, exponent: number) → number | base raised to the power exponent |
Math.random() → number | A pseudo-random number from 0 up to 1; differs on every evaluation |
Math.round(x: number) → number | x rounded to the nearest integer |
Math.sign(x: number) → number | -1, 0 or 1 by the sign of x |
Math.sin(x: number) → number | The sine of x radians |
Math.sinh(x: number) → number | The hyperbolic sine of x |
Math.sqrt(x: number) → number | The square root of x |
Math.sumPrecise(values: number[]) → number | The exact sum of an array of numbers, rounded once |
Math.SQRT1_2: number | The square root of 1/2, about 0.707 |
Math.SQRT2: number | The square root of 2, about 1.414 |
Math.tan(x: number) → number | The tangent of x radians |
Math.tanh(x: number) → number | The hyperbolic tangent of x |
Math.trunc(x: number) → number | x with the fraction removed |