easing-gradient
v1.0.1
Published
Create smooth gradients that approximate easing functions
Maintainers
Readme
easing-gradient
Create smooth gradients that approximate easing functions.
CSS gradients (such as linear-gradient, radial-gradient, conic-gradient) interpolate linearly between
their color stops. This often produces gradients that look harsh, muddy, or "banded" where two colors meet,
because real-world color transitions rarely move in a straight line. easing-gradient fixes this by
re-expressing a single eased transition as many tiny color stops (an "interpolation polyline"), so the browser
renders a smooth, natural-looking gradient.
import { easingGradient } from "easing-gradient";
easingGradient("linear", "to right", [
["black", "0%", "ease"],
["transparent", "100%"],
]);Why not just use a PostCSS plugin?
This project is inspired by larsenwork.com/easing-gradients and is based on the postcss-easing-gradients plugin by the same author Andreas Larsen. That plugin only works as a PostCSS transform, which forces you to write plain CSS files for it to take effect. If you use CSS-in-JS, or need to compute an eased gradient at runtime from plain JavaScript (e.g. in a Node.js environment), that approach becomes awkward.
easing-gradient is a plain, framework-agnostic JavaScript utility function. You can call it anywhere:
- directly in a browser app;
- in a Node.js script;
- as the foundation of a plugin for a styling library — CSS-in-JS solutions (styled-components, Emotion, etc.) and atomic CSS frameworks (Tailwind CSS, etc.) alike.
Because it returns an ordinary CSS <gradient> string, whatever consumes CSS will accept its output.
Installation
# npm
npm install easing-gradient
# yarn
yarn add easing-gradient
# pnpm
pnpm add easing-gradient
# bun
bun add easing-gradientThe package ships ES modules and TypeScript declarations.
Usage
import { easingGradient } from "easing-gradient";
// Linear gradient easing from left to right.
easingGradient("linear", "to right", [
["green", "0%", "ease"],
["red", "100%"],
]);
// => "linear-gradient(to right, color-mix(in srgb-linear, green, red 0%), ...)"
// Radial gradient with an eased transition.
easingGradient("radial", "circle at top right", [
["red", "0%", "ease-in-out"],
["blue", "100%"],
]);By default each transition is split into many interpolation stops. You can lower the number of stops to produce a smaller (but more "low-poly") gradient:
easingGradient("linear", undefined, [
["black", "0%", "cubic-bezier(0.48, 0.30, 0.64, 1.00)"],
["transparent", "100%"],
], { stops: 5 });
// => "linear-gradient(
// color-mix(in srgb-linear, black, transparent 0%),
// color-mix(in srgb-linear, black, transparent 28.281%) 30.813%,
// color-mix(in srgb-linear, black, transparent 61.25%) 54.5%,
// color-mix(in srgb-linear, black, transparent 88.594%) 75.938%,
// color-mix(in srgb-linear, black, transparent 100%),
// transparent
// )"API
easingGradient(type, towards, colorStops, options?)
Returns a CSS gradient string.
type
"linear" | "radial" | "conic" — the gradient function to generate.
| Value | Gradient function |
| ---------- | ------------------- |
| "linear" | linear-gradient() |
| "radial" | radial-gradient() |
| "conic" | conic-gradient() |
towards
The direction/shape argument for the gradient, or undefined to omit it. Examples: "to right",
"to bottom left", "30deg", "circle at top right".
colorStops
An array of color stops. Each stop is a tuple [color, progress, easing?]:
| Index | Parameter | Type | Required | Description |
| ----- | ---------- | -------------------- | -------- | -------------------------------------------------------------------------------------------------- |
| 0 | color | string | Yes | The color at this stop. Any CSS color works: named colors, hex, rgb(), hsl(), etc. |
| 1 | progress | string \| string[] | Yes | The stop position: a percentage ("0%", "100%"), a length ("20px"), or an array of these. |
| 2 | easing | string | No | The CSS easing function applied to the transition into this stop. Defaults to "linear". |
The easing on a stop describes the transition into that stop (the segment that precedes it), because an
easing function only shapes how interpolation moves between two color stops.
progress as an array
progress can also be an array, which expands into multiple stops sharing the same color.
This is the same behavior as it in CSS.
[["black", ["0%", "20%"]]]
// is equivalent to
[["black", "0%"], ["black", "20%"]]A note on easing
Since easing only has meaning between two stops, the easing on the last color stop is ignored — there
is nothing after it to interpolate toward. It's therefore best to omit easing on the final stop.
options
| Option | Type | Default | Description |
| ------------------ | -------------------------------------------- | -------------- | ---------------------------------------------------------------------------------------------------- |
| stops | number | 13 | Interpolation points per eased segment. Lower values are more "low-poly" (less code, more banding). |
| alphaDecimals | number | 3 | Decimal places for interpolated alpha values. Lower values can cause banding. |
| colorMode | ColorSpace | "srgb-linear"| Color space used for interpolation. Passed to the CSS color-mix() function. |
| colorMixFunction | (args: ColorMixFunctionArgument) => string | color-mix() | Custom function to compute each mixed color. See below. |
ColorSpace is one of hsl, hwb, lch, oklch, srgb, srgb-linear, display-p3,
display-p3-linear, a98-rgb, prophoto-rgb, rec2020, lab, oklab, xyz, xyz-d50, xyz-d65.
Supported easing functions
Any CSS <easing-function> accepted by the
underlying parser works as the easing argument:
- keyword easings:
linear,ease,ease-in,ease-out,ease-in-out cubic-bezier(x1, y1, x2, y2)steps(n, <jump>)
easingGradient("linear", "to right", [
["green", "0%", "steps(4, jump-none)"],
["red", "100%"],
]);Custom color mixing
By default, interpolation uses the CSS-native color-mix() function. If you'd rather compute colors with your
own color library (for wider browser support, or for a non-CSS target), pass a colorMixFunction.
colorMixFunction receives a single argument — an object with the following 7 properties:
| Property | Type | Description |
| --------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
| colors | [string, string] | The two endpoint colors being mixed, as [startColor, endColor]. |
| progress | string | The generated stop's position, e.g. "25%". When the stop positions are lengths rather than percentages, this is a calc(...) expression. |
| progressValue | number | Numeric form of progress (e.g. 0.25 for "25%"). It is NaN when the positions are lengths, not percentages. |
| amount | string | The interpolation amount as a percentage string, e.g. "42.5%". |
| amountValue | number | Numeric form of amount (e.g. 0.425 for "42.5%"), in the range [0, 1]. |
| alphaDecimals | number | The alphaDecimals option passed to easingGradient (or its default). |
| colorMode | ColorSpace | The colorMode option passed to easingGradient (or its default). |
The function must return the resulting color as a string. Here is an example using
chroma.js:
import chroma from "chroma-js";
import { easingGradient } from "easing-gradient";
easingGradient("linear", "to right", [["red", "0%", "ease"], ["blue", "100%"]], {
colorMixFunction: ({ colors, amountValue, colorMode }) => {
return chroma.mix(colors[0], colors[1], amountValue, colorMode);
},
});Building on top of this
easing-gradient is intentionally small and unopinionated so it can be wrapped into other tools. For example:
- Tailwind CSS — expose it as a utility (or plugin) that generates an eased gradient value on demand.
- CSS-in-JS — call it directly inside a styled component, or wrap it in a tagged-template helper.
- Any runtime — use it in Node.js, Deno, Bun, or the browser without a build step for CSS.
Native support (CSSWG proposal)
Smooth gradients are a real problem being discussed at the CSS Working Group. If you'd like to see eased gradients built into browsers natively, please support this proposal:
If it ships, browsers will handle this natively and this library will have served its purpose.
Credits
- Author: Andreas Larsen
- Inspiration: larsenwork.com/easing-gradients
- Based on: postcss-easing-gradients
- Easing parsing: easing-coordinates
