Skip to content

color-mix: division by zero in polar color space unpremultiply when alpha is 0 #1194

@queelius

Description

@queelius

Bug Description

The polar_premultiply macro's unpremultiply method divides by self.alpha without checking for zero, causing division by zero when interpolating colors with alpha=0 in polar color spaces (HSL, HWB, LCH, OKLCH).

This produces NaN values that leak into the output as none keyword in the serialized CSS for color spaces like oklch and lch that are not converted to RGBA.

The rectangular_premultiply macro correctly guards against this with self.alpha != 0.0, but polar_premultiply is missing this check.

Reproduction

/* Input */
.foo { color: color-mix(in oklch, oklch(0.5 0.2 120 / 0), oklch(0.7 0.1 240 / 0)); }

/* Actual (buggy) output */
.foo{color:oklch(none none 180/0)}

/* Expected output */
.foo{color:oklch(0% 0 180/0)}

Similarly for LCH:

/* Input */
.foo { color: color-mix(in lch, lch(50 30 120 / 0), lch(70 10 240 / 0)); }

/* Actual (buggy) output */
.foo{color:lch(none none 180/0)}

/* Expected output */
.foo{color:lch(0% 0 180/0)}

Root Cause

In src/values/color.rs, the polar_premultiply macro:

fn unpremultiply(&mut self, alpha_multiplier: f32) {
    self.h %= 360.0;
    if !self.alpha.is_nan() {  // Missing: && self.alpha != 0.0
        self.$a /= self.alpha;  // Division by zero when alpha=0 -> NaN
        self.$b /= self.alpha;
        self.alpha *= alpha_multiplier;
    }
}

Compare with rectangular_premultiply which correctly checks:

if !self.alpha.is_nan() && self.alpha != 0.0 {

Fix

Add && self.alpha != 0.0 to the polar_premultiply unpremultiply guard, matching the rectangular_premultiply behavior.

Affected Color Spaces

  • HSL (via color-mix(in hsl, ...))
  • HWB (via color-mix(in hwb, ...))
  • LCH (via color-mix(in lch, ...))
  • OKLCH (via color-mix(in oklch, ...))

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions