Skip to content

phpcolor/phpcolor

Repository files navigation

Colors

PHPColor logo

A PHP library for color: 15 color spaces, perceptual mixing, WCAG contrast,
palette generation, CSS Level 4/5 support, and vision simulation.

  PHP Version   CI   Coverage   PHPStan   Release   License

Colors

Color Library

  • Modern CSS Support: Full CSS Color 4/5 parsing, including oklch(), color-mix(), and relative color syntax.
  • Perceptual Accuracy: Uses Oklab for mixing and gradients to ensure uniform brightness and hue stability.
  • 15 Color Spaces: Direct conversion between sRGB, P3, Lab, XYZ, and more via optimized matrices.
  • Immutable API: Chainable value objects for colors, palettes, and gradients.
  • Accessibility First: WCAG 2.1/APCA contrast ratios, contrast solver, and 7-profile vision simulation.
  • Developer Friendly: PHP 8.3+, zero dependencies, typed enums, and PHPStan level 10.

Installation

composer require phpcolor/phpcolor
use PhpColor\Color\Color;

$color = Color::parse('oklch(65% 0.15 210)');
$lighter = $color->lighten(0.1)->to('display-p3');

echo $lighter->toCss(); // color(display-p3 0.45 0.72 0.88)

Create

The Color class is the main entry point. It accepts strings anywhere a ColorInterface is expected, and provides named constructors for every supported color space.

From CSS strings

Any valid CSS color string is accepted, including modern Level 4/5 syntax.

use PhpColor\Color\Color;

$color = Color::parse('#3b82f6');
$color = Color::parse('hsl(217 91% 60%)');
$color = Color::parse('oklch(0.65 0.18 264)');
$color = Color::parse('color(display-p3 0.23 0.51 0.96)');
$color = Color::parse('rebeccapurple');

Color::tryFrom() returns null on invalid input instead of throwing.

From channel values

$color = Color::rgb(0.23, 0.51, 0.96);
$color = Color::rgb(0.23, 0.51, 0.96, 0.8); // with alpha
$color = Color::oklab(0.62, -0.05, -0.15);
$color = Color::oklch(0.65, 0.18, 264);
$color = Color::hex('#3b82f6');

// Named shortcuts
$black = Color::black();
$white = Color::white();
$red   = Color::red();

-> Parsing docs

Convert

Every color object converts to any supported color space. Conversions are exact and round-trip safely. The result is always a new typed object that can be further manipulated or formatted.

// Typed conversions
$oklab = $color->toOklab();
$oklch = $color->toOklch();
$srgb  = $color->toSrgb();
$p3    = $color->toDisplayP3();
$lab   = $color->toLab();
$xyz   = $color->toXyz();

// Generic conversion by name
$color->to('oklch');
$color->to('display-p3');
$color->to('rec2020');

-> Color spaces docs

Format

Formatting produces a string or structured value from any color object. The output space defaults to the color's current space, but can be overridden.

// CSS string (default: native space)
$color->toCss();                // 'oklch(0.65 0.18 264)'
$color->toCss('hsl');           // 'hsl(217 91% 60%)'
$color->toCss('srgb');          // 'rgb(59 130 246)'
$color->toCss('color');         // 'color(srgb 0.231 0.510 0.965)'

// Hex
$color->toHex();                // '#3b82f6'
$color->toHex(withAlpha: true); // '#3b82f6cc'

// String cast (same as toCss())
(string) $color;
$color->toString();

// Raw channel values
$color->getChannels();          // ['r' => 0.23, 'g' => 0.51, 'b' => 0.96]

Inspect

Color objects expose read methods for common attributes regardless of the underlying space. All values are normalized to a consistent scale.

Lightness and luminance

$color->isLight();       // perceived brightness
$color->isDark();
$color->getLuminance();  // relative luminance 0-1, per WCAG

Hue, saturation, opacity

$color->getSaturation(); // 0-1
$color->getHue();        // 0-360 degrees
$color->getOpacity();    // 0-1
$color->isOpaque();
$color->isTransparent();
$color->equals($other);  // perceptual equality check

