ozdemirburak / iris
PHP library for color manipulation and conversion.
Requires
- php: ^8.4
Requires (Dev)
- phpstan/phpstan: ^2.3
- phpunit/phpunit: ^13.4
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
PHP library for color manipulation and conversion. Requires PHP 8.4+ and supports Hex, Hexa, RGB, RGBA, HSL, HSLA, HSV, CMYK, and OKLCH color formats.
Install
$ composer require ozdemirburak/iris
Upgrading from 4.x
Version 5 requires PHP 8.4. Most code works without changes. Check these points, the full list is in the change log:
- The manipulation methods and the conversions return a new color and do not change the color you call them on. If your code used that side effect, use the return value:
$hsl = $hsl->lighten(10); - The setters of
Rgb,Rgba,Hsl,Hsla,Hsv,CmykandOklchclamp a value that is out of range (red(300)sets 255), and the hue wraps around 360 (hue(400)sets 40). Before, these values were ignored. A value that is not a number is ignored, and the setter returns the color. - Operations on
Rgba,Hsla,HexaandOklchkeep the alpha.mix(),tint(),shade()andgradient()mix the alpha of both colors and weight the colors by their alpha, like less.js and Sass. - Blending a color with alpha onto its background rounds each channel and no longer rounds down, so some results are 1 higher:
rgba(0,0,0,0.3)givesrgb(179,179,179). Oklchkeeps the alpha ofoklch(… / 0.5). A conversion to a model without alpha (toHex(),toRgb(), …) blends it onto white, as forRgbaandHsla.fade(),fadeIn()andfadeOut()returnHexaforHexandHexa, andOklchforOklch.- Conversions to HSV and CMYK keep one decimal, e.g.
hsv(303.5,96.7%,96.5%), andHsv::value()returns a float. Oklch::values()includes the alpha, and a lightness without%that is not greater than 1 is a number from 0 to 1:oklch(0.7 0.1 120)is 70%.Factory::init()throwsInvalidColorExceptionfor an empty string and for color functions it does not support, and reads 4 hex digits only with a#.- If you extend the color classes:
Rgb::red()returnsint,Hex::red()returnsstring, the$castsIntegerproperty is removed, andBaseColorhas new protected methods (see the change log).
Usage
Hex
use OzdemirBurak\Iris\Color\Hex; $hex = new Hex('#ff00ff'); // same as new Hex('fuchsia'); echo $hex->red(); // ff echo $hex->green(); // 00 echo $hex->blue(); // ff $values = $hex->values(); // ['ff', '00', 'ff'] $hexa = $hex->toHexa(); // \OzdemirBurak\Iris\Color\Hexa('ff00ffff') $hsl = $hex->toHsl(); // \OzdemirBurak\Iris\Color\Hsl('300,100,50') $hsla = $hex->toHsla(); // \OzdemirBurak\Iris\Color\Hsla('300,100,50,1.0') $hsv = $hex->toHsv(); // \OzdemirBurak\Iris\Color\Hsv('300,100,100') $rgb = $hex->toRgb(); // \OzdemirBurak\Iris\Color\Rgb('255,0,255') $rgba = $hex->toRgba(); // \OzdemirBurak\Iris\Color\Rgba('255,0,255,1.0') $oklch = $hex->toOklch(); // \OzdemirBurak\Iris\Color\Oklch('70.17,0.3225,328.36') echo $hex; // #ff00ff
Hexa (hex with alpha value)
use OzdemirBurak\Iris\Color\Hexa; $hexa = new Hexa('#ff00ff4c'); echo $hexa->red(); // ff echo $hexa->green(); // 00 echo $hexa->blue(); // ff echo $hexa->alpha(); // 0.3 - as a float for compatibility with the other conversions $values = $hexa->values(); // ['ff', '00', 'ff', 0.3] $hsl = $hexa->toHsl(); // \OzdemirBurak\Iris\Color\Hsl('300,100,50') $hsla = $hexa->toHsla(); // \OzdemirBurak\Iris\Color\Hsla('300,100,50,0.3') $hsv = $hexa->toHsv(); // \OzdemirBurak\Iris\Color\Hsv('300,100,100') $rgb = $hexa->toRgb(); // \OzdemirBurak\Iris\Color\Rgb('255,0,255') $rgba = $hexa->toRgba(); // \OzdemirBurak\Iris\Color\Rgba('255,0,255,0.3') echo $hexa; // #ff00ff4c
HSL
use OzdemirBurak\Iris\Color\Hsl; $hsl = new Hsl('hsl(300,100%,50%)'); // same as new Hsl('fuchsia'); echo $hsl->hue(); // 300 echo $hsl->saturation(); // 100 echo $hsl->lightness(); // 50 $values = $hsl->values(); // [300, '100%', '50%'] $normalizedValues = $hsl->valuesInUnitInterval(); // [300/360, 100/100, 50/100] $hex = $hsl->toHex(); // \OzdemirBurak\Iris\Color\Hex('ff00ff') $hexa = $hsl->toHexa(); // \OzdemirBurak\Iris\Color\Hexa('ff00ffff') $hsv = $hsl->toHsv(); // \OzdemirBurak\Iris\Color\Hsv('300,100,100') $rgb = $hsl->toRgb(); // \OzdemirBurak\Iris\Color\Rgb('255,0,255') $rgba = $hsl->toRgba(); // \OzdemirBurak\Iris\Color\Rgba('255,0,255,1.0') echo $hsl; // hsl(300,100%,50%)
HSLA
use OzdemirBurak\Iris\Color\Hsla; $hsla = new Hsla('hsla(150,100%,50%,0.3)'); echo $hsla->hue(); // 150 echo $hsla->saturation(); // 100 echo $hsla->lightness(); // 50 echo $hsla->alpha(); // 0.3 $values = $hsla->values(); // [150, '100%', '50%', 0.3] $hex = $hsla->toHex(); // \OzdemirBurak\Iris\Color\Hex('b2ffd8') $rgba = $hsla->toRgba(); // \OzdemirBurak\Iris\Color\Rgba('0,255,128,0.3') $hexa = $hsla->toHexa(); // \OzdemirBurak\Iris\Color\Hexa('00ff804d') echo $hsla; // hsla(150,100%,50%,0.3)
HSV
use OzdemirBurak\Iris\Color\Hsv; $hsv = new Hsv('hsv(300,100%,100%)'); // same as new Hsv('fuchsia'); echo $hsv->hue(); // 300 echo $hsv->saturation(); // 100 echo $hsv->value(); // 100 $values = $hsv->values(); // [300, '100%', '100%'] $normalizedValues = $hsv->valuesInUnitInterval(); // [300/360, 100/100, 100/100] $hex = $hsv->toHex(); // \OzdemirBurak\Iris\Color\Hex('ff00ff') $hexa = $hsv->toHexa(); // \OzdemirBurak\Iris\Color\Hexa('ff00ffff') $hsl = $hsv->toHsl(); // \OzdemirBurak\Iris\Color\Hsl('300,100,50') $hsla = $hsv->toHsla(); // \OzdemirBurak\Iris\Color\Hsla('300,100,50,1.0') $hsv = $hsv->toHsv(); // \OzdemirBurak\Iris\Color\Hsv('300,100,100') $rgb = $hsv->toRgb(); // \OzdemirBurak\Iris\Color\Rgb('255,0,255') $rgba = $hsv->toRgba(); // \OzdemirBurak\Iris\Color\Rgba('255,0,255,1.0') echo $hsv; // hsv(300,100%,100%)
RGB
use OzdemirBurak\Iris\Color\Rgb; $rgb = new Rgb('rgb(255, 0, 255)'); // same as new Rgb('fuchsia'); echo $rgb->red(); // 255 echo $rgb->green(); // 0 echo $rgb->blue(); // 255 $values = $rgb->values(); // [255, 0, 255] $hex = $rgb->toHex(); // \OzdemirBurak\Iris\Color\Hex('ff00ff') $hexa = $rgb->toHexa(); // \OzdemirBurak\Iris\Color\Hexa('ff00ffff') $hsl = $rgb->toHsl(); // \OzdemirBurak\Iris\Color\Hsl('300,100,50') $hsla = $rgb->toHsla(); // \OzdemirBurak\Iris\Color\Hsla('300,100,50,1.0') $hsv = $rgb->toHsv(); // \OzdemirBurak\Iris\Color\Hsv('300,100,100') $rgb = $rgb->toRgb(); // \OzdemirBurak\Iris\Color\Rgb('255,0,255') $rgba = $rgb->toRgba(); // \OzdemirBurak\Iris\Color\Rgba('255,0,255,1.0') echo $rgb; // rgb(255,0,255)
RGBA
use OzdemirBurak\Iris\Color\Rgba; $rgba = new Rgba('rgba(93,111,222,0.33)'); echo $rgba->red(); // 93 echo $rgba->green(); // 111 echo $rgba->blue(); // 222 echo $rgba->alpha(); // 0.33 $hex = $rgba->background((new Hex('ccc'))->toRgb())->toHex(); // \OzdemirBurak\Iris\Color\Hex('a7add2') $hexa = $rgba->toHexa(); // \OzdemirBurak\Iris\Color\Hexa('5d6fde54') - preserves original RGB echo $rgba; // rgba(93,111,222,0.33)
CMYK
use OzdemirBurak\Iris\Color\Cmyk; $cmyk = new Cmyk('cmyk(0,100,0,0)'); echo $cmyk->cyan(); // 0 echo $cmyk->magenta(); // 100 echo $cmyk->yellow(); // 0 echo $cmyk->black(); // 0 $values = $cmyk->values(); // [0, 100, 0, 0] $hex = $cmyk->toHex(); // OzdemirBurak\Iris\Color\Hex('ff00ff') $hexa = $cmyk->toHexa(); // OzdemirBurak\Iris\Color\Hexa('ff00ffff') $hsl = $cmyk->toHsl(); // OzdemirBurak\Iris\Color\Hsl('300,100,50') $hsla = $cmyk->toHsla(); // OzdemirBurak\Iris\Color\Hsla('300,100,50,1.0') $hsv = $cmyk->toHsv(); // OzdemirBurak\Iris\Color\Hsv('300,100,100') $rgb = $cmyk->toRgb(); // OzdemirBurak\Iris\Color\Rgb('255,0,255') $rgba = $cmyk->toRgba(); // OzdemirBurak\Iris\Color\Rgba('255,0,255,1.0') echo $cmyk; // cmyk(0,100,0,0)
OKLCH
use OzdemirBurak\Iris\Color\Oklch; use OzdemirBurak\Iris\Color\Hex; // Comma-separated syntax $oklch = new Oklch('oklch(70%, 0.15, 150)'); // Space-separated syntax (CSS Color Level 4) $oklch = new Oklch('oklch(40.1% 0.123 21.57)'); $oklch = new Oklch('oklch(0.401 0.123 21.57)'); // lightness without % is a number from 0 to 1 (above 1 it is a percentage, as in 4.x) $oklch = new Oklch('oklch(59.69% 0.156 49.77)'); echo $oklch->lightness(); // 59.69 echo $oklch->chroma(); // 0.156 echo $oklch->hue(); // 49.77 $values = $oklch->values(); // [59.69, 0.156, 49.77, 1.0], the last value is the alpha $hex = $oklch->toHex(); // OzdemirBurak\Iris\Color\Hex $rgb = $oklch->toRgb(); // OzdemirBurak\Iris\Color\Rgb $hsl = $oklch->toHsl(); // OzdemirBurak\Iris\Color\Hsl echo $oklch; // oklch(59.69% 0.156 49.77) // Convert from other formats to OKLCH $hex = new Hex('#ff0000'); $oklch = $hex->toOklch(); echo $oklch; // oklch(62.8% 0.2577 29.23) // Alpha $oklch = new Oklch('oklch(62.8% 0.2577 29.23 / 0.5)'); echo $oklch->alpha(); // 0.5 echo $oklch->toRgba(); // rgba(255,0,0,0.5) echo $oklch->toHex(); // #ff8080, blended onto white like Rgba and Hsla echo $oklch; // oklch(62.8% 0.2577 29.23 / 0.5), the alpha is only written when it is below 1
Note: Relative color syntax with the from keyword (e.g., oklch(from green l c h)) is not supported.
Via Factory
If you do not know what the color string will be (for example, you're getting it from a group of rows from a database), then you can try using Factory to instantiate an appropriate color class:
use OzdemirBurak\Iris\Color\Factory; $color = Factory::init('rgba(93,111,222,0.33)'); echo $color->red(); // 93 echo $color->green(); // 111 echo $color->blue(); // 222 echo $color->alpha(); // 0.33 // No exception for a string that is not a color, e.g. for validation rules $color = Factory::tryInit('not a color'); // null var_dump(Factory::isValid('rgb(10 20 30 / 50%)')); // bool(true)
Supported syntax
All color classes and Factory::init() accept the legacy comma syntax and the CSS Color Level 4 syntax. Values out of range are rejected, except the hue, which wraps around 360:
Factory::init('rgb(10 20 30)'); // Rgb, same as rgb(10,20,30) Factory::init('rgb(10 20 30 / 50%)'); // Rgba, same as rgba(10,20,30,0.5) Factory::init('rgb(100% 0% 50%)'); // Rgb('255,0,128') Factory::init('hsl(0.5turn 50% 50% / .3)'); // Hsla('180,50,50,0.3'), also deg, grad and rad Factory::init('#f0f8'); // Hexa('ff00ff88') Factory::init('transparent'); // Rgba('0,0,0,0'), also for new Hexa() and new Hsla()
The setters of Rgb, Rgba, Hsl, Hsla, Hsv, Cmyk and Oklch clamp values that are out of range, the hue wraps around 360, and values that are not numbers are ignored. The setters of Hex and Hexa take hex digits:
echo (new Rgb('10,20,30'))->red(300); // rgb(255,20,30) echo (new Hsl('10,50,50'))->hue(400); // hsl(40,50%,50%) echo (new Hex('#0a141e'))->red('ff'); // #ff141e
Color Cloning
You can clone a color object to make a copy and modify it as needed.
use OzdemirBurak\Iris\Color\Hex; $original = new Hex('#b2b2b2'); $cloned = $original->clone()->toHexa()->alpha(0.5); // OzdemirBurak\Iris\Color\Hexa('#b2b2b280')
Color Manipulation
The manipulation methods return a new color and do not change the color you call them on.
Colors with an alpha channel (Hexa, Rgba, Hsla, Oklch) keep their alpha; mix(), tint(), shade() and gradient() mix the alpha of both colors.
Saturation
Saturate or desaturate by a percent.
echo (new Hsl('90,80%,50%'))->saturate(20)->toHex(); // #80ff00 echo (new Hsl('90, 80%, 50%'))->desaturate(20)->toRgb(); // rgb(128,204,51) echo (new Hex('#80cc33'))->grayscale(); // #808080, same as desaturate 100
Lightness
Lighten, darken or brighten by a percent.
$hex = new Hex('#333'); echo $hex->lighten(20); // #666666 echo $hex->darken(20); // #000000 echo $hex->brighten(20); // #666666
Spin
Spin the hue by an angle in degrees. The result wraps around 360.
$hex = (new Hsl('10,90%,50'))->spin(30)->toHex(); echo $hex; // #f2a60d
Mix
Mix by a percent.
$hex = new Hex('#000'); echo $hex->mix(new Hex('#fff'), 50); // #808080
Tint
Mix color with white by a percent.
$hex = new Hex('#000'); echo $hex->tint(50); // #808080
Shade
Mix color with black by a percent.
$hex = new Hex('#FFF'); echo $hex->shade(50); // #808080
Fade
Set the absolute opacity of a color by a percent.
$hsl = new Hsl('90,90,50'); echo $hsl->fade(10); // hsla(90,90%,50%,0.1) $rgb = new Rgb('128,242,13'); echo $rgb->fade(10); // rgba(128,242,13,0.1)
FadeIn
Increase the opacity of a color by a percent.
$hsla = new Hsla('90,90,50,0.3'); echo $hsla->fadeIn(10); // hsla(90,90%,50%,0.4) $rgba = new Rgba('128,242,13,0.3'); echo $rgba->fadeIn(10); // rgba(128,242,13,0.4)
FadeOut
Decrease the opacity of a color by a percent.
$hsla = new Hsla('90,90,50,0.3'); echo $hsla->fadeOut(10); // hsla(90,90%,50%,0.2) $rgba = new Rgba('128,242,13,0.3'); echo $rgba->fadeOut(10); // rgba(128,242,13,0.2)
Is light or dark
Determine if color is dark or light color.
$hex = new Hex('#000'); var_dump($hex->isLight()); // bool(false) var_dump($hex->isDark()); // bool(true)
isLight() uses the luma of the color. To choose a text color, use the WCAG contrast below.
Contrast and readability
WCAG 2 relative luminance and contrast ratio. A text color with alpha is blended onto the background first.
$white = new Hex('#fff'); echo (new Hex('#000'))->luminance(); // 0 echo (new Hex('#767676'))->contrast($white); // 4.54..., from 1 to 21 var_dump((new Hex('#777777'))->isReadable($white)); // bool(false), normal text needs 4.5 (WCAG AA) var_dump((new Hex('#777777'))->isReadable($white, 3)); // bool(true), large text needs 3 echo (new Hex('#ff0000'))->mostReadable(); // #000000, the best text color on red: black or white echo (new Hex('#eee'))->mostReadable([new Hex('#ff0'), new Hex('#333')]); // #333333
Distance and equality
The color difference deltaE OK (CSS Color 4), from 0 for the same color. A distance below about 0.02 is hard to see.
$red = new Hex('#ff0000'); echo $red->distance(new Hsl('0,100%,50%')); // 0 var_dump($red->equals(new Hsl('0,100%,50%'))); // bool(true), in any model, and the alpha must be the same var_dump($red->equals(new Hex('#fe0000'))); // bool(false) var_dump($red->equals(new Hex('#fe0000'), 0.02)); // bool(true)
Names, invert and complement
echo (new Hex('#ff6347'))->toName(); // tomato, null when the color has no name echo (new Hex('#ff0000'))->invert(); // #00ffff, the alpha is kept echo (new Hex('#ff0000'))->complement(); // #00ffff, the same as spin(180) echo json_encode([new Hex('#f00'), new Rgba('1,2,3,0.5')]); // ["#ff0000","rgba(1,2,3,0.5)"]
Gradient
Generate an array of colors (gradient) between two or more colors.
use OzdemirBurak\Iris\Color\Hex; use OzdemirBurak\Iris\Color\Rgb; // Two-color gradient: 5 steps from red to blue $gradient = (new Hex('#ff0000'))->gradient(new Hex('#0000ff'), 5); // ['#ff0000', '#bf0040', '#800080', '#4000bf', '#0000ff'] // Multi-color gradient with pivot: red → yellow → green in 7 steps $gradient = (new Hex('#ff0000'))->gradient([new Hex('#ffff00'), new Hex('#00ff00')], 7); // ['#ff0000', '#ff5500', '#ffaa00', '#ffff00', '#aaff00', '#55ff00', '#00ff00'] // Works with any color type - output matches the starting color type $gradient = (new Rgb('255,0,0'))->gradient(new Rgb('0,0,255'), 3); // [rgb(255,0,0), rgb(128,0,128), rgb(0,0,255)]
Change log
Please see CHANGELOG for more information what has changed recently.
Testing
$ composer test # PHPUnit $ composer analyse # PHPStan, at the max level $ composer check # both
Contributing
Pull requests are welcome. Run composer check (PHPStan and the tests) before you open one, and add tests for your change.
Credits
License
The MIT License (MIT). Please see License File for more information.