better-color-tools
Better color manipulation for Sass and JavaScript/TypeScript.
Installing
npm install better-color-tools
Sass
Sass has built-in color functions, but they aren’t as usable as they could be. Here’s why this library exists as an alternative.
Mix
Let’s compare this library’s mix function to Sass’ (Sass on top; better-color-tools on bottom):
Blend | Comparison |
---|
red–lime | |
red–yellow | |
blue–yellow | |
blue–fuchsia | |
blue–lime | |
It may be hard to tell a difference at first, but upon closer inspection you’ll see better results with the bottom colors in each row:
- better-color-utils produces brighter, more vibrant colors when mixing complementary colors, while Sass results in dark, muddy colors (compare mid tones in all examples)
- better-color-utils gives better spacing between colors while Sass inconsistently clumps certain hues together (compare blues in all examples)
- better-color-utils produces more expected colors than Sass (compare how better-color-tools passes through teal in blue–lime while Sass doesn’t)
Usage
@use 'better-color-tools' as color;
$mix: color.mix(#1a7f37, #cf222e, 0);
$mix: color.mix(#1a7f37, #cf222e, 0.25);
$mix: color.mix(#1a7f37, #cf222e, 0.5);
$mix: color.mix(#1a7f37, #cf222e, 0.75);
$mix: color.mix(#1a7f37, #cf222e, 1);
Lighten / Darken
⚠️ Still in development. It’s important to note that Sass’ new color.scale()
utility is now a fantastic way to lighten / darken colors (previous attempts had been lacking). color.scale()
produces better results than this library,
currently, and I’m not happy with that 🙂.
@use 'better-color-tools' as color;
$lighter: color.lighten(#cf222e, 0);
$lighter: color.lighten(#cf222e, 0.25);
$lighter: color.lighten(#cf222e, 1);
$darker: color.darken(#cf222e, 0);
$darker: color.darken(#cf222e, 0.25);
$darker: color.darken(#cf222e, 1);
JavaScript / TypeScript
Mix
View comparison (Sass’ mix function is a generic implementation of mixing you’ll find with other libraries in JavaScript)
Note: you’ll see 0xcf222e
in the examples which is just another way of writing '#cf222e'
. It’s just replacing the #
with 0x
. Use what you prefer!
import color from 'better-color-tools';
const mix = color.mix(0x1a7f37, 0xcf222e, 0);
const mix = color.mix(0x1a7f37, 0xcf222e, 0.25);
const mix = color.mix(0x1a7f37, 0xcf222e, 0.5);
const mix = color.mix(0x1a7f37, 0xcf222e, 0.75);
const mix = color.mix(0x1a7f37, 0xcf222e, 1);
Lighten / Darken
⚠️ In development (see note)
import color from 'better-color-tools';
color.lighten(0xcf222e, 0);
color.lighten(0xcf222e, 0.25);
color.lighten(0xcf222e, 1);
color.darken(0xcf222e, 0);
color.darken(0xcf222e, 0.25);
color.darken(0xcf222e, 1);
Conversion
Color conversion between RGB and hexadecimal is a trivial 1:1 conversion, so this library isn’t better than any other in that regard.
It’s in HSL handling where approaches differ. Because HSL is a smaller color space than RGB, in order to use it, it requires at least 1 decimal place. So any library that rounds out-of-the-box will produce inaccurate results (compare this library to
color-convert converting from RGB -> HSL and back again):
color.from(color.from([167, 214, 65]).hsl).rgbVal;
convert.hsl.rgb(convert.rgb.hsl(167, 214, 65));
The reason, again, is rounding by default. This is a known limitation of HSL, so many libraries can disable rounding with overrides, but in addition to that not being default behavior it also produces noisy results:
color.from([167, 214, 65]).hsl;
convert.rgb.hsl.raw([167, 214, 65]);
This library takes the opinion that HSL should have RGB precision by default. So this library generates values that support infinite conversions without quality loss that are still readable.
Usage
import color from 'better-color-tools';
color.from('rgb(196, 67, 43)').hex;
color.from([196, 67, 43]).hex;
color.from('rgb(196, 67, 43)').hexVal;
color.from('#C4432B').rgb;
color.from(0xc4432b).rgb;
color.from('#C4432B').rgbVal;
color.from('#C4432B').rgba;
color.from(0xc4432b).rgba;
color.from('#C4432B').rgbaVal;
color.from('#C4432B').hsl;
color.from(0xc4432b).hsl;
color.from('#C4432B').hslVal;
color.from('rebeccapurple').hex;
TODO / Roadmap
- Adding color spaces like Adobe and Rec 709 to allow color mixing and lightening/darkening to use different perceptual color algorithms
- This library currently only supports 8-bit RGB (web & apps); is 16-bit useful? (create an issue!)