Saturday, July 2, 2022

Color Manipulation with JavaScript

CSS-in-JS Tutorials

One of many draws of CSS libraries like Sass and Less are their wide assortment of color manipulation functions such as lighten, darken, saturate, etc. With the growing popularity of CSS-in-JS, the need for JavaScript (JS) equivalents has followed suit. Right on cue, a number of JS libraries for color conversion and manipulation have cropped up to make the jobs of web developers easier. In fact, right now it is hard to settle on which one to use. One library that comes recommended by the popular JSS CSS-in-JS library is simply called “color“. It is quite feature rich, having more to offer than say color2k and a smaller size than chroma.js. This web development tutorial will provide an overview of some of color’s many features and put them to work in an Angular Single Page Application (SPA).

Before getting started, you may want to check out our Introduction to CSS-in-JS tutorial.

How to Setup and Install the JSS color Library

The recommended way to install color is to use the Node Package Manager (NPM). The command is simply:

$ npm install color

Or even npm i color if you are really into minimizing typing.

Then you can refer to it in your node.js files using require:

const Color = require('color');

In Angular, you should use import:

import Color from 'color';

The exposed Color object provides a few different constructors to choose from, most of which accept some permutation of Red Green Blue (RGB) values. It will also accept a limited number of strings, such as the oft-used HEX format:

const color = Color('#FFFFFF');
const color = Color('rgb(255, 255, 255)');
const color = Color({r: 255, g: 255, b: 255});
const color = Color.rgb(255, 255, 255);
const color = Color.rgb([255, 255, 255]);

Under the covers, String constructors are handled by the color-string library.

You can learn more about dynamic styling options by reading our Dynamic Styling with JSS tutorial.

Exploring the JS Color API

Color’s API was heavily inspired by color-js, whose manipulation functions closely resemble those of CSS tools like Sass, LESS, and Stylus. But, before we get to those, let’s look at how to convert colors to other formats.

CSS supports a multitude of color types, from preset names like “aliceblue“, and “teal” to HEX (“#ee652e“), RGB (rgb(138, 238, 130)), and RGBA (rgba(0,255,0,0.75)).

Looking for the rgb number value? Call the hex() method:

color.hex() // #ffffff

Convert a color to an rgb object:

color.object(); // {r: 255, g: 255, b: 255}

Here’s how to set a hash of the color value and reflect the color’s current model:

color.rgb().array() // [255, 255, 255]

To get the individual rgb values, use these:

color.red()   //255
color.green() //255
color.blue()  //255

Read: Using an Angular Service to Read Sass Variables

Color Manipulation Functions and CSS Colors

Sass comes with a lot of great functions that can easily be applied to CSS colors. These functions take some of the sting out of choosing and manipulating colors. These include darken and lighten, saturate and desaturate, rgba, and more. So, too, with the color library. Here’s a sampling:

//lighten by 50%
color.lighten(0.5)     // hsl(100, 50%, 50%) -> hsl(100, 50%, 75%)
//darken by 50%
color.darken(0.5)      // hsl(100, 50%, 50%) -> hsl(100, 50%, 25%)
color.saturate(0.5)    // hsl(100, 50%, 50%) -> hsl(100, 75%, 50%)
color.desaturate(0.5)  // hsl(100, 50%, 50%) -> hsl(100, 25%, 50%)
rgba(10, 10, 10, 0.8)

Other useful manipulation functions include:

color.grayscale()      // #5CBF54 -> #969696
color.whiten(0.5)      // hwb(100, 50%, 50%) -> hwb(100, 75%, 50%)
color.blacken(0.5)     // hwb(100, 50%, 50%) -> hwb(100, 50%, 75%)

color.fade(0.5)        // rgba(10, 10, 10, 0.8) -> rgba(10, 10, 10, 0.4)
color.opaquer(0.5)     // rgba(10, 10, 10, 0.8) -> rgba(10, 10, 10, 1.0)

color.rotate(180)      // hsl(60, 20%, 20%) -> hsl(240, 20%, 20%)
color.rotate(-90)      // hsl(60, 20%, 20%) -> hsl(330, 20%, 20%)

color.mix(Color("yellow"))        // cyan -> rgb(128, 255, 128)
color.mix(Color("yellow"), 0.3)   // cyan -> rgb(77, 255, 179)

All of the above functions may be chained together:

color.green(100).grayscale().lighten(0.6)

Other functions provide information about a color:

color.isLight()         // true
color.isDark()          // false

Using color.js to Set Theme Colors

In the last couple of articles on CSS-in-JS we have been adapting the Using an Angular Service to Read Sass Variables Demo to employ JSS to set an Angular component’s colors via @Input parameters. Although JSS and color.js make a perfect pairing, each library can be used independently of the other. To emphasize that point, we will apply color.js to the Color Service that defines the theme colors for the application.

In the app.component.scss file, Sass functions are employed to set the light and dark versions of the base colors:

$backgroundColor: rgb(82, 172, 240);
$lightBackgroundCcolor: lighten($backgroundColor, 16%);
$hoverColor: blue;
$darkHoverColor: darken($hoverColor, 20%);
$focusBorderColor: darkgray;
$darkFocusBorderColor: darken($focusBorderColor, 40%);

Imagine a situation where our application was built using vanilla CSS rather than Sass. Without access to the Sass functions, we would require another means of affecting the base colors. That would be an ideal use of the color library.

We would still have access to the three base colors via CSS variables:

.color-demo-app {
  --background-color: rgb(82, 172, 240);
  --hover-color: blue;
  --focus-border-color: lightgray;
}

Now, in loadColors(), we can add two additional calls to the new setMapColor() function for each CSS property’s light and dark variation:

import Color from 'color';

const CSS_PREFIX = "--";
const CSS_SUFFIX = "color";
const CSS_DELIMITER = "-";

export enum PropertyNames {
  background  = 'background',
  hover       = 'hover',
  focusborder = 'focus-border'
}

public loadColors() {
  // Read the custom property of body section with given name:
  const appElement = 
    document.getElementsByClassName('color-demo-app');
  if (appElement && appElement.length > 0) {
    const appStyles = window.getComputedStyle(appElement[0]);
    Object.values(PropertyNames).forEach(propertyName => {
      const cssVariableName = CSS_PREFIX
        + `${propertyName}${CSS_DELIMITER}`
        + CSS_SUFFIX;
      const cssVariableValue = 
        appStyles.getPropertyValue(cssVariableName)
                  .replace(' ', '');
      if (cssVariableValue) {
        this.setMapColor(propertyName, cssVariableValue);
      }
      // load light color 
      this.setMapColor(
        propertyName, 
        Color(cssVariableValue).lighten(0.2).hex(),
        ColorOptions.light
      );
      // load dark color 
      this.setMapColor(
        propertyName, 
        Color(cssVariableValue).darken(0.2).hex(),
        ColorOptions.dark
      );
    });

    this.setThemeDefaults();
  }
}

Extracting the code to set the _colorMap entry to a helper function only makes sense since we have to do it three times for each CSS property:

private setMapColor(
  propertyName: PropertyNames, 
  value: string,
  colorOption = ColorOptions.standard 
) {
  const colorMapKey = <ColorProperty>{
    name: propertyName,
    option: colorOption
  };
  this._colorMap.set(
    JSON.stringify(colorMapKey),
    value
  );
}

You can see how all of the above code works together in the stackblitz demo.

Conclusion

There are a few reasons to love color libraries like color.js. Firstly, using JavaScript to manipulate colors provides additional scoping and dynamic execution to accomplish things that would be quite difficult using CSS, even with help from extension libraries like Sass and Less. Another attractive feature is their ease of use, at least where color.js is concerned.

Read more CSS and web development tutorials by visiting our CSS programming section.

Rob Gravelle
Rob Gravelle
Rob Gravelle resides in Ottawa, Canada, and has been an IT guru for over 20 years. In that time, Rob has built systems for intelligence-related organizations such as Canada Border Services and various commercial businesses. In his spare time, Rob has become an accomplished music artist with several CDs and digital releases to his credit.

Popular Articles

Featured