Write the sRGB fallback first, then the Display P3 declaration. Browsers that understand the second declaration use it; other browsers retain the first.
Derive the fallback deliberately
This Display P3 red lies outside sRGB, so reducing it to hexadecimal changes the available gamut:
use PhpColor\Color\Color;
use PhpColor\Color\Colorimetry\Gamut;
$wide = Color::parse('color(display-p3 1 0 0)');
assert(false === Gamut::isInGamut($wide, 'srgb'));
$fallback = $wide->toSrgb()->toHex();
$enhancement = $wide->toCss();
assert('#ff0000' === $fallback);
assert('color(display-p3 1 0 0)' === $enhancement);
use PhpColor\Color\Color;
use PhpColor\Color\Colorimetry\Gamut;
$wide = Color::parse('color(display-p3 1 0 0)');
assert(false === Gamut::isInGamut($wide, 'srgb'));
$fallback = $wide->toSrgb()->toHex();
$enhancement = $wide->toCss();
assert('#ff0000' === $fallback);
assert('color(display-p3 1 0 0)' === $enhancement);
Emit both declarations
Keep the fallback and enhancement in the order the browser must evaluate:
printf(
".brand {\n color: %s;\n color: %s;\n}\n",
$fallback,
$enhancement,
);
printf(
".brand {\n color: %s;\n color: %s;\n}\n",
$fallback,
$enhancement,
);
PHPColor converts colors; it does not generate fallback pairs automatically or determine browser support. Check the final component in both gamuts, including contrast. Continue with precision and gamut, format CSS colors, or preserve a Display P3 token.