Temperature

$color->temperature();   // estimated value in Kelvin
$color->isHot();
$color->isCold();

-> Hue docs · Chroma docs · Temperature docs

Manipulate

All manipulation methods return a new instance. Colors are immutable and chain freely across color spaces.

Lightness

$lighter = $color->lighten(0.15);
$darker  = $color->darken(0.15);
$grey    = $color->grayscale();
$inv     = $color->invert();

Saturation and hue

$vivid   = $color->saturate(0.1);
$muted   = $color->desaturate(0.2);
$shifted = $color->rotateHue(30);

Temperature

$warmer = $color->warm(0.1);
$cooler = $color->cool(0.1);

Transparency

$semiOpaque = $color->withAlpha(0.5);

Blend

Blends this color against a backdrop using standard CSS compositing modes.

$blended = $color->blend('#000000', 'multiply');
$blended = $color->blend($other, 'screen');
// Also: normal, overlay, darken, lighten

For perceptual two-color interpolation, see Mix.

Channel control

Direct channel access is useful when you need to set an exact value in a specific color space rather than applying a relative shift.

$adjusted = $color->toOklch()->withChannel('l', 0.75);
$adjusted = $color->toOklch()->withChannels(['l' => 0.75, 'c' => 0.12]);

// Chain across spaces
$result = $color->toOklch()->withChannel('h', 120)->to('display-p3')->toCss();

-> Hue docs · Chroma docs

Mix

Mixing blends two colors at a given ratio. Oklab is the default interpolation space, which produces perceptually uniform results without the hue shift and brightness dip of sRGB mixing.

// 50/50 blend in Oklab (default)
$mixed = Color::mix(Color::red(), Color::blue(), 0.5);

// Control the interpolation space
$mixed = Color::mix('#ff0000', '#0000ff', 0.3, 'oklch');
$mixed = Color::mix($a, $b, 0.5, 'srgb');

// Tint (toward white) and shade (toward black) on a single color
$tinted = $color->tint(0.3);  // 30% white
$shaded = $color->shade(0.3); // 30% black

For multi-step interpolation across a full range, see Palettes.

-> Mixing docs

Harmonies

These methods generate sets of colors that are harmonically balanced in hue, based on color theory intervals. They operate on any color object and return arrays in the same color space. For detecting or fixing harmony across an existing palette, see Palettes.

// Two colors 30 degrees away on either side
[$left, $right] = $color->analogous();
$set = $color->analogous(count: 4);

// Direct opposite on the hue wheel
$opposite = $color->complementary();

// Two colors flanking the complement
[$a, $b] = $color->splitComplementary();

// Three colors evenly spaced (120 degrees apart)
[$a, $b, $c] = $color->triadic();

// Four colors (90 degrees apart)
[$a, $b, $c, $d] = $color->tetradic();

-> Harmony docs

Palettes

A palette is an ordered or named collection of colors. It supports generation, batch transformation, lookup, and CSS export.

Generate

use PhpColor\Color\Palette\ColorPalette;

// From a base color
$tints  = ColorPalette::tints(Color::parse('#3b82f6'), steps: 9);
$shades = ColorPalette::shades(Color::parse('#3b82f6'), steps: 9);
$scale  = ColorPalette::lightnessScale(Color::parse('#3b82f6'), steps: 11);
$ramp   = ColorPalette::interpolate(Color::red(), Color::blue(), steps: 7);

// From existing colors
$palette = ColorPalette::fromHex(['#ff0000', '#00ff00', '#0000ff']);
$palette = ColorPalette::parse(['red', 'oklch(0.6 0.2 264)', '#3b82f6']);

Transform

All transformation methods return a new palette. They mirror the single-color Manipulate API.

$darkened  = $scale->darken(0.1);
$shifted   = $scale->rotateHue(30)->desaturate(0.05);
$p3palette = $scale->to('display-p3');

