PHPColor 1.0

One immutable Color object, 13 color spaces, mixing that follows the eye, and contrast you can check against WCAG and APCA.

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/phpcolor
composer require phpcolor/phpcolor

The Getting Started guide walks through the first color, and every example above runs unchanged in the playground.

Related posts

PHPColor 1.1 Releases · Aug 12, 2026 · 1 min read phpcolor.dev Project · Sep 4, 2026 · 2 min read

Discover PHPColor

Modern Color for PHP

Build accurate palettes, contrast checks, conversions, and modern CSS color workflows with PHPColor.