PHPColor 1.0 is out. It parses a color, converts it, transforms it, and tells you
whether the result is readable. It needs PHP 8.3 or later and has no runtime
dependencies, so adding it is one composer require.
One object, thirteen spaces
Color::parse() takes hex, the CSS color functions, and the wide-gamut color()
notation. What comes back is an immutable object that every other part of the
library accepts.
use PhpColor\Color\Color;
$brand = Color::parse('#7c5cff');
echo $brand->to('oklch')->toCss();
// oklch(0.599049 0.229939 286.20467)
echo $brand->to('display-p3')->toCss();
// color(display-p3 0.467271 0.365452 0.96762)
use PhpColor\Color\Color;
$brand = Color::parse('#7c5cff');
echo $brand->to('oklch')->toCss();
// oklch(0.599049 0.229939 286.20467)
echo $brand->to('display-p3')->toCss();
// color(display-p3 0.467271 0.365452 0.96762)
->to() reaches thirteen spaces: srgb, srgb-linear, display-p3, a98-rgb,
prophoto-rgb, rec2020, xyz-d65, lab, lch, oklab, oklch, hwb and
cmyk. HSL parses and serializes as an sRGB notation rather than a separate
space, which is what it is.
Mixing that follows the eye
Blue and yellow are the honest test. In sRGB the channels average out and the midpoint collapses into grey. In Oklab the same midpoint keeps its color.
use PhpColor\Color\Color;
$blue = Color::parse('#0000ff');
$yellow = Color::parse('#ffff00');
echo Color::mix($blue, $yellow, 0.5, 'srgb')->toHex();
// #bcbcbc
echo Color::mix($blue, $yellow, 0.5, 'oklab')->toHex();
// #6cabc7
use PhpColor\Color\Color;
$blue = Color::parse('#0000ff');
$yellow = Color::parse('#ffff00');
echo Color::mix($blue, $yellow, 0.5, 'srgb')->toHex();
// #bcbcbc
echo Color::mix($blue, $yellow, 0.5, 'oklab')->toHex();
// #6cabc7
That grey is the failure mode behind dull gradients and palettes that lose their identity in the middle. Mixing, tints, shades and gradients default to Oklab for this reason.
Contrast you check instead of guess
A ratio against white tells you nothing about the same color against black. Both need testing, and 1.0 makes each one a single call.
use PhpColor\Color\Color;
use PhpColor\Color\Contrast\ColorContrast;
use PhpColor\Color\Contrast\WcagLevel;
$slate = Color::parse('#64748b');
echo round(Color::contrast($slate, Color::white()), 2); // 4.76
echo round(Color::contrast($slate, Color::black()), 2); // 4.41
ColorContrast::meetsFor($slate, Color::white(), WcagLevel::AA); // true
ColorContrast::meetsFor($slate, Color::black(), WcagLevel::AA); // false
use PhpColor\Color\Color;
use PhpColor\Color\Contrast\ColorContrast;
use PhpColor\Color\Contrast\WcagLevel;
$slate = Color::parse('#64748b');
echo round(Color::contrast($slate, Color::white()), 2); // 4.76
echo round(Color::contrast($slate, Color::black()), 2); // 4.41
ColorContrast::meetsFor($slate, Color::white(), WcagLevel::AA); // true
ColorContrast::meetsFor($slate, Color::black(), WcagLevel::AA); // false
This slate passes AA on white and fails it on black, by less than half a point in
either direction. ApcaContrast::lc() computes the APCA score for interfaces that
need a closer model of perceived contrast than a luminance ratio.
Install
composer require phpcolor/phpcolorcomposer require phpcolor/phpcolor
The Getting Started guide walks through the first color, and every example above runs unchanged in the playground.