// Filter and map (map callable must return a ColorInterface)
$light    = $scale->filter(fn($c) => $c->toOklch()->l > 0.5);
$inverted = $scale->map(fn($c) => $c->rotateHue(180));

// Structural: merge(), slice(), reverse(), count()

Lookup

$closest = $scale->closest(Color::parse('#4f46e5')); // by Oklab distance
$mid     = $scale->at(0.5);                          // interpolated position

Export

Named palettes produce CSS custom properties directly, useful for design token pipelines.

$ui = ColorPalette::named([
    'primary'   => Color::parse('#3b82f6'),
    'secondary' => Color::parse('#8b5cf6'),
    'neutral'   => Color::parse('#6b7280'),
]);

echo $ui->toCssVariables('color');
// --color-primary: #3b82f6;
// --color-secondary: #8b5cf6;
// --color-neutral: #6b7280;

// Array export
$hexArray = $scale->toHex();
$cssArray = $scale->toCss('oklch');

-> Palette docs

Gradients

The gradient builder uses a fluent API modeled on CSS gradient syntax. Oklab interpolation is the default, avoiding the hue shift and brightness loss of sRGB gradients. For two-color blending, see Mix.

use PhpColor\Color\Gradient\GradientBuilder;

// Linear gradient
$gradient = GradientBuilder::linear(90)
    ->from('#ff0000')
    ->via('#a855f7')
    ->to('#3b82f6')
    ->in('oklch')
    ->build();

$gradient->toCss(); // 'linear-gradient(90deg, ...)'

// Radial gradient with shape and position
GradientBuilder::radial()
    ->circle()
    ->at('center')
    ->from('white')
    ->to('black')
    ->build();

// Conic gradient with explicit stop positions (0.0-1.0)
GradientBuilder::conic(0)
    ->stop('red', 0.0)
    ->stop('yellow', 0.33)
    ->stop('blue', 0.66)
    ->build();

-> Gradient docs

Accessibility

WCAG contrast

ColorContrast calculates contrast ratios per WCAG 2.x. The WcagLevel enum prevents invalid inputs and replaces the old string-based API.

use PhpColor\Color\Contrast\ColorContrast;
use PhpColor\Color\Contrast\WcagLevel;

$ratio = ColorContrast::calculate(Color::black(), Color::white()); // 21.0

ColorContrast::meetsFor($text, $background, WcagLevel::AA);
ColorContrast::meetsFor($text, $background, WcagLevel::AAA, largeText: true);

ColorContrast::requiredRatio(WcagLevel::AA);  // 4.5 normal, 3.0 large text
ColorContrast::requiredRatio(WcagLevel::AAA); // 7.0 normal, 4.5 large text

APCA

APCA (Accessible Perceptual Contrast Algorithm) is a newer model that accounts for font weight and size. It returns a signed lightness contrast value rather than a simple ratio.

use PhpColor\Color\Contrast\ApcaContrast;

$lc = ApcaContrast::lc($text, $background);

Solving contrast

ContrastSolver helps fix colors that fail a requirement, rather than just checking them. See also Vision simulation for accessibility testing across color vision profiles.

use PhpColor\Color\Contrast\ContrastSolver;

// Adjust lightness until the color passes the target ratio
$fixed = ContrastSolver::adjustLightnessToContrast($text, $background, 4.5);

// Pick the most accessible option from a set of candidates
$best = ContrastSolver::bestOn($background, [$white, $black, $brand]);

// Find the minimum opacity at which a color passes on a given background
$alpha = ContrastSolver::requiredAlpha($text, $background, 4.5);

-> Contrast docs

Distance

Color distance measures how different two colors appear to a human observer. PHPColor uses CIEDE2000 by default, the most perceptually accurate formula available. Color::deltaE() is a convenient shorthand.

// Via the Color facade
$delta = Color::deltaE($a, $b);
$dist  = Color::distance($a, $b, 'CMC');

// Direct, with explicit algorithm choice
use PhpColor\Color\Distance\ColorDistance;

