{"record":{"id":"40b6f6f9ba4958b0","repo":"Popmotion/popmotion","slug":"color-is-not-an-animatable-color-use-the-equ","errorCode":null,"errorMessage":"'${color}' is not an animatable color. Use the equivalent color code instead.","messagePattern":"'(.+?)' is not an animatable color\\. Use the equivalent color code instead\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/popmotion/src/utils/mix-color.ts","lineNumber":26,"sourceCode":"// Demonstrated http://codepen.io/osublake/pen/xGVVaN\nexport const mixLinearColor = (from: number, to: number, v: number) => {\n    const fromExpo = from * from\n    const toExpo = to * to\n    return Math.sqrt(Math.max(0, v * (toExpo - fromExpo) + fromExpo))\n}\n\nconst colorTypes = [hex, rgba, hsla]\nconst getColorType = (v: Color | string) =>\n    colorTypes.find((type) => type.test(v))\n\nconst notAnimatable = (color: Color | string) =>\n    `'${color}' is not an animatable color. Use the equivalent color code instead.`\n\nexport const mixColor = (from: Color | string, to: Color | string) => {\n    let fromColorType = getColorType(from)\n    let toColorType = getColorType(to)\n\n    invariant(!!fromColorType, notAnimatable(from))\n    invariant(!!toColorType, notAnimatable(to))\n\n    let fromColor = fromColorType.parse(from)\n    let toColor = toColorType.parse(to)\n\n    if (fromColorType === hsla) {\n        fromColor = hslaToRgba(fromColor)\n        fromColorType = rgba\n    }\n\n    if (toColorType === hsla) {\n        toColor = hslaToRgba(toColor)\n        toColorType = rgba\n    }\n\n    const blended = { ...fromColor }\n\n    return (v: number) => {","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/Popmotion/popmotion/blob/adf681efd8568ada018ce68082dbd585f25c4c7d/packages/popmotion/src/utils/mix-color.ts#L8-L44","documentation":"`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.).","triggerScenarios":"`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')`.","commonSituations":"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.","solutions":["Convert the first color to a supported format (hex, rgb() or hsla()) before calling","Expand named keywords manually, e.g. 'red' -> '#ff0000'","Resolve CSS variables to concrete color strings before animating","Check the string for typos (partial hex, missing parens)"],"exampleFix":"// before\nmixColor('red', '#0000ff');\n// after\nmixColor('#ff0000', '#0000ff');","handlingStrategy":"validation","validationCode":"const COLOR_RE = /^(#([0-9a-f]{3,8})|rgba?\\([^)]+\\)|hsla?\\([^)]+\\))$/i;\nfunction isAnimatableColor(c) {\n  return typeof c === 'string' && COLOR_RE.test(c.trim());\n}\nif (!isAnimatableColor(from)) throw new Error(`Cannot animate color: ${from}`);","typeGuard":"function isAnimatableColor(value: unknown): value is string {\n  return typeof value === 'string' &&\n    /^(#([0-9a-f]{3,8})|rgba?\\([^)]+\\)|hsla?\\([^)]+\\))$/i.test(value.trim());\n}","tryCatchPattern":null,"preventionTips":["Normalize all colors to hex or rgba at the boundary (theme load, token parsing)","Never pass CSS variables, named keywords, or color functions like oklch()/lab() directly","Store design tokens as concrete color strings, not references","Sanitize user-supplied colors with a regex check before animating"],"tags":["color","animation","parsing","validation"],"backgroundTag":"non-animatable-color","analyzedSha":"adf681efd8568ada018ce68082dbd585f25c4c7d","analyzedAt":"2026-09-02T10:01:11.645Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T16:17:10.729Z"}