Package Exports
- @ericrovell/blossom
 - @ericrovell/blossom/plugins/harmonies
 - @ericrovell/blossom/plugins/monochromatic
 
Readme
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/blossomimport { blossom } from "@ericrovell/blossom";
blossom("#FF0000")
  .grayscale
  .setAlpha(0.25)
  .toStringRGB
// -> rgb(128 128 128 / 0.25)Supported color models
- Hexadecimal strings;
 - RGB (strings, objects);
 - HSL (strings, objects);
 - HSV (objects);
 - CMYK (objects).
 
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!"); // -> nullColor 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 }
    .cmyk
  
Returns the CMYK color space object of a color.
blossom("#fffff").cmyk; // -> { c: 0, m: 0, y: 0, k: 0, a: 1 }
blossom("#555aaa").cmyk; // -> { c: 50, m: 47, y: 0, k: 33, a: 1 }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 analysis
    .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; // -> falseColor 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
]);Developing plugins
To develop a custom plugin and extend library's functionality, the function should be created which integrates methods using prototype chain.
export const plugin = (BaseClass) =>  {
  BaseClass.prototype.newMethod = function() {
    // ...
  }
}After that it should be provided as the parameter for extend function.
Developing plugins with TypeScript
To develop a plugin with TypeScript, module blossom should be declared with the Blossom interface described.
import { Plugin } from "@blossom/types";
declare module "blossom" {
  interface Blossom {
    newMethod(): void;
  }
}
export const pluginHarmonyColors: Plugin = (BaseClass): void =>  {
  BaseClass.prototype.newMethod = function() {
    // ...
  }
}Included plugins
    
      Harmonies
    
  
Provides functionatity to generate harmony colors.
import { blossom, extends } from "@ericrovell/blossom";
import { harmonies } from "blossom/plugins/harmonies";
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("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("splitcomplimentary")
  .map(color => color.hex); // -> [ "#FF0000", "#00FF80", "#0080FF" ]Harmony color schemes is available as type:
import type { Harmony } from "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 { monochromatic } from "blossom/plugins/monochromatic";
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 { monochromatic } from "blossom/plugins/monochromatic";
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" ]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 "blossom/types";
const foo: ColorHSL = { h: 0, s: 0, l: 0 };
const bar: ColorRGB = { r: 0, g: 0, v: 0 }; // type error!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.