$delta = ColorDistance::deltaE($a, $b);                 // CIEDE2000
$dist  = ColorDistance::calculate($a, $b, 'DeltaE94');
$dist  = ColorDistance::calculate($a, $b, 'CMC');

Distance is used internally by ColorPalette::closest() and by ColorVisionSimulator::areDistinguishable(). See Vision.

-> Distance docs

Vision

ColorVisionSimulator applies transformation matrices that approximate how colors appear under a given vision deficiency profile. Useful for testing whether a UI or palette works for colorblind users.

use PhpColor\Color\Vision\ColorVisionSimulator;
use PhpColor\Color\Vision\VisionProfile;

$simulator = ColorVisionSimulator::create(VisionProfile::Deuteranomaly);

// Simulate how a single color looks
$simulated = $simulator->simulate($color);

// Check if two colors remain distinguishable under this profile
$ok = $simulator->areDistinguishable($colorA, $colorB); // threshold: 0.06

// Simulate an entire set of colors at once
$simulated = $simulator->simulateAll($palette->all());

Supported profiles: Protanopia, Deuteranopia, Tritanopia, Protanomaly, Deuteranomaly (most common, affecting ~5% of males), Tritanomaly, Monochromacy.

-> Vision docs

CSS

PHPColor parses and resolves the full CSS Color Level 4/5 expression layer. These features are useful for design token pipelines, server-side theme processing, and any context where colors are expressed as CSS values rather than raw channels.

color-mix()

Resolves a color-mix() expression to a concrete color, using the interpolation space declared in the function.

use PhpColor\Color\Css\CssColor;
use PhpColor\Color\Css\CssContext;

$expr     = CssColor::parse('color-mix(in oklab, #ff0000 30%, #0000ff)');
$resolved = $expr->resolve(new CssContext());

light-dark()

Produces a theme-aware color that resolves differently depending on the context's color scheme flag.

$adaptive = CssColor::lightDark(
    Color::parse('#1a1a1a'), // light theme value
    Color::parse('#f5f5f5'), // dark theme value
);

$resolved = $adaptive->resolve(new CssContext(scheme: 'dark'));

Custom properties

Wraps a color with a CSS custom property reference and an optional fallback.

$withVar = CssColor::var('--brand-color', Color::parse('#3b82f6'));

Relative color syntax

Allows deriving a new color from an existing one using CSS channel expressions, following the CSS Color 5 relative color syntax.

$relative = CssColor::relative('oklch', $origin, ['l' => 'calc(l + 0.1)'], null);

-> CSS colors docs · Relative colors docs

Color Spaces

Space Class Description
sRGB SrgbColor Standard RGB (0-1 channels)
Oklab OklabColor Perceptually uniform, ideal for mixing
Oklch OklchColor Cylindrical Oklab, intuitive hue control
Lab LabColor CIELAB (D50 illuminant)
LCH LchColor Cylindrical Lab
HWB HwbColor Hue, Whiteness, Blackness (CSS Color 4)
CMYK CmykColor Subtractive (print)
Display P3 DisplayP3Color Wide-gamut (Apple displays)
Adobe RGB A98RgbColor Adobe RGB 1998
ProPhoto ProPhotoColor Wide-gamut professional photography
Rec. 2020 Rec2020Color HDR and broadcast
CIE XYZ XyzColor Device-independent reference space
Linear sRGB LinearSrgbColor sRGB without gamma encoding

-> Color spaces docs

Contributing

Contributions are welcome. Please open an issue before submitting a pull request for significant changes.

git clone https://github.com/phpcolor/phpcolor.git
cd phpcolor
composer install

composer test  # PHPUnit
composer sa    # PHPStan static analysis
composer cs    # PHP-CS-Fixer

License

PHPColor is released under the MIT license.

Colors

About

PHP Color Manipulation Library

Resources

License

Code of conduct

Security policy

Stars

Watchers

Forks

Sponsor this project

 

Contributors

Languages