Popmotion/popmotion · error

'${color}' is not an animatable color. Use the equivalent co

Error message

'${color}' is not an animatable color. Use the equivalent color code instead.

What it means

`mixColor` needs both endpoints to be parseable, animatable colors. `getColorType(from)` returned no recognized color type (e.g. an unsupported format or a plain string like a named keyword it cannot parse), so `invariant(!!fromColorType, notAnimatable(from))` throws for the `from` argument. The library only animates colors in formats it has parsers for (hex, rgba, hsla, etc.).

Source

Thrown at packages/popmotion/src/utils/mix-color.ts:26

// Demonstrated http://codepen.io/osublake/pen/xGVVaN
export const mixLinearColor = (from: number, to: number, v: number) => {
    const fromExpo = from * from
    const toExpo = to * to
    return Math.sqrt(Math.max(0, v * (toExpo - fromExpo) + fromExpo))
}

const colorTypes = [hex, rgba, hsla]
const getColorType = (v: Color | string) =>
    colorTypes.find((type) => type.test(v))

const notAnimatable = (color: Color | string) =>
    `'${color}' is not an animatable color. Use the equivalent color code instead.`

export const mixColor = (from: Color | string, to: Color | string) => {
    let fromColorType = getColorType(from)
    let toColorType = getColorType(to)

    invariant(!!fromColorType, notAnimatable(from))
    invariant(!!toColorType, notAnimatable(to))

    let fromColor = fromColorType.parse(from)
    let toColor = toColorType.parse(to)

    if (fromColorType === hsla) {
        fromColor = hslaToRgba(fromColor)
        fromColorType = rgba
    }

    if (toColorType === hsla) {
        toColor = hslaToRgba(toColor)
        toColorType = rgba
    }

    const blended = { ...fromColor }

    return (v: number) => {

View on GitHub (pinned to adf681efd8)

Solutions

  1. Convert the first color to a supported format (hex, rgb() or hsla()) before calling
  2. Expand named keywords manually, e.g. 'red' -> '#ff0000'
  3. Resolve CSS variables to concrete color strings before animating
  4. Check the string for typos (partial hex, missing parens)

Example fix

// before
mixColor('red', '#0000ff');
// after
mixColor('#ff0000', '#0000ff');
Defensive patterns

Strategy: validation

Validate before calling

const COLOR_RE = /^(#([0-9a-f]{3,8})|rgba?\([^)]+\)|hsla?\([^)]+\))$/i;
function isAnimatableColor(c) {
  return typeof c === 'string' && COLOR_RE.test(c.trim());
}
if (!isAnimatableColor(from)) throw new Error(`Cannot animate color: ${from}`);

Type guard

function isAnimatableColor(value: unknown): value is string {
  return typeof value === 'string' &&
    /^(#([0-9a-f]{3,8})|rgba?\([^)]+\)|hsla?\([^)]+\))$/i.test(value.trim());
}

Prevention

When it happens

Trigger: `mixColor('red', '#0000ff')` (unsupported color keyword as the first arg), `mixColor('transparent-ish', ...)`, `mixColor(var(--color), '#fff')` where the variable resolves to an unsupported format, or a typo'd hex like `mixColor('#ff00', '#ffffff')`.

Common situations: Passing CSS named colors or `color-mix()`/`lab()`/`oklch()` strings the parser doesn't support; reading colors from CSS variables at runtime; typos in hex strings; design tokens that aren't concrete color values.

Related errors


AI-assisted analysis of Popmotion/popmotion@adf681efd8 (2026-09-02). Data as JSON: /api/errors/40b6f6f9ba4958b0. Report an issue: GitHub.