@ericrovell/blossom
TypeScript icon, indicating that this package has built-in type declarations

1.10.0 • Public • Published
Library logo as flower

Blossom

Blossom is a JS library tool for color manipulations and transformations.

Features:

  • Chainable API;
  • Ummutable;
  • Written in Typescript;
  • Types included;
  • Dependency-free;

Getting started

npm i @ericrovell/blossom
import { blossom } from "@ericrovell/blossom";

blossom("#FF0000")
  .grayscale
  .setAlpha(0.25)
  .toStringRGB

// -> rgb(128 128 128 / 0.25)

Supported color models

  • Hexadecimal strings;
  • RGB (objects, strings);
  • HSL (objects, strings);
  • HSV (objects);
  • CMYK (objects);
  • XYZ (objects);
  • LAB (objects);
  • LCH (objects, strings);
  • HWB (objects, strings);
  • Named colors

API

Color parsing

blossom

Parses the given input and creates a new Blossom instance.

import { blossom } from "@ericrovell/blossom";

// string input
blossom("#ABC");
blossom("#AABBCC");
blossom("#ADCDEF12");
blossom("rgb(100, 200, 255)");
blossom("rgba(100, 200, 255, 0.5)");
blossom("rgba(10% 20% 30% / 35%)");
blossom("hsl(180, 78%, 87%)");
blossom("hsla(180, 78%, 87%, 0.5)");
blossom("hsla(180deg 78% 87% / 50%)");

// object input
blossom({ r: 12, g: 34, b: 56 });
blossom({ r: 12, g: 34, b: 56, a: 1 });
blossom({ h: 180, s: 50, l: 75 });
blossom({ h: 180, s: 50, l: 75, a: 1 });
blossom({ h: 180, s: 50, v: 65 });
blossom({ h: 180, s: 50, v: 65, a: 1 });
blossom({ c: 25, m: 50, k: 75, k: 100 });
blossom({ c: 25, m: 50, k: 75, k: 100, a: 1 });
getModel

Parses a color and returns a color model name of the given input.

  import { getModel } from "@ericrovell/blossom";

  getModel("#ADC123"); // -> "hex"
  getModel({ r: 13, g: 237, b: 162 }); // -> "rgb"
  getModel("hsl(180deg 50% 50%)"); // -> "hsl"
  getModel("Hi!"); // -> null

Color transformations

.hex

Returns the hexadecimal representation of a color. Outputs the modern #RRGGBBAA opacity syntax for transparent colors.

blossom("rgb(0, 255, 0)").hex; // -> "#00FF00"
blossom({ h: 300, s: 100, l: 50 }).hex; // -> "#FF00FF"
blossom({ r: 255, g: 255, b: 255, a: 0 }).hex; // -> "#FFFFFF00"
.rgb

Returns the RGB color model object of a color.

blossom("#ff0000").rgb; // -> { r: 255, g: 0, b: 0, a: 1 }
blossom({ h: 180, s: 100, l: 50, a: 0.5 }).rgb; // -> { r: 0, g: 255, b: 255, a: 0.5 }
.toStringRGB

Returns the RGB color model string of a color. Outputs the modern whitespace syntax.

blossom("#ff0000").toStringRGB; // -> "rgb(255 0 0)"
blossom({ h: 180, s: 100, l: 50, a: 0.5 }).toStringRGB; // -> "rgb(0 255 255 / 0.5)"
.hsl

Returns the HSL color space object of a color.

blossom("#ffff00").hsl; // -> { h: 60, s: 100, l: 50, a: 1 }
blossom("rgba(0, 0, 255, 0.5)").hsl; // -> { h: 240, s: 100, l: 50, a: 0.5 }
.toStringHSL

Returns the HSL color space string of a color. Outputs the modern whitespace syntax.

blossom("#ffff00").toStringHSL; // -> "hsl(60deg 100% 50%)"
blossom("rgba(0, 0, 255, 0.5)").toStringHSL; // -> "hsl(240deg 100% 50% / 0.5)"
.hsv

Returns the HSV color space object of a color.

blossom("#ffff00").hsv; // -> { h: 60, s: 100, v: 100, a: 1 }
blossom("rgba(0, 255, 255, 0.5)").hsv; // -> { h: 180, s: 100, v: 100, a: 1 }
.toStringHSV

Returns the HSV color space string of a color. Outputs the modern whitespace syntax.

blossom("#FFFF00").toStringHSV; // -> "hsc(60deg 100% 100%)"
blossom("rgba(0, 0, 255, 0.5)").toStringHSV; // -> "hsc(240deg 100% 100% / 0.5)"
.cmyk

Returns the CMYK color space object of a color.

blossom("#FFFFFF").cmyk; // -> { c: 0, m: 0, y: 0, k: 0, a: 1 }
blossom("#555AAA").cmyk; // -> { c: 50, m: 47, y: 0, k: 33, a: 1 }
.toStringCMYK

Returns the CMYK color space string of a color.

blossom("#FFFFFF").toStringCMYK; // -> device-cmyk(0% 0% 0% 0%){ c: 0, m: 0, y: 0, k: 0, a: 1 }
blossom("#555AAA80").toStringCMYK; // -> device-cmyk(50% 47% 0 33% / 0.5)
.xyz, available via XYZ plugin

Returns the CIE XYZ color space object of a color.

blossom("#FFFFFF").xyz; // -> { x: 95.047, y: 100, z: 108.883 a: 1 }
blossom({ x: 0, y: 0, z: 0 }).hex; // -> "#000000"
.toStringXYZ

Returns the CIE XYZ color space string of a color.

blossom("#FFFFFF").toStringXYZ; // -> color(xyz 96.42 100 82.52)
blossom("#555AAA80").toStringXYZ; // -> color(xyz 13.65 11.79 29.83 / 0.5)
.lab, available via LAB plugin

Returns the CIE LAB color space object of a color.

blossom("#FFFFFF").lab; // -> { l: 100, a: 0, b: 0, alpha: 1 }
blossom("#123ABC").lab; // -> { l: 29.95, a: 29.48, b: -72.93, alpha: 1 }
.toStringLAB

Returns the CIE LAB color space string of a color.

blossom("#FFFFFF").toStringLAB; // -> lab(100% 0 0)
blossom("#555AAA80").toStringLAB; // -> lab(40.88% 15.43 -44.4)
.lch, available via LCH plugin

Returns the CIE LCH color space object of a color.

blossom("#FFFFFF").lch; // -> { l: 100, c: 0, h: 0, a: 1 }
blossom("#123ABC").lch; // -> { l: 29.95, c: 78.66, c: 292.01, alpha: 1 }
.toStringLCH

Returns the CIE LCH color space string of a color.

blossom("#FFFFFF").toStringLCH; // -> lch(100% 0 0)
blossom("#555AAA80").toStringLCH; // -> lch(40.88% 47.01 289.16 / 0.5)
.hwb, available via HWB plugin

Returns the HWB color space object of a color.

blossom("#FFFFFF").hwb; // -> { h: 0, w: 100, b: 0, a: 1 }
blossom("#123ABC").hwb; // -> { h: 226, w: 7, b: 26, a: 1 }
.toStringHWB

Returns the HWB color space string of a color.

blossom("#FFFFFF").toStringHWB; // -> hwb(0 100% 0%)
blossom("#555AAA80").toStringHWB; // -> hwb(236 33% 33% / 0.5)
.name, available via Names plugin

Returns the name of the color as exact match of browser supported list of 140 named colors. If the color has not an alias returns null instead.

blossom("#FF0000").name; // -> "red"
blossom("#123ABC").name; // -> null
.closestName, available via Names plugin

Returns the closest named color from browser supported list of 140 named colors color.

blossom("#FF0001").closestName; // -> "red"
blossom("#66339A").closestName; // -> "rebeccapurple"

Color manipulation

.setAlpha(value)

Changes the alpha channel value and returns a new Blossom instance.

blossom("rgb(0, 0, 0)")
  .setAlpha(0.5)
  .toStringRGB; // -> "rgb(0 0 0 / 0.5)"
.inverted

Creates a new Blossom instance with an inverted color.

blossom("#aabbcc")
  .inverted
  .hex; // -> "#554433"
.saturate(amount = 0.1)

Increases the HSL saturation of a color by the given amount.

blossom("#bf4040")
  .saturate(0.25)
  .hex; // -> "#df2020"

blossom("hsl(0, 50%, 50%)")
  .saturate(0.5)
  .toStringHSL; // -> "hsl(0deg 100% 50%)"
.desaturate(amount = 0.1)

Decreases the HSL saturation of a color by the given amount.

blossom("#df2020")
  .saturate(0.25)
  .hex; // -> "#bf4040"

blossom("hsl(0, 100%, 50%)")
  .saturate(0.5)
  .toStringHSL; // -> "hsl(0deg 50% 50%)"  
.grayscale

Creates a gray color with the same lightness as a source color. Same result as .desaturate(1).

blossom("#bf4040")
  .grayscale
  .hex; // -> "#808080"

blossom("hsl(0, 100%, 50%)")
  .grayscale
  .toStringHSL; // -> "hsl(0deg 0% 50%)"
.lighten(amount = 0.1)

Increases the HSL lightness of a color by the given amount.

blossom("#000000")
  .lighten(0.5)
  .hex; // -> "#808080"

blossom("#223344")
  .lighten(0.3)
  .hex; // -> "#5580aa"

blossom("hsl(0, 50%, 50%)")
  .lighten(0.5)
  .toStringHSL; // -> "hsl(0deg 50% 100%)"
.darken(amount = 0.1)

Decreases the HSL lightness of a color by the given amount.

blossom("#ffffff")
  .darken(0.5)
  .hex; // -> "#808080"

blossom("#5580aa")
  .darken(0.3)
  .hex; // -> "#223344"

blossom("hsl(0, 50%, 100%)")
  .lighten(0.5)
  .toStringHSL; // -> "hsl(0, 50%, 50%)"
.setHue(value)

Changes the hue value and returns a new Blossom instance.

blossom("hsl(90, 50%, 50%)")
  .setHue(180)
  .toStringHSL; // -> "hsl(180deg 50% 50%)"

blossom("hsl(90, 50%, 50%)")
  .setHue(370)
  .toStringHSL; // -> "hsl(10deg 50% 50%)"
.rotate(amout = 15)

Increases the HSL hue value of a color by the given amount.

blossom("hsl(90, 50%, 50%)")
  .rotate(90)
  .toStringHSL; // -> "hsl(180deg 50% 50%)"

blossom("hsl(90, 50%, 50%)")
  .rotate(-180)
  .toStringHSL; // -> "hsl(270deg 50% 50%)"

Color properties

.valid

Returns a boolean indicating whether or not an input has been parsed successfully. On unsuccess, color value defaults to black without error.

blossom("#FFF").valid; // -> true
blossom("#NaN").valid; // -> false
blossom("hello").valid; // -> false
blossom({ r: 0, g: 0, b: 0 }).valid; // -> true
blossom({ r: 0, g: 0, v: 0 }).valid; // -> false
.alpha

Returns an alpha channel value of the color.

blossom("#FFFFFF").alpha; // -> 1
blossom("rgba(50 100 150 / 0.5)").alpha; // -> 0.5
.opaque

Returns a boolean indicating whether or not a color is opaque.

blossom("#FFFFFF").opaque; // -> true
blossom("rgba(50 100 150 / 0.5)").opaque; // -> false
.transparent

Returns a boolean indicating whether or not a color is transparent.

blossom("#FFFFFF").transparent; // -> false
blossom("rgba(50 100 150 / 0.5)").transparent; // -> true
.hue

Returns the Hue value of the number on the color wheel.

blossom("hsl(90deg 50% 50%)").hue; // -> 90
blossom("hsl(-10deg 50% 50%)").hue; // -> 350
.saturation

Returns the saturation value of the number.

blossom("hsl(90deg 50% 50%)").saturation; // -> 0.5
blossom("hsl(-10deg 98% 50%)").saturation; // -> 0.98
.lightness

Returns the lightness value of the number.

blossom("hsl(90deg 50% 50%)").lightness; // -> 0.5
blossom("hsl(-10deg 50% 46%)").lightness; // -> 0.46
.brightness

Returns the brightness of a color in range [0; 1]. The calculation logic is modified from Web Content Accessibility Guidelines.

blossom("#000000").brightness; // -> 0
blossom("#808080").brightness; // -> 0.5
blossom("#FFFFFF").brightness; // -> 1
.light

A Boolean indicator whether or not a color is light (brightness >= 0.5).

blossom("#000000").light; // -> false
blossom("#808080").light; // -> true
blossom("#FFFFFF").light; // -> true
.dark

A Boolean indicator whether or not a color is dark (brightness < 0.5).

blossom("#000000").dark; // -> true
blossom("#808080").dark; // -> false
blossom("#FFFFFF").dark; // -> false
.luminance, available via a11y plugin

Returns the relative luminance of a color, normalized to range [0, 1] as from pure black to pure white according to WCAG 2.0.

blossom("#000000").luminance; // 0
blossom("#808080").luminance; // 0.22
blossom("#ffffff").luminance; // 1
.contrast(color = "#FFF"), available via a11y plugin

Calculates a contrast ratio for a pair of colors.

The luminance difference lies in range [1 (white on white), 21 (black on white)]. WCAG required a ratio at least 4.5 for normal text and 3:1 for large one.

blossom("#000000").contrast(); // 21 (black on white)
blossom("#ffffff").contrast("#000000"); // 21 (white on black)
blossom("#777777").contrast(); // 4.47 (gray on white)
blossom("#ff0000").contrast(); // 3.99 (red on white)
blossom("#0000ff").contrast("#ff000"); // 2.14 (blue on red)
.readable(color = "#FFF", options?), available via a11y plugin

Checks that a background and text color pair is readable according to WCAG 2.0 Contrast and Color Requirements.

blossom("#000000").isReadable(); // true (normal black text on white bg conforms to WCAG AA)
blossom("#777777").isReadable(); // false (normal gray text on white bg conforms to WCAG AA)
blossom("#ffffff").isReadable("#000000"); // true (normal white text on black bg conforms to WCAG AA)
blossom("#e60000").isReadable("#ffff47", { level: "AAA" }); // false (normal red text on yellow bg does not conform to WCAG AAA)
blossom("#e60000").isReadable("#ffff47", { level: "AAA", size: "large" }); // true (large red text on yellow bg conforms to WCAG AAA)
.mix(color, ratio = 0.5), available via mix plugin

Produces a mixture of two colors and returns the result as new Blossom instance. Mix produced using CIE LAB color space.

import { colord, extend } from "blossom";
import pluginMix from "@ericrovell/blossom/plugins/mix";

extend([ mixPlugin ]);

blossom("#FFFFFF").mix("#000000").hex; // -> "#777777"
blossom("#800080").mix("#DDA0DD").hex; // -> "#AF5CAE"
blossom("#CD853F").mix("#EEE8AA", 0.6).hex; // -> "#E3C07E"
blossom("#008080").mix("#808000", 0.35).hex; // -> "#50805D"
.delta(color = "#FFF"), available via LAB plugin

Calculates the perceived color difference between two colors. The difference calculated according to Delta E2000.

Returns the normalized value in range from [0, 1], where 0 means the same colors and 1 are completely different ones.

colord("#3296fa").delta("#197dc8") // 0.099
colord("#faf0c8").delta("#fff") // 0.148
colord("#afafaf").delta("#b4b4b4") // 0.014
colord("#000").delta("#fff") // 1.0

Color Utilities

random()

Creates new instance with a random color.

import { random } from "@ericrovell/blossom";

random().hex; // -> "#01C8EC"
random().setAlpha(0.5).rgb; // -> { r: 13, g: 237, b: 162, a: 0.5 }

Plugins

Usage

To extend the functionality using plugins, the extend function should be used:

import { blossom, extend } from "@ericrovell/blossom";
import { plugin1, plugin2 } from "plugin-path;

extend([
  plugin1,
  plugin2
]);

Included plugins

Harmonies

Provides functionatity to generate harmony colors.

Available harmony pallets:

  • analogous;
  • complimentary;
  • double-split-comlimentary;
  • rectangle;
  • tetradic;
  • triadic;
  • split-complimentary;
import { blossom, extends } from "@ericrovell/blossom";
import { pluginHarmonies } from "blossom/plugins/harmonies";

extend([ pluginHarmonies ]);

const color = blossom("FF0000");

color.harmonies("analogous")
  .map(color => color.hex); // -> [ "#FF0080", "#FF0000", "#FF8000"]
color.harmonies("complimentary")
  .map(color => color.hex); // -> [ "#FF0000", "#00FFFF" ]
color.harmonies("double-split-complimentary")
  .map(color => color.hex); // -> [ "#FF0080", "#FF0000", "#FF8000", "#00FF80", "#0080FF" ]
color.harmonies("rectangle")
  .map(color => color.hex); // -> [ "#FF0000", "#FFFF00", "#00FFFF", "#0000FF" ]
color.harmonies("tetradic")
  .map(color => color.hex); // -> [ "#FF0000", "#80FF00", "#00FFFF", "#8000FF" ]
color.harmonies("triadic"  )
  .map(color => color.hex); // -> [ "#FF0000", "#00FF00", "#0000FF" ]
color.harmonies("split-complimentary")
  .map(color => color.hex); // -> [ "#FF0000", "#00FF80", "#0080FF" ]

Harmony color schemes type is available for import:

import type { Harmony } from "@ericrovell/blossom/plugins/harmonies";

const harmony: Harmony = "analogous";
const notHarmony: Harmony = "round"; // TypeError
Monochromatic

Provides functionatity to generate monochromatic colors as:

  • Tints;
  • Shades;
  • Tones.
import { blossom, extends } from "@ericrovell/blossom";
import { pluginMonochromatic } from "blossom/plugins/monochromatic";

extend([ pluginMonochromatic ]);

const color = blossom("FF0000");

color.tints(4).map(tint => tint.hex); // -> [ "#FF0000", "#FF4242", "#FF8585", "#FFC7C7", "#FFFFFF" ]
color.shades(4).map(shade => shade.hex); // -> [ "#FF0000", "#BD0000", "#7A0000", "#380000", "#000000" ]
color.tones(4).map(tone => tone.hex); // -> [ "#FF0000", "#DF2020", "#BF4040", "#9F6060", "#808080" ]

The original color is always included as first palette item.

If there is not enough space between colors to generate required number of colors, less number of colors will be generated. For example, generating 10 shades for #050505 is not practical as #000000 is too close and all shades will be indistinguishable:

import { blossom, extends } from "@ericrovell/blossom";
import { pluginMonochromatic } from "blossom/plugins/monochromatic";

extend([ pluginMonochromatic ]);

blossom("#FAFAFA").tints(10).map(tint => tint.hex); // -> [ "#FAFAFA", "#FDFDFD", "#FFFFFF" ]
blossom("#050505").shades(10).map(shade => shade.hex); // -> [ "#050505", "#020202", "#000000" ]
blossom("#827D7D").tones(10).map(tone => tone.hex); // -> [ "#827D7D", "#817E7E", "#808080" ]
A11Y (Accessibility)

Adds accessibility and color contrast utilities working according to Web Content Accessibility Guidelines 2.0.

import { blossom, extend } from "@ericrovell/blossom";
import { pluginA11Y } from "@ericrovell/blossom/plugins/a11y";

extend([ pluginA11Y ]);

blossom("#000000").luminance; // 0
blossom("#CCDDEE").luminance; // 0.71
blossom("#FFFFFF").luminance; // 1

blossom("#000000").contrast(); // 21 (black on white)
blossom("#FFFFFF").contrast("#000000"); // 21 (white on black)
blossom("#0000ff").contrast("#FF000"); // 2.14 (blue on red)

blossom("#000000").readable(); // true (black on white)
blossom("#FFFFFF").readable("#000000"); // true (white on black)
blossom("#777777").readable(); // false (gray on white)
blossom("#E60000").readable("#FFFF47"); // true (normal red text on yellow bg conforms to WCAG AA)
blossom("#E60000").readable("#FFFF47", { level: "AAA" }); // false (normal red text on yellow bg does not conform to WCAG AAA)
blossom("#E60000").readable("#FFFF47", { level: "AAA", size: "large" }); // true (large red text on yellow bg conforms to WCAG AAA)
XYZ (CIE XYZ Color space)

Adds support of CIE XYZ color model.

import { blossom, extend } from "@ericrovell/blossom";
import { pluginXYZ } from "@ericrovell/blossom/plugins/xyz";

extend([ pluginXYZ ]);

blossom("#FFFFFF").xyz; // -> { x: 95.047, y: 100, z: 108.883, a: 1 }
blossom({ x: 0, y: 0, z: 0 }).hex; // -> "#000000"
LAB (CIE LAB Color space)

Adds support of CIE LAB color model. Plugin provides .delta method for perceived color difference calculations: 0 for same colors, 1 for completely different ones.

import { blossom, extend } from "@ericrovell/blossom";
import { pluginLAB } from "@ericrovell/blossom/plugins/lab";

extend([ pluginLAB ]);

blossom({ l: 29.95, a: 29.48, b: -72.93 }).hex; // "#123ABC"
blossom("#FFFFFF").lab; // { l: 100, a: 0, b: 0, alpha: 1 }

blossom("#afafaf").delta("#b4b4b4") // 0.014
blossom("#000").delta("#fff") // 1.0
LCH (CIE LCH Color space)

Adds support of CIE LCH color model.

import { blossom, extend } from "@ericrovell/blossom";
import { pluginLCH } from "@ericrovell/blossom/plugins/lch";

extend([ pluginLCH ]);

blossom({ l: 29.95, a: 29.48, b: 40.21 }).hex; // "#6B372A"
blossom("#FFFFFF").lab; // { l: 100, c: 0, h: 0, a: 1 }
HWB

Adds support of HWB color model.

import { blossom, extend } from "@ericrovell/blossom";
import { pluginHWB } from "@ericrovell/blossom/plugins/hwb";

extend([ pluginHWB ]);

blossom({h: 236, w: 33, b: 33 }).hex; // "#555AAA"
blossom("#FFFFFF").hwb; // { h: 0, w: 100, b: 0 }
Mix

Adds support for mixing colors. Mixture is produced using CIE LAB color space.

import { colord, extend } from "blossom";
import pluginMix from "@ericrovell/blossom/plugins/mix";

extend([ mixPlugin ]);

blossom("#FFFFFF").mix("#000000").hex; // -> "#777777"
blossom("#800080").mix("#DDA0DD").hex; // -> "#AF5CAE"
blossom("#CD853F").mix("#EEE8AA", 0.6).hex; // -> "#E3C07E"
blossom("#008080").mix("#808000", 0.35).hex; // -> "#50805D"
Names

Adds support for a list of 140 named colors supported by all modern browsers.

import { colord, extend } from "blossom";
import pluginNames from "@ericrovell/blossom/plugins/names";

extend([ pluginNames ]);

blossom("white").hex           // -> "#FFFFFF"
blossom("red").hex             // -> "#FF0000"
blossom("#FF0000").name        // -> "red"
blossom("#ABC123").name        // -> null
blossom("#FF0101").closestName // -> "red"
blossom("#66339A").closestName // -> "rebeccapurple"

Types

Blossom is written in strict TypeScript and ships with types in the library itself.

While not only typing its own functions and variables, you can also type yours. Depending on the color space you are using, the types can be also imported and used to type the code.

import type { ColorRGB, ColorHSL } from "@ericrovell/blossom";

const foo: ColorHSL = { h: 0, s: 0, l: 0 };
const bar: ColorRGB = { r: 0, g: 0, v: 0 }; // type error!

Extending the functionality

To extend functionality for your needs, just extend the Blossom class:

import { Blossom } from "@ericrovell/blossom";

class BlossomExtended extends Blossom {
  constructor(input?) {
		super(input);
	}

  get red() {
    return this.color.r;
  }
}

const extended = new BlossomExtended("#FF0000");
extended.red // -> 255;

Inspiration

It was a long time I was thinking about this project. There were two unsuccessfull attemps, it was not thoughfull enough.

This project is inspired by fantastic colord project, that gave me the idea about architecture and implementation.

Package Sidebar

Install

npm i @ericrovell/blossom

Weekly Downloads

5

Version

1.10.0

License

MIT

Unpacked Size

123 kB

Total Files

102

Last publish

Collaborators

  • ericrovell