+
+
diff --git a/spacebook/_includes/layouts/robots.njk b/spacebook/_includes/layouts/robots.njk
new file mode 100644
index 00000000..1f53798b
--- /dev/null
+++ b/spacebook/_includes/layouts/robots.njk
@@ -0,0 +1,2 @@
+User-agent: *
+Disallow: /
diff --git a/spacebook/_tmp/style.css b/spacebook/_tmp/style.css
new file mode 100644
index 00000000..7911f43a
--- /dev/null
+++ b/spacebook/_tmp/style.css
@@ -0,0 +1,385 @@
+/*! tailwindcss v2.2.19 | MIT License | https://tailwindcss.com */
+
+/*! modern-normalize v1.1.0 | MIT License | https://github.com/sindresorhus/modern-normalize */
+
+/*
+Document
+========
+*/
+
+/**
+Use a better box model (opinionated).
+*/
+
+*,
+::before,
+::after {
+ box-sizing: border-box;
+}
+
+/**
+Use a more readable tab size (opinionated).
+*/
+
+/**
+1. Correct the line height in all browsers.
+2. Prevent adjustments of font size after orientation changes in iOS.
+*/
+
+/*
+Sections
+========
+*/
+
+/**
+Remove the margin in all browsers.
+*/
+
+/**
+Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3)
+*/
+
+/*
+Grouping content
+================
+*/
+
+/**
+1. Add the correct height in Firefox.
+2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
+*/
+
+/*
+Text-level semantics
+====================
+*/
+
+/**
+Add the correct text decoration in Chrome, Edge, and Safari.
+*/
+
+/**
+Add the correct font weight in Edge and Safari.
+*/
+
+/**
+1. Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3)
+2. Correct the odd 'em' font sizing in all browsers.
+*/
+
+/**
+Add the correct font size in all browsers.
+*/
+
+/**
+Prevent 'sub' and 'sup' elements from affecting the line height in all browsers.
+*/
+
+/*
+Tabular data
+============
+*/
+
+/**
+1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
+2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
+*/
+
+/*
+Forms
+=====
+*/
+
+/**
+1. Change the font styles in all browsers.
+2. Remove the margin in Firefox and Safari.
+*/
+
+/**
+Remove the inheritance of text transform in Edge and Firefox.
+1. Remove the inheritance of text transform in Firefox.
+*/
+
+/**
+Correct the inability to style clickable types in iOS and Safari.
+*/
+
+/**
+Remove the inner border and padding in Firefox.
+*/
+
+::-moz-focus-inner {
+ border-style: none;
+ padding: 0;
+}
+
+/**
+Restore the focus styles unset by the previous rule.
+*/
+
+/**
+Remove the additional ':invalid' styles in Firefox.
+See: https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737
+*/
+
+/**
+Remove the padding so developers are not caught out when they zero out 'fieldset' elements in all browsers.
+*/
+
+/**
+Add the correct vertical alignment in Chrome and Firefox.
+*/
+
+/**
+Correct the cursor style of increment and decrement buttons in Safari.
+*/
+
+::-webkit-inner-spin-button,
+::-webkit-outer-spin-button {
+ height: auto;
+}
+
+/**
+1. Correct the odd appearance in Chrome and Safari.
+2. Correct the outline style in Safari.
+*/
+
+/**
+Remove the inner padding in Chrome and Safari on macOS.
+*/
+
+::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+
+/**
+1. Correct the inability to style clickable types in iOS and Safari.
+2. Change font properties to 'inherit' in Safari.
+*/
+
+::-webkit-file-upload-button {
+ -webkit-appearance: button; /* 1 */
+ font: inherit; /* 2 */
+}
+
+/*
+Interactive
+===========
+*/
+
+/*
+Add the correct display in Chrome and Safari.
+*/
+
+/**
+ * Manually forked from SUIT CSS Base: https://github.com/suitcss/base
+ * A thin layer on top of normalize.css that provides a starting point more
+ * suitable for web applications.
+ */
+
+/**
+ * Removes the default spacing and border for appropriate elements.
+ */
+
+/**
+ * Tailwind custom reset styles
+ */
+
+/**
+ * 1. Use the user's configured `sans` font-family (with Tailwind's default
+ * sans-serif font stack as a fallback) as a sane default.
+ * 2. Use Tailwind's default "normal" line-height so the user isn't forced
+ * to override it to ensure consistency even when using the default theme.
+ */
+
+/**
+ * Inherit font-family and line-height from `html` so users can set them as
+ * a class directly on the `html` element.
+ */
+
+/**
+ * 1. Prevent padding and border from affecting element width.
+ *
+ * We used to set this in the html element and inherit from
+ * the parent element for everything else. This caused issues
+ * in shadow-dom-enhanced elements like
where the content
+ * is wrapped by a div with box-sizing set to `content-box`.
+ *
+ * https://github.com/mozdevs/cssremedy/issues/4
+ *
+ *
+ * 2. Allow adding a border to an element by just adding a border-width.
+ *
+ * By default, the way the browser specifies that an element should have no
+ * border is by setting it's border-style to `none` in the user-agent
+ * stylesheet.
+ *
+ * In order to easily add borders to elements by just setting the `border-width`
+ * property, we change the default border-style for all elements to `solid`, and
+ * use border-width to hide them instead. This way our `border` utilities only
+ * need to set the `border-width` property instead of the entire `border`
+ * shorthand, making our border utilities much more straightforward to compose.
+ *
+ * https://github.com/tailwindcss/tailwindcss/pull/116
+ */
+
+*,
+::before,
+::after {
+ box-sizing: border-box; /* 1 */
+ border-width: 0; /* 2 */
+ border-style: solid; /* 2 */
+ border-color: currentColor; /* 2 */
+}
+
+/*
+ * Ensure horizontal rules are visible by default
+ */
+
+/**
+ * Undo the `border-style: none` reset that Normalize applies to images so that
+ * our `border-{width}` utilities have the expected effect.
+ *
+ * The Normalize reset is unnecessary for us since we default the border-width
+ * to 0 on all elements.
+ *
+ * https://github.com/tailwindcss/tailwindcss/issues/362
+ */
+
+/**
+ * Override legacy focus reset from Normalize with modern Firefox focus styles.
+ *
+ * This is actually an improvement over the new defaults in Firefox in our testing,
+ * as it triggers the better focus styles even for links, which still use a dotted
+ * outline in Firefox by default.
+ */
+
+/**
+ * Reset links to optimize for opt-in styling instead of
+ * opt-out.
+ */
+
+/**
+ * Reset form element properties that are easy to forget to
+ * style explicitly so you don't inadvertently introduce
+ * styles that deviate from your design system. These styles
+ * supplement a partial reset that is already applied by
+ * normalize.css.
+ */
+
+/**
+ * Use the configured 'mono' font family for elements that
+ * are expected to be rendered with a monospace font, falling
+ * back to the system monospace stack if there is no configured
+ * 'mono' font family.
+ */
+
+/**
+ * 1. Make replaced elements `display: block` by default as that's
+ * the behavior you want almost all of the time. Inspired by
+ * CSS Remedy, with `svg` added as well.
+ *
+ * https://github.com/mozdevs/cssremedy/issues/14
+ *
+ * 2. Add `vertical-align: middle` to align replaced elements more
+ * sensibly by default when overriding `display` by adding a
+ * utility like `inline`.
+ *
+ * This can trigger a poorly considered linting error in some
+ * tools but is included by design.
+ *
+ * https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210
+ */
+
+/**
+ * Constrain images and videos to the parent width and preserve
+ * their intrinsic aspect ratio.
+ *
+ * https://github.com/mozdevs/cssremedy/issues/14
+ */
+
+/**
+ * Ensure the default browser behavior of the `hidden` attribute.
+ */
+
+*, ::before, ::after {
+ --tw-border-opacity: 1;
+ border-color: rgba(229, 231, 235, var(--tw-border-opacity));
+}
+
+::-webkit-datetime-edit-fields-wrapper {
+ padding: 0;
+}
+
+::-webkit-date-and-time-value {
+ min-height: 1.5em;
+}
+
+/* Set up some default image behavior for nicer images */
+
+/* Overrides for Tailwind Typography prose class */
+
+/* Define blockquotes and some standard callout blocks */
+
+/* Overrides for nav/Table of Contents block */
+
+/* Utilities and misc */
+
+@keyframes spin {
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+@keyframes ping {
+ 75%, 100% {
+ transform: scale(2);
+ opacity: 0;
+ }
+}
+
+@keyframes pulse {
+ 50% {
+ opacity: .5;
+ }
+}
+
+@keyframes bounce {
+ 0%, 100% {
+ transform: translateY(-25%);
+ animation-timing-function: cubic-bezier(0.8,0,1,1);
+ }
+
+ 50% {
+ transform: none;
+ animation-timing-function: cubic-bezier(0,0,0.2,1);
+ }
+}
+
+*, ::before, ::after {
+ --tw-shadow: 0 0 #0000;
+}
+
+*, ::before, ::after {
+ --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);
+ --tw-ring-offset-width: 0px;
+ --tw-ring-offset-color: #fff;
+ --tw-ring-color: rgba(59, 130, 246, 0.5);
+ --tw-ring-offset-shadow: 0 0 #0000;
+ --tw-ring-shadow: 0 0 #0000;
+}
+
+@media (min-width: 640px) {
+}
+
+@media (min-width: 768px) {
+}
+
+@media (min-width: 1024px) {
+}
+
+@media (min-width: 1280px) {
+}
+
+@media (min-width: 1536px) {
+}
\ No newline at end of file
diff --git a/spacebook/content/images/huetiful-logo.png b/spacebook/content/images/huetiful-logo.png
new file mode 100644
index 00000000..8348f68d
Binary files /dev/null and b/spacebook/content/images/huetiful-logo.png differ
diff --git a/spacebook/content/pages/colors.md b/spacebook/content/pages/colors.md
new file mode 100644
index 00000000..a08b819e
--- /dev/null
+++ b/spacebook/content/pages/colors.md
@@ -0,0 +1,278 @@
+---
+title: Colors
+eleventyNavigation:
+ key: Colors
+ order: 1
+ title: Colors
+---
+
+# Module:📦 colors
+
+## Table of contents📜
+
+### Classes🗃
+
+- [Color](../classes/colors.Color.md)
+- [ColorArray](../classes/colors.ColorArray.md)
+
+### Functions🧰
+
+- [color](colors.md#color)
+- [colors](colors.md#colors)
+- [diverging](colors.md#diverging)
+- [load](colors.md#load)
+- [qualitative](colors.md#qualitative)
+- [sequential](colors.md#sequential)
+- [tailwindColors](colors.md#tailwindColors)
+
+## Functions
+
+### color
+
+▸ **color**(`color`): [`Color`](../classes/colors.Color.md)
+
+Wrapper function over the Color class that returns a new Color method chain.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `color` | [`ColorToken`](types.md#ColorToken) | The color token to bind. |
+
+#### Returns🔙
+
+[`Color`](../classes/colors.Color.md)
+
+A new Color class with all the utilities that take a single color as the first parameter bound to its prototype.
+
+___
+
+### colors
+
+▸ **colors**(`shade`, `value?`): [`ColorToken`](types.md#ColorToken) \| [`ColorToken`](types.md#ColorToken)[]
+
+A wrapper function for the default Tailwind palette. If called with both parameters it return the hex code at the specified shade and value. Else, if called with the shade parameter as "all" it will return all colors from the shades in the palette map at the specified value (if value is undefined it will default to "500"). When called with the shade parameter only it will return all the colors from 100 to 900 of the specified shade.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `shade` | [`TailwindColorFamilies`](types.md#TailwindColorFamilies) \| ``"all"`` | Any shade in the default TailwindCSS palette e.g amber,blue. |
+| `value?` | [`ScaleValues`](types.md#ScaleValues) | Any value from 100 to 900 in increments of 100 e.g "200". |
+
+#### Returns🔙
+
+[`ColorToken`](types.md#ColorToken) \| [`ColorToken`](types.md#ColorToken)[]
+
+color Returns a hex code string or array of hex codes depending on how the function is called.
+
+**`Example`** 📋
+
+```ts
+import { colors } from "huetiful-js";
+
+let all300 = colors("all", 300);
+
+console.log(all300)
+//[
+ '#cbd5e1', '#d1d5db', '#d4d4d8',
+ '#d4d4d4', '#d6d3d1', '#fca5a5',
+ '#fdba74', '#fcd34d', '#fde047',
+ '#bef264', '#86efac', '#6ee7b7',
+ '#5eead4', '#7dd3fc', '#93c5fd',
+ '#c4b5fd', '#d8b4fe', '#f0abfc',
+ '#f9a8d4', '#fda4af'
+]
+
+let red = colors("red");
+console.log(red);
+
+// [
+ '#fef2f2', '#fee2e2',
+ '#fecaca', '#fca5a5',
+ '#f87171', '#ef4444',
+ '#dc2626', '#b91c1c',
+ '#991b1b', '#7f1d1d'
+]
+
+let red100 = colors("red", 100);
+
+console.log(red100)
+// #fee2e2
+```
+
+___
+
+### diverging
+
+▸ **diverging**(`scheme`): [`ColorToken`](types.md#ColorToken)[]
+
+A wrapper function for ColorBrewer's map of diverging color schemes.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `scheme` | [`DivergingScheme`](types.md#DivergingScheme) | The name of the scheme. |
+
+#### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)[]
+
+An array of colors in hex represantation.
+
+**`Example`** 📋
+
+```ts
+import { diverging } from 'huetiful-js'
+
+console.log(diverging("Spectral"))
+//[
+ '#7fc97f', '#beaed4',
+ '#fdc086', '#ffff99',
+ '#386cb0', '#f0027f',
+ '#bf5b17', '#666666'
+]
+```
+
+___
+
+### load
+
+▸ **load**(`colors`): [`ColorArray`](../classes/colors.ColorArray.md)
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `colors` | [`ColorToken`](types.md#ColorToken)[] | An array of colors to chain the array methods on. Every element in the array will be parsed as a color token. |
+
+#### Returns🔙
+
+[`ColorArray`](../classes/colors.ColorArray.md)
+
+___
+
+### qualitative
+
+▸ **qualitative**(`scheme`): [`ColorToken`](types.md#ColorToken)[]
+
+A wrapper function for ColorBrewer's map of qualitative color schemes.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `scheme` | [`QualitativeScheme`](types.md#QualitativeScheme) | The name of the scheme |
+
+#### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)[]
+
+An array of colors in hex represantation.
+
+**`Example`** 📋
+
+```ts
+import { qualitative } from 'huetiful-js'
+
+console.log(qualitative("Accent"))
+// [
+ '#7fc97f', '#beaed4',
+ '#fdc086', '#ffff99',
+ '#386cb0', '#f0027f',
+ '#bf5b17', '#666666'
+]
+```
+
+___
+
+### sequential
+
+▸ **sequential**(`scheme`): [`ColorToken`](types.md#ColorToken)[]
+
+A wrapper function for ColorBrewer's map of sequential color schemes.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `scheme` | [`SequentialScheme`](types.md#SequentialScheme) | The name of the scheme |
+
+#### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)[]
+
+An array of colors in hex represantation.
+
+**`Example`** 📋
+
+```ts
+import { sequential } from 'huetiful-js
+
+console.log(sequential("OrRd"))
+
+// [
+ '#fff7ec', '#fee8c8',
+ '#fdd49e', '#fdbb84',
+ '#fc8d59', '#ef6548',
+ '#d7301f', '#b30000',
+ '#7f0000'
+]
+```
+
+___
+
+### tailwindColors
+
+▸ **tailwindColors**(`shade`): (`val?`: [`ScaleValues`](types.md#ScaleValues)) => `string` \| `string`[]
+
+Wrapper function that returns TailwindCSS color value(s) of the specified shade. If invoked with no parameters it returns an array of colors from 100 to 900. If invoked with parameter will return the specified shade vale,
+
+#### Parameters🧮
+
+| Name | Type |
+| :------ | :------ |
+| `shade` | `number` \| typeof `iterator` \| ``"length"`` \| ``"toString"`` \| ``"concat"`` \| ``"slice"`` \| ``"indexOf"`` \| ``"lastIndexOf"`` \| ``"charAt"`` \| ``"charCodeAt"`` \| ``"localeCompare"`` \| ``"match"`` \| ``"replace"`` \| ``"search"`` \| ``"split"`` \| ``"substring"`` \| ``"toLowerCase"`` \| ``"toLocaleLowerCase"`` \| ``"toUpperCase"`` \| ``"toLocaleUpperCase"`` \| ``"trim"`` \| ``"substr"`` \| ``"codePointAt"`` \| ``"includes"`` \| ``"endsWith"`` \| ``"normalize"`` \| ``"repeat"`` \| ``"startsWith"`` \| ``"anchor"`` \| ``"big"`` \| ``"blink"`` \| ``"bold"`` \| ``"fixed"`` \| ``"fontcolor"`` \| ``"fontsize"`` \| ``"italics"`` \| ``"link"`` \| ``"small"`` \| ``"strike"`` \| ``"sub"`` \| ``"sup"`` \| ``"valueOf"`` |
+
+#### Returns🔙
+
+`fn`
+
+color A hex string value or array of hex strings.
+
+▸ (`val?`): `string` \| `string`[]
+
+##### Parameters🧮
+
+| Name | Type |
+| :------ | :------ |
+| `val?` | [`ScaleValues`](types.md#ScaleValues) |
+
+##### Returns🔙
+
+`string` \| `string`[]
+
+**`Example`** 📋
+
+```ts
+import { tailwindColors } from "huetiful-js";
+
+// We pass in red as the target hue.
+// It returns a function that can be called with an optional value parameter
+let red = tailwindColors("red");
+console.log(red());
+// [
+ '#fef2f2', '#fee2e2',
+ '#fecaca', '#fca5a5',
+ '#f87171', '#ef4444',
+ '#dc2626', '#b91c1c',
+ '#991b1b', '#7f1d1d'
+]
+
+console.log(red(100));
+// '#fee2e2'
+
+console.log(red('900'));
+// '#7f1d1d'
+```
diff --git a/spacebook/content/pages/converters.md b/spacebook/content/pages/converters.md
new file mode 100644
index 00000000..f3be38cd
--- /dev/null
+++ b/spacebook/content/pages/converters.md
@@ -0,0 +1,196 @@
+---
+title: Converter functions
+date: Last Modified
+eleventyNavigation:
+ order: 3
+ title: Converter functions
+---
+
+# Module:📦 converters
+
+## Table of contents📜
+
+### Functions🧰
+
+- [num2rgb](converters.md#num2rgb)
+- [rgb2num](converters.md#rgb2num)
+- [temp2Color](converters.md#temp2Color)
+- [toColorTuple](converters.md#toColorTuple)
+- [toHex](converters.md#toHex)
+- [ucsConverter](converters.md#ucsConverter)
+
+## Functions
+
+### num2rgb
+
+▸ **num2rgb**(`num`, `hex?`): [`ColorToken`](types.md#ColorToken)
+
+Returns the RGB color equivalent of any number between 0 and 16,777,215.
+
+#### Parameters🧮
+
+| Name | Type | Default value | Description |
+| :------ | :------ | :------ | :------ |
+| `num` | `number` | `undefined` | The number to convert to RGB |
+| `hex` | `boolean` | `false` | - |
+
+#### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)
+
+color An RGB color object or hex string.
+
+**`Example`** 📋
+
+```ts
+import { num2rgb } from 'huetiful-js'
+
+console.log(num2rgb(900, true))
+// #000384
+```
+
+___
+
+### rgb2num
+
+▸ **rgb2num**(`color`): `number`
+
+Returns the numerical equivalent of a color.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `color` | [`ColorToken`](types.md#ColorToken) | The color to convert to its numerical equivalent. |
+
+#### Returns🔙
+
+`number`
+
+value The numerical value of the color from 0 to 16,777,215.
+
+**`Example`** 📋
+
+```ts
+import { rgb2num } from 'huetiful-js'
+
+console.log(rgb2num("b2c3f1"))
+// 11715569
+```
+
+___
+
+### temp2Color
+
+▸ **temp2Color**(`kelvin`, `hex?`): [`ColorToken`](types.md#ColorToken)
+
+Converts the temperature value (in Kelvins) to an RGB color.
+
+#### Parameters🧮
+
+| Name | Type | Default value | Description |
+| :------ | :------ | :------ | :------ |
+| `kelvin` | `number` | `undefined` | The number of Kelvins. From 0 to 30,000 . |
+| `hex` | `boolean` | `false` | Optional boolean parameter to either return an RGB color object or hexadecimal string. Default is true. |
+
+#### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)
+
+color The color as a hexadecimal or RGB color object.
+
+**`Example`** 📋
+
+```ts
+import { temp2Color } from 'huetiful-js'
+
+console.log(temp2Color(2542))
+// #ffa44a
+```
+
+___
+
+### toColorTuple
+
+▸ **toColorTuple**(`color`, `mode`): `any`[]
+
+Returns an array of channel values in the mode color space. It does not mutate the values of the passed in color token.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `color` | `string` \| `object` | Expects the color to be in hexadecimal represantation or as a plain color object. |
+| `mode` | [`Colorspaces`](types.md#Colorspaces) | The mode color space to return channel values for |
+
+#### Returns🔙
+
+`any`[]
+
+An array of channel values with the colorspace as first element and the alpha channel if its explicitly defined in the passed in color.
+
+**`Example`** 📋
+
+```ts
+let rgbColor = {
+ r: 0.4,
+ g: 0.3,
+ b: 0.7,
+ mode: "rgb",
+};
+console.log(toColorTuple(rgbColor,'rgb'));
+
+// [ 'rgb', 0.4, 0.3, 0.7 ]
+```
+
+___
+
+### toHex
+
+▸ **toHex**(`color`): `string`
+
+Converts a wide range of color tokens which are color objects, and CSS named colors (for example 'red'), numbers from 0 to 166,777,215 and arrays in the form of [string,number,number,number,numer?] the first element in the array being the mode color space and the fourth optional number element as the opacity value to hexadecimal.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `color` | [`ColorToken`](types.md#ColorToken) | The color to convert to hexadecimal. Works on color objects and CSS named colors. |
+
+#### Returns🔙
+
+`string`
+
+A hexadecimal representation of the passed in color.
+
+**`Example`** 📋
+
+```ts
+import { toHex } from "huetiful-js";
+
+console.log(toHex({ l: 50, c: 31, h: 100, alpha: 0.5, mode: "lch" }))
+// #7b794180
+
+console.log(toHex({ l: 50, c: 31, h: 100, mode: "lch" }))
+// #7b7941
+```
+
+___
+
+### ucsConverter
+
+▸ **ucsConverter**(`colorspace`): `ConvertFn`\<`any`\>
+
+Converter function with mode definitions for uniform color spaces. The function is curried to return a converter in the passed colospace.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `colorspace` | [`UniformColorSpaces`](types.md#UniformColorSpaces) | The mode converter to return. |
+
+#### Returns🔙
+
+`ConvertFn`\<`any`\>
+
+The converter function in the mode colorspace.
diff --git a/spacebook/content/pages/filterBy.md b/spacebook/content/pages/filterBy.md
new file mode 100644
index 00000000..a3f745ab
--- /dev/null
+++ b/spacebook/content/pages/filterBy.md
@@ -0,0 +1,290 @@
+---
+title: Filtering functions
+eleventyNavigation:
+ order: 2
+ title: Filtering functions
+---
+
+# Module:📦 filterBy
+
+## Table of contents📜
+
+### Functions🧰
+
+- [filterByContrast](filterBy.md#filterByContrast)
+- [filterByDistance](filterBy.md#filterByDistance)
+- [filterByHue](filterBy.md#filterByHue)
+- [filterByLightness](filterBy.md#filterByLightness)
+- [filterByLuminance](filterBy.md#filterByLuminance)
+- [filterBySaturation](filterBy.md#filterBySaturation)
+
+## Functions
+
+### filterByContrast
+
+▸ **filterByContrast**(`collection`, `against`, `startContrast?`, `endContrast?`): [`ColorToken`](types.md#ColorToken)[]
+
+Returns an array of colors with the specified contrast range. The contrast is tested against a comparison color (the 'against' param) and the specified contrast ranges.
+
+#### Parameters🧮
+
+| Name | Type | Default value | Description |
+| :------ | :------ | :------ | :------ |
+| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | `undefined` | - |
+| `against` | [`ColorToken`](types.md#ColorToken) | `undefined` | - |
+| `startContrast` | `number` | `1` | The minimum end of the contrast range. |
+| `endContrast` | `number` | `21` | The maximum end of the contrast range. |
+
+#### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)[]
+
+Array of filtered colors.
+
+**`Example`** 📋
+
+```ts
+import { filterByContrast } from 'huetiful-js'
+
+let sample = [
+ '#00ffdc',
+ '#00ff78',
+ '#00c000',
+ '#007e00',
+ '#164100',
+ '#ffff00',
+ '#310000',
+ '#3e0000',
+ '#4e0000',
+ '#600000',
+ '#720000',
+]
+
+console.log(filterByContrast(sample, 'green', '>=3'))
+// [ '#00ffdc', '#00ff78', '#ffff00', '#310000', '#3e0000', '#4e0000' ]
+```
+
+___
+
+### filterByDistance
+
+▸ **filterByDistance**(`collection`, `against`, `startDistance?`, `endDistance?`): [`ColorToken`](types.md#ColorToken)[]
+
+Returns an array of colors with the specified distance range. The distance is tested against a comparison color (the 'against' param) and the specified distance ranges. Uses the differenceHyab metric for calculating the distances.
+
+#### Parameters🧮
+
+| Name | Type | Default value | Description |
+| :------ | :------ | :------ | :------ |
+| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | `undefined` | - |
+| `against` | [`ColorToken`](types.md#ColorToken) | `undefined` | - |
+| `startDistance` | `number` | `0.05` | The minimum end of the distance range. |
+| `endDistance?` | `number` | `undefined` | The maximum end of the distance range. |
+
+#### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)[]
+
+Array of filtered colors.
+
+**`Example`** 📋
+
+```ts
+import { filterByDistance } from 'huetiful-js'
+
+let sample = [
+ "#ffff00",
+ "#00ffdc",
+ "#00ff78",
+ "#00c000",
+ "#007e00",
+ "#164100",
+ "#720000",
+ "#600000",
+]
+
+console.log(filterByDistance(sample, "yellow", 0.1))
+// [ '#ffff00' ]
+```
+
+___
+
+### filterByHue
+
+▸ **filterByHue**(`collection`, `startHue?`, `endHue?`, `colorspace?`): [`ColorToken`](types.md#ColorToken)[]
+
+Returns colors in the specified hue ranges between 0 to 360.
+
+#### Parameters🧮
+
+| Name | Type | Default value | Description |
+| :------ | :------ | :------ | :------ |
+| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | `undefined` | - |
+| `startHue` | `number` | `0` | The minimum end of the hue range. |
+| `endHue` | `number` | `360` | The maximum end of the hue range. |
+| `colorspace?` | [`HueColorSpaces`](types.md#HueColorSpaces) | `undefined` | - |
+
+#### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)[]
+
+Array of the filtered colors.
+
+**`Example`** 📋
+
+```ts
+let sample = [
+ '#00ffdc',
+ '#00ff78',
+ '#00c000',
+ '#007e00',
+ '#164100',
+ '#ffff00',
+ '#310000',
+ '#3e0000',
+ '#4e0000',
+ '#600000',
+ '#720000',
+]
+
+filterByHue(sample, 20, 80)
+
+// [ '#310000', '#3e0000', '#4e0000', '#600000', '#720000' ]
+```
+
+___
+
+### filterByLightness
+
+▸ **filterByLightness**(`collection`, `startLightness?`, `endLightness?`, `colorspace?`): [`ColorToken`](types.md#ColorToken)[]
+
+Returns an array of colors in the specified lightness range. The range is between 0 and 100.
+
+#### Parameters🧮
+
+| Name | Type | Default value | Description |
+| :------ | :------ | :------ | :------ |
+| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | `undefined` | - |
+| `startLightness` | `number` | `5` | The minimum end of the lightness range. |
+| `endLightness` | `number` | `100` | The maximum end of the lightness range. |
+| `colorspace?` | [`HueColorSpaces`](types.md#HueColorSpaces) | `undefined` | The mode colorspace to retrieve the lightness value from. The default is lch65 |
+
+#### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)[]
+
+Array of filtered colors.
+
+**`Example`** 📋
+
+```ts
+import { filterByLightness } from 'huetiful-js'
+let sample = [
+ '#00ffdc',
+ '#00ff78',
+ '#00c000',
+ '#007e00',
+ '#164100',
+ '#ffff00',
+ '#310000',
+ '#3e0000',
+ '#4e0000',
+ '#600000',
+ '#720000',
+]
+
+filterByLightness(sample, 20, 80)
+
+// [ '#00c000', '#007e00', '#164100', '#720000' ]
+```
+
+___
+
+### filterByLuminance
+
+▸ **filterByLuminance**(`collection`, `startLuminance?`, `endLuminance?`): [`ColorToken`](types.md#ColorToken)[]
+
+Returns an array of colors in the specified luminance range. The range is normalised to [0,1].
+
+#### Parameters🧮
+
+| Name | Type | Default value | Description |
+| :------ | :------ | :------ | :------ |
+| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | `undefined` | - |
+| `startLuminance` | `number` | `0.05` | The minimum end of the luminance range. |
+| `endLuminance` | `number` | `1` | The maximum end of the luminance range. |
+
+#### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)[]
+
+Array of filtered colors.
+
+**`Example`** 📋
+
+```ts
+import { filterByLuminance } from 'huetiful-js'
+let sample = [
+ '#00ffdc',
+ '#00ff78',
+ '#00c000',
+ '#007e00',
+ '#164100',
+ '#ffff00',
+ '#310000',
+ '#3e0000',
+ '#4e0000',
+ '#600000',
+ '#720000',
+]
+
+filterByLuminance(sample, 0.4, 0.9)
+
+// [ '#00ffdc', '#00ff78' ]
+```
+
+___
+
+### filterBySaturation
+
+▸ **filterBySaturation**(`collection`, `startSaturation?`, `endSaturation?`, `colorspace?`): [`ColorToken`](types.md#ColorToken)[]
+
+Returns an array of colors in the specified saturation range. The range is normalised to [0,1].
+
+#### Parameters🧮
+
+| Name | Type | Default value | Description |
+| :------ | :------ | :------ | :------ |
+| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | `undefined` | - |
+| `startSaturation` | `number` | `0.05` | The minimum end of the saturation range. |
+| `endSaturation` | `number` | `1` | The maximum end of the saturation range. |
+| `colorspace?` | [`HueColorSpaces`](types.md#HueColorSpaces) | `undefined` | The color space to fetch the saturation value from. Any color space with a chroma channel e.g 'lch' or 'hsl' will do. |
+
+#### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)[]
+
+Array of filtered colors.
+
+**`Example`** 📋
+
+```ts
+import { filterByContrast } from 'huetiful-js'
+
+let sample = [
+ '#00ffdc',
+ '#00ff78',
+ '#00c000',
+ '#007e00',
+ '#164100',
+ '#ffff00',
+ '#310000',
+ '#3e0000',
+ '#4e0000',
+ '#600000',
+ '#720000',
+]
+
+console.log(filterByContrast(sample, 'green', '>=3'))
+// [ '#00ffdc', '#00ff78', '#ffff00', '#310000', '#3e0000', '#4e0000' ]
+```
diff --git a/spacebook/content/pages/generators.md b/spacebook/content/pages/generators.md
new file mode 100644
index 00000000..41fee021
--- /dev/null
+++ b/spacebook/content/pages/generators.md
@@ -0,0 +1,292 @@
+---
+title: Generator functions
+eleventyNavigation:
+ order: 5
+ title: Generator functions
+---
+
+# Module:📦 generators
+
+## Table of contents📜
+
+### References
+
+- [ucsConverter](generators.md#ucsConverter)
+
+### Functions🧰
+
+- [discoverPalettes](generators.md#discoverPalettes)
+- [earthtone](generators.md#earthtone)
+- [hueShift](generators.md#hueShift)
+- [interpolateSpline](generators.md#interpolateSpline)
+- [interpolator](generators.md#interpolator)
+- [pairedScheme](generators.md#pairedScheme)
+- [pastel](generators.md#pastel)
+- [scheme](generators.md#scheme)
+
+## References
+
+### ucsConverter
+
+Re-exports [ucsConverter](converters.md#ucsConverter)
+
+## Functions
+
+### discoverPalettes
+
+▸ **discoverPalettes**(`colors`, `schemeType?`): [`ColorToken`](types.md#ColorToken)[] \| `object`
+
+Takes an array of colors and finds the best matches for a set of predefined palettes. The function does not work on achromatic colors, you may use isAchromatic to filter grays from your collection before passing it to the function.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `colors` | [`ColorToken`](types.md#ColorToken)[] | The array of colors to create palettes from. Preferably use 5 or more colors for better results. |
+| `schemeType?` | ``"analogous"`` \| ``"triadic"`` \| ``"tetradic"`` \| ``"complementary"`` | (Optional) The palette type you want to return. |
+
+#### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)[] \| `object`
+
+An array of colors if the scheme parameter is specified else it returns an object of all the palette types as keys and their values as an array of colors. If no colors are valid for the palette types it returns an empty array for the palette results.
+
+**`Example`** 📋
+
+```ts
+import { discoverPalettes } from 'huetiful-js'
+
+let sample = [
+ "#ffff00",
+ "#00ffdc",
+ "#00ff78",
+ "#00c000",
+ "#007e00",
+ "#164100",
+ "#720000",
+ "#600000",
+ "#4e0000",
+ "#3e0000",
+ "#310000",
+]
+
+console.log(discoverPalettes(sample, "tetradic"))
+// [ '#ffff00ff', '#00ffdcff', '#310000ff', '#720000ff' ]
+```
+
+___
+
+### earthtone
+
+▸ **earthtone**(`color`, `colorspace?`, `options?`): [`ColorToken`](types.md#ColorToken)[]
+
+Creates a scale of a spline based interpolation between an earthtone and a color.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `color` | [`ColorToken`](types.md#ColorToken) | The color to interpolate an earth tone with. * |
+| `colorspace?` | [`HueColorSpaces`](types.md#HueColorSpaces) | - |
+| `options?` | [`EarthtoneOptions`](types.md#EarthtoneOptions) | Optional overrides for customising interpolation and easing functions. |
+
+#### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)[]
+
+The array of colors resulting from the earthtone interpolation as hex codes.
+
+**`Example`** 📋
+
+```ts
+import { earthtone } from 'huetiful-js'
+
+console.log(earthtone("pink",{earthtones:'clay',iterations:5 }))
+// [ '#6a5c52ff', '#8d746aff', '#b38d86ff', '#d9a6a6ff', '#ffc0cbff' ]
+```
+
+___
+
+### hueShift
+
+▸ **hueShift**(`color`, `colorspace?`, `options?`): [`ColorToken`](types.md#ColorToken)[]
+
+Generates a palette of hue shifted colors (as a color becomes lighter, its hue shifts up and darker when its hue shifts down. ) from a single base color. Min and max lightness value determine how light or dark our colour will be at either extreme.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `color` | [`ColorToken`](types.md#ColorToken) | The color to use as the base of the hueshift. Colors are internally converted to LCH. |
+| `colorspace?` | [`UniformColorSpaces`](types.md#UniformColorSpaces) | - |
+| `options?` | [`HueShiftOptions`](types.md#HueShiftOptions) | The optional overrides object to customize per channel options like interpolation methods and channel fixups. |
+
+#### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)[]
+
+An array of colors in hex. The length of the resultant array is the number of iterations multiplied by 2 plus the base color passed or (iterations*2)+1
+
+**`Example`** 📋
+
+```ts
+import { hueShift } from "huetiful-js";
+
+let hueShiftedPalette = hueShift("#3e0000");
+
+console.log(hueShiftedPalette);
+
+// [
+ '#ffffe1', '#ffdca5',
+ '#ca9a70', '#935c40',
+ '#5c2418', '#3e0000',
+ '#310000', '#34000f',
+ '#38001e', '#3b002c',
+ '#3b0c3a'
+]
+```
+
+___
+
+### interpolateSpline
+
+▸ **interpolateSpline**(`colors`, `colorspace?`, `samples?`, `kind?`, `closed?`, `options?`): [`ColorToken`](types.md#ColorToken)[]
+
+Returns a spline based interpolator function with customizable interpolation methods (passed in as 'kind') and optional channel specific overrides.
+
+#### Parameters🧮
+
+| Name | Type | Default value | Description |
+| :------ | :------ | :------ | :------ |
+| `colors` | [`ColorToken`](types.md#ColorToken)[] | `undefined` | The array of colors to interpolate. If a color has a falsy channel for example black has an undefined hue channel some interpolation methods may return NaN affecting the final result. |
+| `colorspace?` | [`HueColorSpaces`](types.md#HueColorSpaces) | `undefined` | The colorspace to perform the color space in. Prefer uniform color spaces for better results such as Lch or Jch. |
+| `samples?` | `number` | `undefined` | - |
+| `kind?` | ``"natural"`` \| ``"monotone"`` \| ``"basis"`` | `undefined` | The type of the spline interpolation method. Default is basis. |
+| `closed` | `boolean` | `false` | Optional parameter to return the 'closed' variant of the 'kind' of interpolation method which can be useful for cyclical color scales. Default is false |
+| `options?` | [`InterpolatorOptions`](types.md#InterpolatorOptions) | `undefined` | Optional channel specific overrides. |
+
+#### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)[]
+
+A hexadecimal representation of the resultant color.
+
+___
+
+### interpolator
+
+▸ **interpolator**(`colors`, `colorspace?`, `options?`): `Interpolator`\<[`HueColorSpaces`](types.md#HueColorSpaces)\>
+
+#### Parameters🧮
+
+| Name | Type |
+| :------ | :------ |
+| `colors` | [`ColorToken`](types.md#ColorToken)[] |
+| `colorspace?` | [`HueColorSpaces`](types.md#HueColorSpaces) |
+| `options?` | `object` |
+
+#### Returns🔙
+
+`Interpolator`\<[`HueColorSpaces`](types.md#HueColorSpaces)\>
+
+___
+
+### pairedScheme
+
+▸ **pairedScheme**(`color`, `options?`): [`ColorToken`](types.md#ColorToken)[] \| [`ColorToken`](types.md#ColorToken)
+
+pairedScheme
+ Creates a scheme that consists of a base color that is incremented by a hueStep to get the final hue to pair with.The colors are interpolated via white or black.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `color` | [`ColorToken`](types.md#ColorToken) | The color to return a paired color scheme from. |
+| `options?` | [`PairedSchemeOptions`](types.md#PairedSchemeOptions) | The optional overrides object to customize per channel options like interpolation methods and channel fixups. |
+
+#### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)[] \| [`ColorToken`](types.md#ColorToken)
+
+An array containing the paired scheme.
+
+**`Example`** 📋
+
+```ts
+import { pairedScheme } from 'huetiful-js'
+
+console.log(pairedScheme("green",{hueStep:6,iterations:4,tone:'dark'}))
+// [ '#008116ff', '#006945ff', '#184b4eff', '#007606ff' ]
+```
+
+___
+
+### pastel
+
+▸ **pastel**(`color`): [`ColorToken`](types.md#ColorToken)
+
+Returns a random pastel variant of the passed in color.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `color` | [`ColorToken`](types.md#ColorToken) | The color to return a pastel variant of. |
+
+#### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)
+
+A random pastel color.
+
+**`Example`** 📋
+
+```ts
+import { pastel } from 'huetiful-js'
+
+console.log(pastel("green"))
+// #036103ff
+```
+
+___
+
+### scheme
+
+▸ **scheme**(`schemeType`): (`color`: [`ColorToken`](types.md#ColorToken), `easingFunc?`: (`t`: `number`) => `number`) => [`ColorToken`](types.md#ColorToken)[]
+
+Generates a randomised classic color scheme from a single base color.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `schemeType` | `string` | Any classic color scheme either "analogous"\|"triadic"\|"tetradic"\|"complementary"\|"splitComplementary". |
+
+#### Returns🔙
+
+`fn`
+
+An array of 8 character hex codes. Elements in the array depend on the number of sample colors in the targeted scheme.
+
+▸ (`color`, `easingFunc?`): [`ColorToken`](types.md#ColorToken)[]
+
+##### Parameters🧮
+
+| Name | Type |
+| :------ | :------ |
+| `color` | [`ColorToken`](types.md#ColorToken) |
+| `easingFunc?` | (`t`: `number`) => `number` |
+
+##### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)[]
+
+**`Example`** 📋
+
+```ts
+import { base } from 'huetiful-js'
+
+console.log(base("triadic")("#a1bd2f", true))
+// [ '#a1bd2fff', '#00caffff', '#ff78c9ff' ]
+```
diff --git a/spacebook/content/pages/helpers.md b/spacebook/content/pages/helpers.md
new file mode 100644
index 00000000..833c591f
--- /dev/null
+++ b/spacebook/content/pages/helpers.md
@@ -0,0 +1,776 @@
+---
+title: Helper functions
+eleventyNavigation:
+ order: 8
+ title: Helper functions
+---
+
+# Module:📦 helpers
+
+## Table of contents📜
+
+### Variables
+
+- [interpolatorConfig](helpers.md#interpolatorConfig)
+
+### Functions🧰
+
+- [adjustHue](helpers.md#adjustHue)
+- [channelDifference](helpers.md#channelDifference)
+- [checkArg](helpers.md#checkArg)
+- [colorObj](helpers.md#colorObj)
+- [colorObjArr](helpers.md#colorObjArr)
+- [customConcat](helpers.md#customConcat)
+- [customFindKey](helpers.md#customFindKey)
+- [customSort](helpers.md#customSort)
+- [eq](helpers.md#eq)
+- [expressionParser](helpers.md#expressionParser)
+- [filteredArr](helpers.md#filteredArr)
+- [floorCeil](helpers.md#floorCeil)
+- [getModeChannel](helpers.md#getModeChannel)
+- [gt](helpers.md#gt)
+- [gte](helpers.md#gte)
+- [inRange](helpers.md#inRange)
+- [isInteger](helpers.md#isInteger)
+- [lt](helpers.md#lt)
+- [lte](helpers.md#lte)
+- [matchChromaChannel](helpers.md#matchChromaChannel)
+- [matchComparator](helpers.md#matchComparator)
+- [matchDigits](helpers.md#matchDigits)
+- [matchLightnessChannel](helpers.md#matchLightnessChannel)
+- [max](helpers.md#max)
+- [min](helpers.md#min)
+- [normalize](helpers.md#normalize)
+- [random](helpers.md#random)
+- [sortedArr](helpers.md#sortedArr)
+
+## Variables
+
+### interpolatorConfig
+
+• `Const` **interpolatorConfig**: `Object`
+
+#### Type declaration
+
+| Name | Type |
+| :------ | :------ |
+| `chromaInterpolator` | [`Interpolator`](types.md#Interpolator) |
+| `easingFunc` | (`t`: `number`) => `number` |
+| `hueFixup` | (`arr`: `number`[]) => `number`[] |
+| `hueInterpolator` | [`Interpolator`](types.md#Interpolator) |
+| `lightnessInterpolator` | [`Interpolator`](types.md#Interpolator) |
+
+## Functions
+
+### adjustHue
+
+▸ **adjustHue**(`value`): `number`
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `value` | `number` | The hue angle to normalize. |
+
+#### Returns🔙
+
+`number`
+
+The normalized hue angle or passed in value if it was within [0,360]
+
+**`Example`** 📋
+
+```ts
+console.log(adjustHue(4));
+// 4
+
+console.log(adjustHue(444));
+// 84
+```
+
+___
+
+### channelDifference
+
+▸ **channelDifference**(`color`, `modeChannel`): (`subtrahend`: [`ColorToken`](types.md#ColorToken)) => `number`
+
+Returns the channel value difference between the passed in colors. They are both converted to the colorspace in the modeChannel parameter before values are computed.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `color` | [`ColorToken`](types.md#ColorToken) | The color to subtract values from/ |
+| `modeChannel` | `string` | The colorspace and channel string to perform the operation in. |
+
+#### Returns🔙
+
+`fn`
+
+The difference between the color channel(s)
+
+▸ (`subtrahend`): `number`
+
+##### Parameters🧮
+
+| Name | Type |
+| :------ | :------ |
+| `subtrahend` | [`ColorToken`](types.md#ColorToken) |
+
+##### Returns🔙
+
+`number`
+
+**`Example`** 📋
+
+```ts
+
+```
+
+___
+
+### checkArg
+
+▸ **checkArg**(`arg`, `def`): `unknown`
+
+Returns the first truthy value.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `arg` | `unknown` | The value to check |
+| `def` | `unknown` | The value to cast if arg is falsy |
+
+#### Returns🔙
+
+`unknown`
+
+The first truthy value
+
+___
+
+### colorObj
+
+▸ **colorObj**(`factor`, `callback`): (`color`: [`ColorToken`](types.md#ColorToken)) => \{ `color`: [`ColorToken`](types.md#ColorToken) = color }
+
+#### Parameters🧮
+
+| Name | Type |
+| :------ | :------ |
+| `factor` | [`Factor`](types.md#Factor) |
+| `callback` | `unknown` |
+
+#### Returns🔙
+
+`fn`
+
+▸ (`color`): `Object`
+
+##### Parameters🧮
+
+| Name | Type |
+| :------ | :------ |
+| `color` | [`ColorToken`](types.md#ColorToken) |
+
+##### Returns🔙
+
+`Object`
+
+| Name | Type |
+| :------ | :------ |
+| `color` | [`ColorToken`](types.md#ColorToken) |
+
+___
+
+### colorObjArr
+
+▸ **colorObjArr**(`factor`, `callback`): (`collection`: `object` \| [`ColorToken`](types.md#ColorToken)[]) => \{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: [`Factor`](types.md#Factor) }[]
+
+#### Parameters🧮
+
+| Name | Type |
+| :------ | :------ |
+| `factor` | [`Factor`](types.md#Factor) |
+| `callback` | `any` |
+
+#### Returns🔙
+
+`fn`
+
+▸ (`collection`): \{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: [`Factor`](types.md#Factor) }[]
+
+##### Parameters🧮
+
+| Name | Type |
+| :------ | :------ |
+| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] |
+
+##### Returns🔙
+
+\{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: [`Factor`](types.md#Factor) }[]
+
+___
+
+### customConcat
+
+▸ **customConcat**(`hue`): `number`[]
+
+#### Parameters🧮
+
+| Name | Type |
+| :------ | :------ |
+| `hue` | `object` |
+
+#### Returns🔙
+
+`number`[]
+
+___
+
+### customFindKey
+
+▸ **customFindKey**(`collection`, `factor`): `string`
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `collection` | `object` | The collection to inspect. |
+| `factor` | `number` | The value to compare against |
+
+#### Returns🔙
+
+`string`
+
+Returns the found element or its key, else `undefined`.
+
+___
+
+### customSort
+
+▸ **customSort**(`order`, `factor?`): (`a`: `any`, `b`: `any`) => `number`
+
+Helper function for native sorting method for arrays.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `order` | [`Order`](types.md#Order) | Either ascending or descending. |
+| `factor?` | `string` | The property to query. |
+
+#### Returns🔙
+
+`fn`
+
+A sorted array.
+
+▸ (`a`, `b`): `number`
+
+##### Parameters🧮
+
+| Name | Type |
+| :------ | :------ |
+| `a` | `any` |
+| `b` | `any` |
+
+##### Returns🔙
+
+`number`
+
+___
+
+### eq
+
+▸ **eq**(`x`, `y`): `boolean`
+
+#### Parameters🧮
+
+| Name | Type |
+| :------ | :------ |
+| `x` | `number` |
+| `y` | `number` |
+
+#### Returns🔙
+
+`boolean`
+
+___
+
+### expressionParser
+
+▸ **expressionParser**(`color`, `modeChannel`, `expression`): `number`
+
+Takes an arithmetic operator followed by a value and passes the result of the expression to the specified channel. Currently supports addition,subtraction,division and multiplication symbols only.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `color` | [`ColorToken`](types.md#ColorToken) | The color. |
+| `modeChannel` | `string` | The colorspace channel to set. |
+| `expression` | `string` | The expression assignment as a string. |
+
+#### Returns🔙
+
+`number`
+
+**`Example`** 📋
+
+```ts
+console.log(lch('blue'));
+// { mode: 'lch',l: 29.568297153444703,c: 131.2014771995311,h: 301.36428148973533}
+
+console.log(expressionParser('blue', 'lch.l', '*0.3'));
+// { mode: 'lch',l: 8.87048914603341,c: 131.2014771995311,h: 301.36428148973533 }
+```
+
+___
+
+### filteredArr
+
+▸ **filteredArr**(`factor`, `cb?`): (`collection`: `object` \| [`ColorToken`](types.md#ColorToken)[], `start`: `string` \| `number`, `end?`: `number`) => [`ColorToken`](types.md#ColorToken)[]
+
+Filters an array according to the value of a color's queried factor
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `factor` | [`Factor`](types.md#Factor) | The property to query and use as filtering criteria |
+| `cb?` | `unknown` | The function to use for comparison |
+
+#### Returns🔙
+
+`fn`
+
+The filtered array
+
+▸ (`collection`, `start`, `end?`): [`ColorToken`](types.md#ColorToken)[]
+
+##### Parameters🧮
+
+| Name | Type |
+| :------ | :------ |
+| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] |
+| `start` | `string` \| `number` |
+| `end?` | `number` |
+
+##### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)[]
+
+___
+
+### floorCeil
+
+▸ **floorCeil**(`num`): `number`
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `num` | `number` | The number to round up or down. |
+
+#### Returns🔙
+
+`number`
+
+An integer
+
+
+
+Rounds up or down a number based on the float value.
+
+**`Example`** 📋
+
+```ts
+console.log(floorCeil(1.45));
+// 1
+console.log(floorCeil(1.501));
+// 2
+```
+
+___
+
+### getModeChannel
+
+▸ **getModeChannel**(`colorspace`, `index?`): `string`
+
+Gets the clipped string of a passed in colorspace by removing non-channel characters.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `colorspace` | `string` | The colorspace to get the channel keys. |
+| `index?` | `number` | Optional index to return a single specified channel. |
+
+#### Returns🔙
+
+`string`
+
+A string.
+
+**`Example`** 📋
+
+```ts
+console.log(getModeChannel("oklch"));
+// lch
+
+console.log(getModeChannel("okhsl", 2));
+// l
+```
+
+___
+
+### gt
+
+▸ **gt**(`x`, `y`): `boolean`
+
+#### Parameters🧮
+
+| Name | Type |
+| :------ | :------ |
+| `x` | `number` |
+| `y` | `number` |
+
+#### Returns🔙
+
+`boolean`
+
+___
+
+### gte
+
+▸ **gte**(`x`, `y`): `boolean`
+
+#### Parameters🧮
+
+| Name | Type |
+| :------ | :------ |
+| `x` | `number` |
+| `y` | `number` |
+
+#### Returns🔙
+
+`boolean`
+
+___
+
+### inRange
+
+▸ **inRange**(`number`, `start`, `end?`): `boolean`
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `number` | `number` | The number to check. |
+| `start` | `number` | The minimum or starting value. |
+| `end?` | `number` | The maximum or starting value. |
+
+#### Returns🔙
+
+`boolean`
+
+True if the number is in range else false.
+
+
+
+Checks if a value is within the start and end range.
+
+___
+
+### isInteger
+
+▸ **isInteger**(`num`): `boolean`
+
+Checks if a number is an integer or float.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `num` | `string` \| `number` | The number to query |
+
+#### Returns🔙
+
+`boolean`
+
+True if the number is an integer else false if it is a float.
+
+___
+
+### lt
+
+▸ **lt**(`x`, `y`): `boolean`
+
+#### Parameters🧮
+
+| Name | Type |
+| :------ | :------ |
+| `x` | `number` |
+| `y` | `number` |
+
+#### Returns🔙
+
+`boolean`
+
+___
+
+### lte
+
+▸ **lte**(`x`, `y`): `boolean`
+
+#### Parameters🧮
+
+| Name | Type |
+| :------ | :------ |
+| `x` | `number` |
+| `y` | `number` |
+
+#### Returns🔙
+
+`boolean`
+
+___
+
+### matchChromaChannel
+
+▸ **matchChromaChannel**(`colorspace`): `string`
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `colorspace` | `string` | The color space to match saturation/chroma channel. |
+
+#### Returns🔙
+
+`string`
+
+The mode channel string passed to getChannel()
+
+
+
+Matches the chroma/saturation channel of any compliant color space
+
+**`Example`** 📋
+
+```ts
+import { matchChromaChannel } from 'huetiful-js'
+console.log(matchChromaChannel("jch"));
+// jch.c
+
+console.log(matchChromaChannel("okhsl"));
+// okhsl.s
+```
+
+___
+
+### matchComparator
+
+▸ **matchComparator**(`s`): `string`
+
+Matches the comparison symbols used in the expression string.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `s` | `string` | The string to match. |
+
+#### Returns🔙
+
+`string`
+
+The matched comparator, if any, as a string.
+
+___
+
+### matchDigits
+
+▸ **matchDigits**(`s`): `string`
+
+Gets the digits in the expression string
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `s` | `string` | Thestring to match |
+
+#### Returns🔙
+
+`string`
+
+The matched digits, if any, as a string.
+
+___
+
+### matchLightnessChannel
+
+▸ **matchLightnessChannel**(`colorspace`): `string`
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `colorspace` | `string` | The color space to match lightness channel. |
+
+#### Returns🔙
+
+`string`
+
+The mode channel string passed to getChannel
+
+
+
+Matches the lightness channel of any compliant color space
+
+**`Example`** 📋
+
+```ts
+console.log(matchLightnessChannel("jch"));
+// jch.j
+
+console.log(matchLightnessChannel("okhsl"));
+// okhsl.l
+```
+
+___
+
+### max
+
+▸ **max**(`array`): `number`
+
+Gets the largest value in an array
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `array` | `number`[] | The array to retrieve maximum value |
+
+#### Returns🔙
+
+`number`
+
+The largest number in the array
+
+**`Example`** 📋
+
+```ts
+console.log(max([0, 3, 4]));
+// 4
+```
+
+___
+
+### min
+
+▸ **min**(`array`): `number`
+
+Gets the smallest value in an array
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `array` | `number`[] | The array to retrieve minimum value |
+
+#### Returns🔙
+
+`number`
+
+The smallest number in the array
+
+**`Example`** 📋
+
+```ts
+console.log(min([0, 3, 4]));
+// 0
+```
+
+___
+
+### normalize
+
+▸ **normalize**(`value`, `modeChannel`): `number`
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `value` | `number` | The value to chec if its in the accepted range for the passed in mode channel |
+| `modeChannel` | `string` | A string defining the mode and channel ranges to use for comparison |
+
+#### Returns🔙
+
+`number`
+
+The normalized channel value or the passed in value if it was within range
+
+
+
+Normalizes passed in channel value to a range accepted by color spaces as defined in Culori.
+
+___
+
+### random
+
+▸ **random**(`min`, `max`): `number`
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `min` | `number` | The lower bound. |
+| `max` | `number` | The upper bound. |
+
+#### Returns🔙
+
+`number`
+
+A number.
+
+
+
+Returns a random number between minimum and maximum bounds.
+
+___
+
+### sortedArr
+
+▸ **sortedArr**(`factor`, `callback`, `order`, `colorObj?`): (`collection`: `object` \| [`ColorToken`](types.md#ColorToken)[]) => `any`[]
+
+Filters an array of color objects with a "factor" property whose value is determined by a predicate or getter via the cb param.
+
+#### Parameters🧮
+
+| Name | Type | Default value | Description |
+| :------ | :------ | :------ | :------ |
+| `factor` | [`Factor`](types.md#Factor) | `undefined` | The property to query |
+| `callback` | `unknown` | `undefined` | The function to use for comparison. |
+| `order` | [`Order`](types.md#Order) | `undefined` | - |
+| `colorObj` | `boolean` | `false` | - |
+
+#### Returns🔙
+
+`fn`
+
+An array of colors or color objects.
+
+▸ (`collection`): `any`[]
+
+##### Parameters🧮
+
+| Name | Type |
+| :------ | :------ |
+| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] |
+
+##### Returns🔙
+
+`any`[]
diff --git a/spacebook/content/pages/index.md b/spacebook/content/pages/index.md
new file mode 100644
index 00000000..ace60b2f
--- /dev/null
+++ b/spacebook/content/pages/index.md
@@ -0,0 +1,93 @@
+---
+title: Home
+permalink: /
+layout: layouts/page.njk
+eleventyNavigation:
+ key: Home
+ order: 0
+---
+
+![Huetiful](/content/images/huetiful-logo.png)
+
+Open source TypeScript library for general purpose color manipulations and generating custom color scales based on Culori.
+
+## Getting started⛳
+
+### Node
+
+The library🧾 is on npm as a package📦 for use in NodeJS:
+
+```bash
+npm i huetiful-js
+```
+
+You can use a CDN in this example, jsdelivr to load the library remotely:
+
+```js
+import {...} from 'https://cdn.jsdelivr.net/npm/huetiful-js/lib/huetiful.esm.min.mjs'
+
+```
+
+### Browser
+
+Or load the library as a UMD glabal (`huetiful`) in your HTML file using a `
+```
+
+## Modules
+
+Each module has a corresponding Typescript source file [in the repository](https://github.com/prjctimg/huetiful).
+
+- [colors](/colors.html)
+- [converters](/converters.html)
+- [filterBy](/filterBy.html)
+- [generators](/generators.html)
+- [helpers](/helpers.html)
+- [sortBy](/sortBy.html)
+- [types](/types.html)
+- [utils](/utils.html)
+
+## Notes and references
+
+- [Quickstart](/quickstart.html)
+- [About generator functions](/about-generators.html)
+- [About filtering functions](/about-filtering-functions)
+- [The attributes of a color: An introduction](about-color.html)
+- [About colorspaces](/about-colorspaces.html)
+- [About common types used](/about-types.html)
+- [About utilities](/about-utils.html)
+
+## What's next🤷🏽♂️
+
+> The possibilities are limited by the imagination🤯 of the user._
+> ~ me :smile:
+
+[See the full docs here📜](https:prjctimg.github.io/huetiful)
+
+## Need help😣 ?
+
+See some unexpected results😖? [Check the issue tracker](https://github.com/prjctimg/huetiful/issues) to open an issue or search for the problem to see if your issue already exists or has been resolved.
+
+Would like to join the chat🗣️ and share ideas💡 and suggestions💭 ? [See the discussions and just say hi, or share a coding meme(whatever breaks the ice🏔️)](https://github.com/prjctimg/huetiful/discussions)
+
+## Contributing👐🏾
+
+This project is fully open source so contributions are welcome! Help make this project better by suggesting improvements or features and patching bugs🐛. See🔍 the [CONTRIBUTING](./CONTRIBUTING.md) file for more information on how to get started.
+
+## Donating
+
+This project is completely free and will remain so forever. But if you wish to support the development of this project or just buy the developer a coffee, please feel free.
+
+## References🔗
+
+[Coloring with code: A programmatic approach by George Francis](https://tympanus.net/codrops/2021/12/07/coloring-with-code-a-programmatic-approach-to-design/)
+
+> ###### License
+>
+> Copyright (c) 2023,
+> Dean Tarisai and contributors
+> huetiful-js is released under the [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0) license.
diff --git a/spacebook/content/pages/pages.json b/spacebook/content/pages/pages.json
new file mode 100644
index 00000000..c93d256b
--- /dev/null
+++ b/spacebook/content/pages/pages.json
@@ -0,0 +1,5 @@
+{
+ "layout": "/layouts/page.njk",
+ "permalink": "/{{title | slug}}",
+ "date": "Last Modified"
+}
diff --git a/spacebook/content/pages/sortBy.md b/spacebook/content/pages/sortBy.md
new file mode 100644
index 00000000..2316e0d8
--- /dev/null
+++ b/spacebook/content/pages/sortBy.md
@@ -0,0 +1,353 @@
+---
+title: Sorting functions
+eleventyNavigation:
+ order: 6
+ title: Sorting functions
+---
+
+# Module:📦 sortBy
+
+## Table of contents📜
+
+### Functions🧰
+
+- [sortByContrast](sortBy.md#sortByContrast)
+- [sortByDistance](sortBy.md#sortByDistance)
+- [sortByHue](sortBy.md#sortByHue)
+- [sortByLightness](sortBy.md#sortByLightness)
+- [sortByLuminance](sortBy.md#sortByLuminance)
+- [sortBySaturation](sortBy.md#sortBySaturation)
+
+## Functions
+
+### sortByContrast
+
+▸ **sortByContrast**(`collection`, `against`, `order?`): [`ColorToken`](types.md#ColorToken)[]
+
+Sorts colors according to their contrast value as defined by WCAG. The contrast is tested against a comparison color (the 'against' param)
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | - |
+| `against` | [`ColorToken`](types.md#ColorToken) | - |
+| `order?` | [`Order`](types.md#Order) | The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') |
+
+#### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)[]
+
+An array of the sorted color values.
+
+**`Example`** 📋
+
+```ts
+import { sortByContrast } from 'huetiful-js'
+
+let sample = ['purple', 'green', 'red', 'brown']
+console.log(sortByContrast(sample, 'yellow'))
+// [ 'red', 'green', 'brown', 'purple' ]
+
+console.log(sortByContrast(sample, 'yellow', 'desc'))
+// [ 'purple', 'brown', 'green', 'red' ]
+```
+
+___
+
+### sortByDistance
+
+▸ **sortByDistance**(`collection`, `against`, `order?`, `options?`): [`ColorToken`](types.md#ColorToken)[]
+
+Sorts colors according to their Euclidean distance. The distance factor is determined by the color space used (some color spaces are not symmetrical meaning that the distance between colorA and colorB is not equal to the distance between colorB and colorA ). The distance is computed from against a color which is used for comparison for all the colors in the array i.e it sorts the colors against the dist
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | - |
+| `against` | [`ColorToken`](types.md#ColorToken) | The color to compare the distance with. All the distances are calculated between this color and the ones in the colors array. |
+| `order?` | ``"asc"`` \| ``"desc"`` | The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') |
+| `options?` | [`ColorDistanceOptions`](types.md#ColorDistanceOptions) | - |
+
+#### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)[]
+
+An array of the sorted color values.
+
+**`Example`** 📋
+
+```ts
+import { sortByDistance } from 'huetiful-js'
+
+let sample = ['purple', 'green', 'red', 'brown']
+console.log(
+ sortByDistance(sample, 'yellow', 'asc', {
+ mode: 'lch',
+ })
+)
+
+// [ 'brown', 'red', 'green', 'purple' ]
+
+let sample = ['purple', 'green', 'red', 'brown']
+console.log(
+ sortByDistance(sample, 'yellow', 'asc', {
+ mode: 'lch',
+ })
+)
+
+// [ 'green', 'brown', 'red', 'purple' ]
+```
+
+___
+
+### sortByHue
+
+▸ **sortByHue**(`collection`, `order?`, `colorspace?`): [`ColorToken`](types.md#ColorToken)[]
+
+Sorts colors according to hue values. It works with any color space with a hue channel. Note that hue values between HSL and Lch do not align. Achromatic colors are not supported
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | - |
+| `order?` | [`Order`](types.md#Order) | The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') |
+| `colorspace?` | [`HueColorSpaces`](types.md#HueColorSpaces) | The color space to compute the color distances in. All colors within the collection will be converted to mode. Also note that because differences in hue mapping certain color spaces such as HSL and LCH hue values do not align. Keep such quirks in mind to avoid weird results. |
+
+#### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)[]
+
+An array of the sorted color values.
+
+**`Example`** 📋
+
+```ts
+let sample = [
+ "#00ffdc",
+ "#00ff78",
+ "#00c000",
+ "#007e00",
+ "#164100",
+ "#ffff00",
+ "#310000",
+ "#3e0000",
+ "#4e0000",
+ "#600000",
+ "#720000",
+];
+
+let sorted = sortByHue(sample);
+console.log(sorted)
+// [
+ '#310000', '#3e0000',
+ '#4e0000', '#600000',
+ '#720000', '#ffff00',
+ '#164100', '#00c000',
+ '#007e00', '#00ff78',
+ '#00ffdc'
+]
+
+let sortedDescending = sortByHue(sample,'desc');
+console.log(sortedDescending)
+// [
+ '#00ffdc', '#00ff78',
+ '#007e00', '#00c000',
+ '#164100', '#ffff00',
+ '#720000', '#600000',
+ '#4e0000', '#3e0000',
+ '#310000'
+]
+```
+
+___
+
+### sortByLightness
+
+▸ **sortByLightness**(`collection`, `order?`, `colorspace?`): [`ColorToken`](types.md#ColorToken)[]
+
+Sorts colors according to their lightness.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | - |
+| `order?` | [`Order`](types.md#Order) | The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') |
+| `colorspace?` | [`HueColorSpaces`](types.md#HueColorSpaces) | The mode colorspace to use for filtering color lightness. Defaut is lch65 |
+
+#### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)[]
+
+An array of the sorted color values.
+
+**`Example`** 📋
+
+```ts
+import { sortByLightness } from "huetiful-js";
+
+let sample = [
+ "#00ffdc",
+ "#00ff78",
+ "#00c000",
+ "#007e00",
+ "#164100",
+ "#ffff00",
+ "#310000",
+ "#3e0000",
+ "#4e0000",
+ "#600000",
+ "#720000",
+]
+
+sortByLightness(sample)
+
+// [
+ '#310000', '#3e0000',
+ '#4e0000', '#600000',
+ '#720000', '#164100',
+ '#007e00', '#00c000',
+ '#00ff78', '#00ffdc',
+ '#ffff00'
+]
+
+sortByLightness(sample,'desc')
+
+// [
+ '#ffff00', '#00ffdc',
+ '#00ff78', '#00c000',
+ '#007e00', '#164100',
+ '#720000', '#600000',
+ '#4e0000', '#3e0000',
+ '#310000'
+]
+```
+
+___
+
+### sortByLuminance
+
+▸ **sortByLuminance**(`collection`, `order`): [`ColorToken`](types.md#ColorToken)[]
+
+Sorts colors according to the relative brightness as defined by WCAG definition.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | - |
+| `order` | ``"asc"`` \| ``"desc"`` | The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') |
+
+#### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)[]
+
+An array of the sorted color values.
+
+**`Example`** 📋
+
+```ts
+import { sortByLuminance } from "huetiful-js";
+let sample = [
+ "#00ffdc",
+ "#00ff78",
+ "#00c000",
+ "#007e00",
+ "#164100",
+ "#ffff00",
+ "#310000",
+ "#3e0000",
+ "#4e0000",
+ "#600000",
+ "#720000",
+];
+
+let sorted = sortByLuminance(sample)
+console.log(sorted)
+// [
+ '#310000', '#3e0000',
+ '#4e0000', '#600000',
+ '#720000', '#164100',
+ '#007e00', '#00c000',
+ '#00ff78', '#00ffdc',
+ '#ffff00'
+]
+
+let sortedDescending = sortByLuminance(sample, "desc");
+console.log(sortedDescending)
+// [
+ '#ffff00', '#00ffdc',
+ '#00ff78', '#00c000',
+ '#007e00', '#164100',
+ '#720000', '#600000',
+ '#4e0000', '#3e0000',
+ '#310000'
+]
+```
+
+___
+
+### sortBySaturation
+
+▸ **sortBySaturation**(`collection`, `order`, `mode?`): [`ColorToken`](types.md#ColorToken)[]
+
+Sorts colors according to their saturation.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | - |
+| `order` | ``"asc"`` \| ``"desc"`` | The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') |
+| `mode?` | [`HueColorSpaces`](types.md#HueColorSpaces) | The mode color space to compute the saturation value in. The default is jch . |
+
+#### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)[]
+
+An array of the sorted color values.
+
+**`Example`** 📋
+
+```ts
+import { sortBySaturation } from "huetiful-js";
+let sample = [
+ "#00ffdc",
+ "#00ff78",
+ "#00c000",
+ "#007e00",
+ "#164100",
+ "#ffff00",
+ "#310000",
+ "#3e0000",
+ "#4e0000",
+ "#600000",
+ "#720000",
+];
+
+let sorted = sortBySaturation(sample);
+console.log(sorted);
+
+// [
+ '#310000', '#3e0000',
+ '#164100', '#4e0000',
+ '#600000', '#720000',
+ '#00ffdc', '#007e00',
+ '#00ff78', '#00c000',
+ '#ffff00'
+]
+
+let sortedDescending = sortBySaturation(sample,'desc');
+console.log(sortedDescending)
+// [
+ '#ffff00', '#00c000',
+ '#00ff78', '#007e00',
+ '#00ffdc', '#720000',
+ '#600000', '#4e0000',
+ '#164100', '#3e0000',
+ '#310000'
+]
+```
diff --git a/spacebook/content/pages/types.md b/spacebook/content/pages/types.md
new file mode 100644
index 00000000..a8804463
--- /dev/null
+++ b/spacebook/content/pages/types.md
@@ -0,0 +1,324 @@
+---
+title: Types
+eleventyNavigation:
+ order: 7
+ title: Types
+---
+
+# Module:📦 types
+
+## Table of contents📜
+
+### Type Aliases
+
+- [AdaptivePaletteOptions](types.md#AdaptivePaletteOptions)
+- [ColorDistanceOptions](types.md#ColorDistanceOptions)
+- [ColorObject](types.md#ColorObject)
+- [ColorOptions](types.md#ColorOptions)
+- [ColorToken](types.md#ColorToken)
+- [ColorTuple](types.md#ColorTuple)
+- [Colorspaces](types.md#Colorspaces)
+- [DeficiencyType](types.md#DeficiencyType)
+- [DivergingScheme](types.md#DivergingScheme)
+- [EarthtoneOptions](types.md#EarthtoneOptions)
+- [Factor](types.md#Factor)
+- [FactorMapper](types.md#FactorMapper)
+- [HueColorSpaces](types.md#HueColorSpaces)
+- [HueFamily](types.md#HueFamily)
+- [HueShiftOptions](types.md#HueShiftOptions)
+- [Interpolator](types.md#Interpolator)
+- [InterpolatorOptions](types.md#InterpolatorOptions)
+- [Options](types.md#Options)
+- [Order](types.md#Order)
+- [PairedSchemeOptions](types.md#PairedSchemeOptions)
+- [QualitativeScheme](types.md#QualitativeScheme)
+- [ScaleValues](types.md#ScaleValues)
+- [SequentialScheme](types.md#SequentialScheme)
+- [TailwindColorFamilies](types.md#TailwindColorFamilies)
+- [Tone](types.md#Tone)
+- [UniformColorSpaces](types.md#UniformColorSpaces)
+- [callback](types.md#callback)
+
+## Type Aliases
+
+### AdaptivePaletteOptions
+
+Ƭ **AdaptivePaletteOptions**: `Object`
+
+**`Description`** ℹ
+
+This object returns the lightMode and darkMode optimized version of a color with support to add color vision deficiency simulation to the final color result.
+
+#### Type declaration
+
+| Name | Type |
+| :------ | :------ |
+| `backgroundColor?` | \{ `dark?`: [`ColorToken`](types.md#ColorToken) ; `light?`: [`ColorToken`](types.md#ColorToken) } |
+| `backgroundColor.dark?` | [`ColorToken`](types.md#ColorToken) |
+| `backgroundColor.light?` | [`ColorToken`](types.md#ColorToken) |
+| `colorBlind?` | `boolean` |
+| `viewingConditions?` | `ViewingConditions` |
+
+___
+
+### ColorDistanceOptions
+
+Ƭ **ColorDistanceOptions**: `Object`
+
+#### Type declaration
+
+| Name | Type |
+| :------ | :------ |
+| `mode?` | [`Colorspaces`](types.md#Colorspaces) |
+| `weights?` | [`number`, `number`, `number`, `number`] |
+
+___
+
+### ColorObject
+
+Ƭ **ColorObject**: `Object`
+
+#### Type declaration
+
+| Name | Type |
+| :------ | :------ |
+| `alpha?` | `number` |
+| `mode` | [`Colorspaces`](types.md#Colorspaces) |
+
+___
+
+### ColorOptions
+
+Ƭ **ColorOptions**: `Object`
+
+#### Type declaration
+
+| Name | Type |
+| :------ | :------ |
+| `alpha?` | `number` |
+| `colorSpace?` | [`HueColorSpaces`](types.md#HueColorSpaces) |
+| `colorspace?` | [`HueColorSpaces`](types.md#HueColorSpaces) |
+| `contrast?` | `number` |
+| `darkMode?` | [`ColorToken`](types.md#ColorToken) |
+| `lightMode?` | [`ColorToken`](types.md#ColorToken) |
+| `lightness?` | `number` |
+| `luminance?` | `number` |
+| `saturation?` | `number` |
+| `temperature?` | `number` |
+
+___
+
+### ColorToken
+
+Ƭ **ColorToken**: `number` \| `string` \| `object` \| [`ColorTuple`](types.md#ColorTuple)
+
+**`Description`** ℹ
+
+Any recognizable color token.
+
+___
+
+### ColorTuple
+
+Ƭ **ColorTuple**: [`string`, `number`, `number`, `number`, number?]
+
+___
+
+### Colorspaces
+
+Ƭ **Colorspaces**: ``"a98"`` \| ``"cubehelix"`` \| ``"dlab"`` \| ``"jab"`` \| ``"lab"`` \| ``"lab65"`` \| ``"lrgb"`` \| ``"luv"`` \| ``"oklab"`` \| ``"rgb"`` \| [`HueColorSpaces`](types.md#HueColorSpaces)
+
+___
+
+### DeficiencyType
+
+Ƭ **DeficiencyType**: ``"red"`` \| ``"blue"`` \| ``"green"`` \| ``"monochromacy"``
+
+___
+
+### DivergingScheme
+
+Ƭ **DivergingScheme**: ``"Spectral"`` \| ``"RdYlGn"`` \| ``"RdBu"`` \| ``"PiYG"`` \| ``"PRGn"`` \| ``"RdYlBu"`` \| ``"BrBG"`` \| ``"RdGy"`` \| ``"PuOr"``
+
+___
+
+### EarthtoneOptions
+
+Ƭ **EarthtoneOptions**: `Omit`\<[`Options`](types.md#Options), ``"hueStep"`` \| ``"via"`` \| ``"maxLightness"`` \| ``"minLightness"``\>
+
+___
+
+### Factor
+
+Ƭ **Factor**: ``"luminance"`` \| ``"temp"`` \| ``"saturation"`` \| ``"contrast"`` \| ``"distance"`` \| ``"lightness"`` \| ``"hue"``
+
+___
+
+### FactorMapper
+
+Ƭ **FactorMapper**: (`factor`: [`Factor`](types.md#Factor), `callback`: [`callback`](types.md#callback), `order?`: [`Order`](types.md#Order), `colorObj?`: `boolean`) => (`colors`: [`ColorToken`](types.md#ColorToken)[]) => [`ColorToken`](types.md#ColorToken)[]
+
+#### Type declaration
+
+▸ (`factor`, `callback`, `order?`, `colorObj?`): (`colors`: [`ColorToken`](types.md#ColorToken)[]) => [`ColorToken`](types.md#ColorToken)[]
+
+##### Parameters🧮
+
+| Name | Type |
+| :------ | :------ |
+| `factor` | [`Factor`](types.md#Factor) |
+| `callback` | [`callback`](types.md#callback) |
+| `order?` | [`Order`](types.md#Order) |
+| `colorObj?` | `boolean` |
+
+##### Returns🔙
+
+`fn`
+
+▸ (`colors`): [`ColorToken`](types.md#ColorToken)[]
+
+##### Parameters🧮
+
+| Name | Type |
+| :------ | :------ |
+| `colors` | [`ColorToken`](types.md#ColorToken)[] |
+
+##### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)[]
+
+___
+
+### HueColorSpaces
+
+Ƭ **HueColorSpaces**: [`UniformColorSpaces`](types.md#UniformColorSpaces) \| ``"hsl"`` \| ``"hsv"`` \| ``"hsi"`` \| ``"hwb"`` \| ``"okhsl"`` \| ``"okhsv"``
+
+___
+
+### HueFamily
+
+Ƭ **HueFamily**: ``"red-purple"`` \| ``"red"`` \| ``"yellow-red"`` \| ``"yellow"`` \| ``"green-yellow"`` \| ``"green"`` \| ``"blue-green"`` \| ``"blue"`` \| ``"purple-blue"`` \| ``"purple"``
+
+___
+
+### HueShiftOptions
+
+Ƭ **HueShiftOptions**: `Omit`\<[`Options`](types.md#Options), ``"via"`` \| ``"earthtones"`` \| ``""``\> & [`InterpolatorOptions`](types.md#InterpolatorOptions)
+
+___
+
+### Interpolator
+
+Ƭ **Interpolator**: (`arr`: `number`[]) => (`t`: `number`) => `number`
+
+#### Type declaration
+
+▸ (`arr`): (`t`: `number`) => `number`
+
+##### Parameters🧮
+
+| Name | Type |
+| :------ | :------ |
+| `arr` | `number`[] |
+
+##### Returns🔙
+
+`fn`
+
+▸ (`t`): `number`
+
+##### Parameters🧮
+
+| Name | Type |
+| :------ | :------ |
+| `t` | `number` |
+
+##### Returns🔙
+
+`number`
+
+___
+
+### InterpolatorOptions
+
+Ƭ **InterpolatorOptions**: `Pick`\<[`Options`](types.md#Options), ``"easingFunc"`` \| ``"hueInterpolator"`` \| ``"chromaInterpolator"`` \| ``"hueFixup"`` \| ``"lightnessInterpolator"``\>
+
+___
+
+### Options
+
+Ƭ **Options**: `Object`
+
+**`Description`** ℹ
+
+The override parameters for palette functions.
+
+#### Type declaration
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `chromaInterpolator?` | [`Interpolator`](types.md#Interpolator) | **`Param`** interpolation method to use on the chroma channel. |
+| `earthtones?` | ``"light-gray"`` \| ``"silver"`` \| ``"sand"`` \| ``"tupe"`` \| ``"mahogany"`` \| ``"brick-red"`` \| ``"clay"`` \| ``"cocoa"`` \| ``"dark-brown"`` \| ``"dark"`` | * **`Param`** The earthtone to interpolate with. |
+| `easingFunc?` | (`t`: `number`) => `number` | The easing function to use. |
+| `hueFixup?` | (`arr`: `number`[]) => `number`[] | |
+| `hueInterpolator?` | [`Interpolator`](types.md#Interpolator) | **`Param`** interpolation method to use on the hue channel. |
+| `hueStep?` | `number` | **`Param`** amount of hue angles to increment each iteration with. |
+| `lightnessInterpolator?` | [`Interpolator`](types.md#Interpolator) | **`Param`** interpolation method to use on the lightness channel. |
+| `maxLightness?` | `number` | **`Param`** Maximum lightness value (range 0-100). |
+| `minLightness?` | `number` | * **`Param`** Minimum lightness value (range 0-100). |
+| `samples?` | `number` | **`Param`** amount of samples to return in the result collection. |
+| `via?` | [`Tone`](types.md#Tone) | **`Param`** color to pass through during interpolation. |
+
+___
+
+### Order
+
+Ƭ **Order**: ``"asc"`` \| ``"desc"``
+
+___
+
+### PairedSchemeOptions
+
+Ƭ **PairedSchemeOptions**: `Omit`\<[`Options`](types.md#Options), ``"earthtones"`` \| ``"maxLightness"`` \| ``"minLightness"``\>
+
+___
+
+### QualitativeScheme
+
+Ƭ **QualitativeScheme**: ``"Set2"`` \| ``"Accent"`` \| ``"Set1"`` \| ``"Set3"`` \| ``"Dark2"`` \| ``"Paired"`` \| ``"Pastel2"`` \| ``"Pastel1"``
+
+___
+
+### ScaleValues
+
+Ƭ **ScaleValues**: ``"50"`` \| ``"100"`` \| ``"200"`` \| ``"300"`` \| ``"400"`` \| ``"500"`` \| ``"600"`` \| ``"700"`` \| ``"800"`` \| ``"900"``
+
+___
+
+### SequentialScheme
+
+Ƭ **SequentialScheme**: ``"OrRd"`` \| ``"PuBu"`` \| ``"BuPu"`` \| ``"Oranges"`` \| ``"BuGn"`` \| ``"YlOrBr"`` \| ``"YlGn"`` \| ``"Reds"`` \| ``"RdPu"`` \| ``"Greens"`` \| ``"YlGnBu"`` \| ``"Purples"`` \| ``"GnBu"`` \| ``"Greys"`` \| ``"YlOrRd"`` \| ``"PuRd"`` \| ``"Blues"`` \| ``"PuBuGn"`` \| ``"Viridis"``
+
+___
+
+### TailwindColorFamilies
+
+Ƭ **TailwindColorFamilies**: ``"indigo"`` \| ``"gray"`` \| ``"zinc"`` \| ``"neutral"`` \| ``"stone"`` \| ``"red"`` \| ``"orange"`` \| ``"amber"`` \| ``"yellow"`` \| ``"lime"`` \| ``"green"`` \| ``"emerald"`` \| ``"teal"`` \| ``"sky"`` \| ``"blue"`` \| ``"violet"`` \| ``"purple"`` \| ``"fuchsia"`` \| ``"pink"`` \| ``"rose"``
+
+___
+
+### Tone
+
+Ƭ **Tone**: ``"light"`` \| ``"dark"``
+
+___
+
+### UniformColorSpaces
+
+Ƭ **UniformColorSpaces**: ``"lch"`` \| ``"jch"`` \| ``"dlch"`` \| ``"lch"`` \| ``"lch65"`` \| ``"lchuv"`` \| ``"oklch"``
+
+___
+
+### callback
+
+Ƭ **callback**: `unknown`
diff --git a/spacebook/content/pages/utils.md b/spacebook/content/pages/utils.md
new file mode 100644
index 00000000..0b4bacd3
--- /dev/null
+++ b/spacebook/content/pages/utils.md
@@ -0,0 +1,886 @@
+---
+title: Utilities
+eleventyNavigation:
+ order: 4
+ title: Utility functions
+---
+
+
+# Module:📦 utils
+
+## Table of contents📜
+
+### Functions🧰
+
+- [alpha](utils.md#alpha)
+- [brighten](utils.md#brighten)
+- [colorDeficiency](utils.md#colorDeficiency)
+- [darken](utils.md#darken)
+- [getChannel](utils.md#getChannel)
+- [getComplimentaryHue](utils.md#getComplimentaryHue)
+- [getContrast](utils.md#getContrast)
+- [getFarthestChroma](utils.md#getFarthestChroma)
+- [getFarthestContrast](utils.md#getFarthestContrast)
+- [getFarthestHue](utils.md#getFarthestHue)
+- [getFarthestLightness](utils.md#getFarthestLightness)
+- [getHueFamily](utils.md#getHueFamily)
+- [getLuminance](utils.md#getLuminance)
+- [getNearestChroma](utils.md#getNearestChroma)
+- [getNearestColor](utils.md#getNearestColor)
+- [getNearestContrast](utils.md#getNearestContrast)
+- [getNearestHue](utils.md#getNearestHue)
+- [getNearestLightness](utils.md#getNearestLightness)
+- [isAchromatic](utils.md#isAchromatic)
+- [isCool](utils.md#isCool)
+- [isWarm](utils.md#isWarm)
+- [overtone](utils.md#overtone)
+- [setChannel](utils.md#setChannel)
+- [setLuminance](utils.md#setLuminance)
+
+## Functions
+
+### alpha
+
+▸ **alpha**(`color`, `value?`): `number`
+
+Sets the opacity of a color. Also gets the alpha value of the color if the value param is omitted
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `color` | [`ColorToken`](types.md#ColorToken) | The color with the targeted opacity/alpha channel. |
+| `value?` | `string` \| `number` | The value to apply to the opacity channel. The value is between [0,1] |
+
+#### Returns🔙
+
+`number`
+
+color The resulting color. Returns an 8 character hex code.
+
+**`Example`** 📋
+
+```ts
+// Getting the alpha
+console.log(alpha('#a1bd2f0d'))
+// 0.050980392156862744
+
+// Setting the alpha
+
+let myColor = alpha('b2c3f1', 0.5)
+
+console.log(myColor)
+
+// #b2c3f180
+```
+
+___
+
+### brighten
+
+▸ **brighten**(`color`, `value`, `colorspace`): [`ColorToken`](types.md#ColorToken)
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `color` | [`ColorToken`](types.md#ColorToken) | The color to brighten. |
+| `value` | `string` \| `number` | The amount to brighten with. Also supports expressions as strings e.g darken("#fc23a1","*0.5") |
+| `colorspace` | `any` | - |
+
+#### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)
+
+___
+
+### colorDeficiency
+
+▸ **colorDeficiency**(`deficiencyType?`): (`color`: [`ColorToken`](types.md#ColorToken), `severity`: `number`) => `string`
+
+Returns the color as a simulation of the passed in type of color vision deficiency with the deficiency filter's intensity determined by the severity value.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `deficiencyType?` | [`DeficiencyType`](types.md#DeficiencyType) | The type of color vision deficiency. To avoid writing the long types, the expected parameters are simply the colors that are hard to perceive for the type of color blindness. For example those with 'tritanopia' are unable to perceive 'blue' light. Default is 'red' when the defeciency parameter is undefined or any falsy value. |
+
+#### Returns🔙
+
+`fn`
+
+The color as its simulated variant as a hexadecimal string.
+
+▸ (`color`, `severity?`): `string`
+
+##### Parameters🧮
+
+| Name | Type | Default value |
+| :------ | :------ | :------ |
+| `color` | [`ColorToken`](types.md#ColorToken) | `undefined` |
+| `severity` | `number` | `1` |
+
+##### Returns🔙
+
+`string`
+
+**`See`**
+
+For a deep dive on color vision deficiency go to
+
+**`Example`** 📋
+
+```ts
+import { colorDeficiency, toHex } from 'huetiful-js'
+
+// Here we are simulating color blindness of tritanomaly or we can't see 'blue'.
+// We are passing in our color as an array of channel values in the mode "rgb". The severity is set to 0.1
+let tritanomaly = colorDeficiency('blue')
+console.log(tritanomaly(['rgb', 230, 100, 50, 0.5], 0.1))
+// #dd663680
+
+// Here we are simulating color blindness of tritanomaly or we can't see 'red'. The severity is not explicitly set so it defaults to 1
+let protanopia = colorDeficiency('red')
+console.log(protanopia({ h: 20, w: 50, b: 30, mode: 'hwb' }))
+// #9f9f9f
+```
+
+___
+
+### darken
+
+▸ **darken**(`color`, `value`): [`ColorToken`](types.md#ColorToken)
+
+Darkens the color by reducing the lightness channel. .
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `color` | [`ColorToken`](types.md#ColorToken) | The color to darken. |
+| `value` | `string` \| `number` | The amount to darken with. Also supports expressions as strings e.g darken("#fc23a1","*0.5") |
+
+#### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)
+
+color The darkened color.
+
+**`Example`** 📋
+
+```ts
+
+```
+
+___
+
+### getChannel
+
+▸ **getChannel**(`mc`): (`color`: [`ColorToken`](types.md#ColorToken)) => `number`
+
+Gets the value specifified channel on the color.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `mc` | `string` | The mode and channel to be retrieved. For example "rgb.b" will return the value of the blue channel in the RGB color space of that color. |
+
+#### Returns🔙
+
+`fn`
+
+value The value of the queried channel.
+
+▸ (`color`): `number`
+
+##### Parameters🧮
+
+| Name | Type |
+| :------ | :------ |
+| `color` | [`ColorToken`](types.md#ColorToken) |
+
+##### Returns🔙
+
+`number`
+
+**`Example`** 📋
+
+```ts
+import { getChannel } from 'huetiful-js'
+
+console.log(getChannel('rgb.g')('#a1bd2f'))
+// 0.7411764705882353
+```
+
+___
+
+### getComplimentaryHue
+
+▸ **getComplimentaryHue**(`color`, `colorspace?`, `colorObj?`): \{ `color`: [`ColorToken`](types.md#ColorToken) ; `hue`: `string` } \| [`ColorToken`](types.md#ColorToken)
+
+Gets the complementary hue of the passed in color. The function is internally guarded against achromatic colors.
+
+#### Parameters🧮
+
+| Name | Type | Default value | Description |
+| :------ | :------ | :------ | :------ |
+| `color` | [`ColorToken`](types.md#ColorToken) | `undefined` | The color to retrieve its complimentary hue. |
+| `colorspace?` | [`HueColorSpaces`](types.md#HueColorSpaces) | `undefined` | - |
+| `colorObj` | `boolean` | `false` | Optional boolean whether to return an object with the result color hue family or just the result color. Default is false. |
+
+#### Returns🔙
+
+\{ `color`: [`ColorToken`](types.md#ColorToken) ; `hue`: `string` } \| [`ColorToken`](types.md#ColorToken)
+
+An object with the hue family and complimentary color as keys.
+
+**`Example`** 📋
+
+```ts
+import { getComplimentaryHue } from "huetiful-js";
+
+console.log(getComplimentaryHue("pink", true))
+//// { hue: 'blue-green', color: '#97dfd7ff' }
+
+console.log(getComplimentaryHue("purple"))
+// #005700ff
+```
+
+___
+
+### getContrast
+
+▸ **getContrast**(`color`, `against`): `number`
+
+Gets the contrast between the passed in colors.
+
+#### Parameters🧮
+
+| Name | Type |
+| :------ | :------ |
+| `color` | [`ColorToken`](types.md#ColorToken) |
+| `against` | [`ColorToken`](types.md#ColorToken) |
+
+#### Returns🔙
+
+`number`
+
+The relative luminance of the lightest color.
+
+**`Example`** 📋
+
+```ts
+import { getContrast } from 'huetiful-js'
+
+console.log(getContrast("black", "white"));
+// 21
+```
+
+___
+
+### getFarthestChroma
+
+▸ **getFarthestChroma**(`collection`, `colorObj?`): `number` \| \{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: `number` }
+
+Gets the largest saturation value from the passed in colors.
+
+#### Parameters🧮
+
+| Name | Type | Default value | Description |
+| :------ | :------ | :------ | :------ |
+| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | `undefined` | - |
+| `colorObj` | `boolean` | `false` | Optional boolean that makes the function return a custom object with factor (saturation) and name of the color as keys. Default is false. |
+
+#### Returns🔙
+
+`number` \| \{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: `number` }
+
+The largest saturation value in the colors passed in or a custom object.
+
+**`Example`** 📋
+
+```ts
+import { getFarthestChroma } from 'huetiful-js'
+
+let sample = ['b2c3f1', '#a1bd2f', '#f3bac1']
+
+console.log(getFarthestChroma(sample, 'lch'))
+// 67.22120855010492
+```
+
+___
+
+### getFarthestContrast
+
+▸ **getFarthestContrast**(`collection`, `against`, `colorObj?`): `number` \| \{ `factor`: `number` ; `name`: [`ColorToken`](types.md#ColorToken) }
+
+Gets the largest contrast value from the passed in colors compared against a sample color.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | - |
+| `against` | [`ColorToken`](types.md#ColorToken) | - |
+| `colorObj?` | `boolean` | Optional boolean that makes the function return a custom object with factor (contrast) and name of the color as keys. Default is false. |
+
+#### Returns🔙
+
+`number` \| \{ `factor`: `number` ; `name`: [`ColorToken`](types.md#ColorToken) }
+
+The largest contrast value in the colors passed in or a custom object.
+
+**`Example`** 📋
+
+```ts
+import { getFarthestContrast } from 'huetiful-js'
+
+console.log(getFarthestContrast(["b2c3f1", "#a1bd2f", "#f3bac1"], "green"));
+// 3.08355493246362
+
+console.log(getFarthestContrast(["b2c3f1", "#a1bd2f", "#f3bac1"], "green", true));
+// { contrast: 3.08355493246362, name: '#f3bac1' }
+```
+
+___
+
+### getFarthestHue
+
+▸ **getFarthestHue**(`collection`, `colorspace?`, `colorObj?`): `number` \| \{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: `number` }
+
+Gets the largest hue value from the passed in colors.
+
+#### Parameters🧮
+
+| Name | Type | Default value | Description |
+| :------ | :------ | :------ | :------ |
+| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | `undefined` | - |
+| `colorspace?` | [`HueColorSpaces`](types.md#HueColorSpaces) | `undefined` | The mode color space to perform the computation in. |
+| `colorObj` | `boolean` | `false` | Optional boolean that makes the function return a custom object with factor (hue) and name of the color as keys. Default is false. |
+
+#### Returns🔙
+
+`number` \| \{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: `number` }
+
+The largest hue value in the colors passed in or a custom object.
+
+**`Example`** 📋
+
+```ts
+import { getFarthestHue } from 'huetiful-js'
+let sample = ['b2c3f1', '#a1bd2f', '#f3bac1']
+
+console.log(getFarthestHue(sample, 'lch'))
+// 273.54920266436477
+```
+
+___
+
+### getFarthestLightness
+
+▸ **getFarthestLightness**(`collection`, `colorspace?`, `colorObj?`): `number` \| \{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: `number` }
+
+Gets the largest lightness value from the passed in colors.
+
+#### Parameters🧮
+
+| Name | Type | Default value | Description |
+| :------ | :------ | :------ | :------ |
+| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | `undefined` | - |
+| `colorspace?` | [`HueColorSpaces`](types.md#HueColorSpaces) | `undefined` | THe mode colorspace to retrieve the lightness value from. |
+| `colorObj` | `boolean` | `false` | Optional boolean that makes the function return a custom object with factor (lightness) and name of the color as keys. Default is false. |
+
+#### Returns🔙
+
+`number` \| \{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: `number` }
+
+The largest lightness value in the colors passed in or a custom object.
+
+**`Example`** 📋
+
+```ts
+import { getFarthestLightness } from 'huetiful-js'
+
+let sample = ["b2c3f1", "#a1bd2f", "#f3bac1"]
+
+console.log(getFarthestLightness(sample, true))
+
+// { lightness: 80.94668903360088, name: '#f3bac1' }
+```
+
+___
+
+### getHueFamily
+
+▸ **getHueFamily**(`color`, `mode?`): [`HueFamily`](types.md#HueFamily)
+
+Gets the hue family which a a color belongs to with the overtone included (if it has one.). For achromatic colors it returns the string "gray".
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `color` | [`ColorToken`](types.md#ColorToken) | The color to query its shade or hue family. |
+| `mode?` | [`HueColorSpaces`](types.md#HueColorSpaces) | - |
+
+#### Returns🔙
+
+[`HueFamily`](types.md#HueFamily)
+
+The name of the hue family for example red or green.
+
+**`Example`** 📋
+
+```ts
+import { getHue } from 'huetiful-js'
+
+console.log(getHue("#310000"))
+// red
+```
+
+___
+
+### getLuminance
+
+▸ **getLuminance**(`color`): `number`
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `color` | [`ColorToken`](types.md#ColorToken) | The color to query. |
+
+#### Returns🔙
+
+`number`
+
+value The color's luminance value.
+
+**`Alias`**
+
+Gets the luminance value of that color as defined by WCAG.
+
+**`Example`** 📋
+
+```ts
+import { getLuminance } from 'huetiful-js'
+
+console.log(getLuminance('#a1bd2f'))
+// 0.4417749513730954
+```
+
+___
+
+### getNearestChroma
+
+▸ **getNearestChroma**(`collection`, `colorspace?`, `colorObj?`): `number` \| \{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: `number` }
+
+Gets the smallest chroma/saturation value from the passed in colors.
+
+#### Parameters🧮
+
+| Name | Type | Default value | Description |
+| :------ | :------ | :------ | :------ |
+| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | `undefined` | - |
+| `colorspace?` | [`HueColorSpaces`](types.md#HueColorSpaces) | `undefined` | The mode color space to perform the computation in. |
+| `colorObj` | `boolean` | `false` | Optional boolean that makes the function return a custom object with factor (saturation) and name of the color as keys. Default is false. |
+
+#### Returns🔙
+
+`number` \| \{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: `number` }
+
+The smallest chroma/saturation value in the colors passed in or a custom object.
+
+**`Example`** 📋
+
+```ts
+import { getNearestChroma } from 'huetiful-js'
+
+let sample = ['b2c3f1', '#a1bd2f', '#f3bac1']
+
+console.log(getNearestChroma(sample, 'lch'))
+// 22.45669293295522
+```
+
+___
+
+### getNearestColor
+
+▸ **getNearestColor**(`collection`, `color`, `num?`): [`ColorToken`](types.md#ColorToken) \| [`ColorToken`](types.md#ColorToken)[]
+
+#### Parameters🧮
+
+| Name | Type | Default value | Description |
+| :------ | :------ | :------ | :------ |
+| `collection` | [`ColorToken`](types.md#ColorToken)[] \| ``"tailwind"`` | `undefined` | The collection of colors to search for nearest colors |
+| `color` | [`ColorToken`](types.md#ColorToken) | `undefined` | The color to use for distance comparison |
+| `num` | `number` | `1` | The number of colors to return, if the value is above the colors in the available sample, the entire collection is returned with colors ordered in ascending order using the differenceHyab metric. |
+
+#### Returns🔙
+
+[`ColorToken`](types.md#ColorToken) \| [`ColorToken`](types.md#ColorToken)[]
+
+An array of colors.
+
+**`Example`** 📋
+
+```ts
+let cols = colors('all', '500')
+
+console.log(getNearestColor(cols, 'blue', 3));
+// [ '#a855f7', '#8b5cf6', '#d946ef' ]
+```
+
+___
+
+### getNearestContrast
+
+▸ **getNearestContrast**(`collection`, `against`, `colorObj?`): `any`
+
+Gets the smallest contrast value from the passed in colors compared against a sample color.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | - |
+| `against` | [`ColorToken`](types.md#ColorToken) | - |
+| `colorObj?` | `boolean` | Optional boolean that makes the function return a custom object with factor (contrast) and name of the color as keys. Default is false. |
+
+#### Returns🔙
+
+`any`
+
+The smallest contrast value in the colors passed in or a custom object.
+
+**`Example`** 📋
+
+```ts
+import { getNearestContrast } from 'huetiful-js'
+
+console.log(getNearestContrast(["b2c3f1", "#a1bd2f", "#f3bac1"], "green"));
+// 2.4061390502133424
+
+console.log(getNearestContrast(["b2c3f1", "#a1bd2f", "#f3bac1"], "green", true));
+// { contrast: 2.4061390502133424, name: '#a1bd2f' }
+```
+
+___
+
+### getNearestHue
+
+▸ **getNearestHue**(`collection`, `colorspace?`, `colorObj?`): `number` \| \{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: `number` }
+
+Gets the smallest hue value from the passed in colors.
+
+#### Parameters🧮
+
+| Name | Type | Default value | Description |
+| :------ | :------ | :------ | :------ |
+| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | `undefined` | - |
+| `colorspace?` | `string` | `undefined` | The mode color space to perform the computation in. |
+| `colorObj` | `boolean` | `false` | Optional boolean that makes the function return a custom object with factor (hue) and name of the color as keys. Default is false. |
+
+#### Returns🔙
+
+`number` \| \{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: `number` }
+
+The smallest hue value in the colors passed in or a custom object.
+
+**`Example`** 📋
+
+```ts
+import { getNearestHue } from 'huetiful-js'
+
+let sample = ['b2c3f1', '#a1bd2f', '#f3bac1']
+
+console.log(getNearestHue(sample, 'lch'))
+// 12.462831644544274
+```
+
+___
+
+### getNearestLightness
+
+▸ **getNearestLightness**(`collection`, `colorspace?`, `colorObj?`): `number` \| \{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: `number` }
+
+Gets the smallest lightness value from the passed in colors.
+
+#### Parameters🧮
+
+| Name | Type | Default value | Description |
+| :------ | :------ | :------ | :------ |
+| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | `undefined` | - |
+| `colorspace?` | [`HueColorSpaces`](types.md#HueColorSpaces) | `undefined` | - |
+| `colorObj` | `boolean` | `false` | Optional boolean that makes the function return a custom object with factor (lightness) and name of the color as keys. Default is false. |
+
+#### Returns🔙
+
+`number` \| \{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: `number` }
+
+The smallest lightness value in the colors passed in or a custom object.
+
+**`Example`** 📋
+
+```ts
+import { getNearestLightness } from 'huetiful-js'
+
+let sample = ["b2c3f1", "#a1bd2f", "#f3bac1"]
+
+console.log(getNearestLightness(sample, true))
+
+// { lightness: 72.61647882089876, name: '#a1bd2f' }
+```
+
+___
+
+### isAchromatic
+
+▸ **isAchromatic**(`color`, `mode?`): `boolean`
+
+Checks if a color is achromatic(without hue or simply grayscale).
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `color` | [`ColorToken`](types.md#ColorToken) | The color to test if it is achromatic or not. |
+| `mode?` | [`HueColorSpaces`](types.md#HueColorSpaces) | - |
+
+#### Returns🔙
+
+`boolean`
+
+boolean Returns true if the color is achromatic else false
+
+**`Example`** 📋
+
+```ts
+import { isAchromatic } from "huetiful-js";
+import { formatHex8, interpolate, samples } from "culori"
+
+isAchromatic('pink')
+// false
+
+let sample = [
+ "#164100",
+ "#ffff00",
+ "#310000",
+ 'pink'
+];
+
+console.log(map(sample, isAchromatic));
+
+// [false, false, false,false]
+
+isAchromatic('gray')
+// Returns true
+
+console.log(map(sample, isAchromatic));
+
+// we create an interpolation using black and white
+let f = interpolate(["black", "white"]);
+
+//We then create 12 evenly spaced samples and pass them to f as the `t` param required by an interpolating function.
+// Lastly we convert the color to hex for brevity for this example (otherwise color objects work fine too.)
+let grays = map(samples(12), (c) => formatHex8(f(c)));
+console.log(map(grays, isAchromatic));
+
+//
+[ false, true, true,
+ true, true, true,
+ true, true, true,
+ true, true, false
+]
+```
+
+___
+
+### isCool
+
+▸ **isCool**(`color`): `boolean`
+
+Checks if a color can be roughly classified as a cool color. Returns true if color is a cool color else false.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `color` | [`ColorToken`](types.md#ColorToken) | The color to check the temperature. |
+
+#### Returns🔙
+
+`boolean`
+
+True or false.
+
+**`Example`** 📋
+
+```ts
+import { isCool } from 'huetiful-js'
+
+let sample = [
+ "#00ffdc",
+ "#00ff78",
+ "#00c000"
+];
+
+console.log(isCool(sample[2]));
+// false
+
+console.log(map(sample, isCool));
+
+// [ true, false, true]
+```
+
+___
+
+### isWarm
+
+▸ **isWarm**(`color`): `boolean`
+
+Checks if a color can be roughly classified as a warm color. Returns true if color is a warm color else false.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `color` | [`ColorToken`](types.md#ColorToken) | The color to check the temperature. |
+
+#### Returns🔙
+
+`boolean`
+
+True or false.
+
+**`Example`** 📋
+
+```ts
+import { isWarm } from 'huetiful-js'
+
+let sample = [
+ "#00ffdc",
+ "#00ff78",
+ "#00c000"
+];
+
+console.log(isWarm(sample[2]));
+//true
+
+console.log(map(sample, isWarm));
+
+// [ false, true, false]
+```
+
+___
+
+### overtone
+
+▸ **overtone**(`color`): `string` \| `boolean`
+
+Returns the hue which is biasing the passed in color
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `color` | [`ColorToken`](types.md#ColorToken) | The color to query its overtone. |
+
+#### Returns🔙
+
+`string` \| `boolean`
+
+The name of the overtone hue. If an achromatic color is passed in it return the string gray otherwise if the color has no bias it returns false.
+
+**`Example`** 📋
+
+```ts
+import { overtone } from "huetiful-js";
+
+console.log(overtone("fefefe"))
+// 'gray'
+
+console.log(overtone("cyan"))
+// 'green'
+
+console.log(overtone("blue"))
+// false
+```
+
+___
+
+### setChannel
+
+▸ **setChannel**(`mc`): (`color`: [`ColorToken`](types.md#ColorToken), `value`: `string` \| `number`) => [`ColorToken`](types.md#ColorToken)
+
+Sets the value for the specified channel in a color.
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `mc` | `string` | The mode and channel to work with. For example 'rgb.b'. |
+
+#### Returns🔙
+
+`fn`
+
+color The mutated color.
+
+▸ (`color`, `value`): [`ColorToken`](types.md#ColorToken)
+
+##### Parameters🧮
+
+| Name | Type |
+| :------ | :------ |
+| `color` | [`ColorToken`](types.md#ColorToken) |
+| `value` | `string` \| `number` |
+
+##### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)
+
+**`Example`** 📋
+
+```ts
+import { setChannel } from 'huetiful-js'
+
+let myColor = setChannel('lch.h')('green',10)
+
+console.log(getChannel('lch.h')(myColor))
+// 10
+```
+
+___
+
+### setLuminance
+
+▸ **setLuminance**(`color`, `lum`): [`ColorToken`](types.md#ColorToken)
+
+Sets the luminance by interpolating the color with black (to decrease luminance) or white (to increase the luminance).
+
+#### Parameters🧮
+
+| Name | Type | Description |
+| :------ | :------ | :------ |
+| `color` | [`ColorToken`](types.md#ColorToken) | The color to set luminance |
+| `lum` | `number` | The amount of luminance to set. The value range is normalised between [0,1] |
+
+#### Returns🔙
+
+[`ColorToken`](types.md#ColorToken)
+
+The mutated color with the modified properties.
+
+**`Example`** 📋
+
+```ts
+import { setLuminance, getLuminance } from 'huetiful-js'
+
+let myColor = setLuminance('#a1bd2f', 0.5)
+
+console.log(getLuminance(myColor))
+// 0.4999999136285792
+```
diff --git a/spacebook/filters/searchFilter.js b/spacebook/filters/searchFilter.js
new file mode 100644
index 00000000..566de841
--- /dev/null
+++ b/spacebook/filters/searchFilter.js
@@ -0,0 +1,56 @@
+const elasticlunr = require("elasticlunr");
+const emojiRegex = require('emoji-regex/RGI_Emoji.js')
+
+module.exports = function (collection) {
+ // what fields we'd like our index to consist of
+ var index = elasticlunr(function () {
+ this.addField("title");
+ this.addField("content");
+ this.setRef("id");
+ });
+
+ // loop through each page and add it to the index
+ collection.forEach((page) => {
+ index.addDoc({
+ id: page.url,
+ title: page.template.frontMatter.data.title,
+ content: squash(page.templateContent),
+ });
+ });
+
+ function squash(text) {
+ const regex = emojiRegex();
+ var content = new String(text);
+
+ // all lower case, please
+ var content = content.toLowerCase();
+
+ // remove all html elements and new lines
+ var re = /(.*?<.*?>)/gi;
+ var plain = unescape(content.replace(re, ''));
+
+ // remove duplicated words
+ var words = plain.split(' ');
+ var deduped = [...(new Set(words))];
+ var dedupedStr = deduped.join(' ')
+
+ // remove short and less meaningful words
+ var result = dedupedStr.replace(/\b(\.|\,|\<;|the|a|an|and|am|you|I|to|if|of|off|me|my|on|in|it|is|at|as|we|do|be|has|but|was|so|no|not|or|up|for)\b/gi, '');
+ //remove newlines, and punctuation
+ result = result.replace(/\.|\,|\?||-|—|\n/g, '');
+ //remove repeated spaces
+ result = result.replace(/[ ]{2,}/g, ' ');
+ // remove most emoji
+ result = result.replace(/([#0-9]\u20E3)|[\xA9\xAE\u203C\u2047-\u2049\u2122\u2139\u3030\u303D\u3297\u3299][\uFE00-\uFEFF]?|[\u2190-\u21FF][\uFE00-\uFEFF]?|[\u2300-\u23FF][\uFE00-\uFEFF]?|[\u2460-\u24FF][\uFE00-\uFEFF]?|[\u25A0-\u25FF][\uFE00-\uFEFF]?|[\u2600-\u27BF][\uFE00-\uFEFF]?|[\u2900-\u297F][\uFE00-\uFEFF]?|[\u2B00-\u2BF0][\uFE00-\uFEFF]?|(?:\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDEFF])[\uFE00-\uFEFF]?/g, '');
+
+ let match;
+ while (match = regex.exec(result)) {
+ const emoji = match[0];
+ result = result.replace(emoji,' ')
+ }
+
+ return result;
+ }
+
+ return index.toJSON();
+};
\ No newline at end of file
diff --git a/spacebook/package.json b/spacebook/package.json
new file mode 100644
index 00000000..68f6c45a
--- /dev/null
+++ b/spacebook/package.json
@@ -0,0 +1,67 @@
+{
+ "name": "spacebook",
+ "version": "1.0.0",
+ "description": "A simple site generator based on Eleventy, Tailwind 2.0, and Alpine.js",
+ "scripts": {
+ "start": "eleventy --serve & postcss styles/tailwind.css --o _tmp/style.css --watch",
+ "build": "eleventy & NODE_ENV=production postcss styles/tailwind.css --o _site/style.css && ./node_modules/.bin/cleancss -o _site/style.css _site/style.css ",
+ "watch": "npx eleventy --watch",
+ "debug": "DEBUG=* npx eleventy"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/broeker/spacebook.git"
+ },
+ "author": "Tim Broeker (https://www.electriccitizen.com/)",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/broeker/spacebook/issues"
+ },
+ "homepage": "https://github.com/broeker/spacebook",
+ "devDependencies": {
+ "@11ty/eleventy": "^2.0.1",
+ "alpinejs": "^2.7.3",
+ "eleventy-plugin-lazyimages": "^2.1.0",
+ "eslint": "^7.9.0",
+ "lazysizes": "^5.2.2",
+ "luxon": "^1.25.0",
+ "markdown-it": "^14.0.0",
+ "markdown-it-anchor": "^5.3.0",
+ "markdown-it-image-lazysizes": "^1.0.0",
+ "postcss-cli": "^8.3.0",
+ "prettier": "^2.1.2",
+ "tailwindcss": "^2.0.2"
+ },
+ "dependencies": {
+ "@11ty/eleventy-img": "^3.1.8",
+ "@11ty/eleventy-navigation": "^0.1.6",
+ "@tailwindcss/forms": "^0.2.1",
+ "@tailwindcss/typography": "^0.3.1",
+ "autoprefixer": "^10.1.0",
+ "clean-css": "^4.2.1",
+ "clean-css-cli": "^4.3.0",
+ "elasticlunr": "^0.9.5",
+ "eleventy-plugin-embed-everything": "^1.9.4",
+ "eleventy-plugin-nesting-toc": "^1.2.0",
+ "eleventy-plugin-svg-contents": "^0.7.0",
+ "eleventy-plugin-toc": "^1.1.0",
+ "emoji-regex": "^9.2.0",
+ "html-minifier": "^4.0.0",
+ "markdown-it-attrs": "^4.1.6",
+ "markdown-it-center-text": "^1.0.4",
+ "markdown-it-container": "^3.0.0",
+ "markdown-it-emoji": "^2.0.0",
+ "markdown-it-footnote": "^3.0.2",
+ "markdown-it-for-inline": "^0.1.1",
+ "markdown-it-linkify-images": "^2.0.0",
+ "markdown-it-table-of-contents": "^0.5.0",
+ "markdown-it-task-lists": "^2.1.1",
+ "postcss": "^8.2.2",
+ "qs": "^6.9.4",
+ "remove": "^0.1.5",
+ "staticrypt": "^1.3.2",
+ "uglify-es": "^3.3.9",
+ "url-pattern": "^1.0.3"
+ },
+ "main": ".eleventy.js"
+}
diff --git a/spacebook/postcss.config.js b/spacebook/postcss.config.js
new file mode 100644
index 00000000..7e8c15e1
--- /dev/null
+++ b/spacebook/postcss.config.js
@@ -0,0 +1,6 @@
+module.exports = {
+ plugins: [
+ require(`tailwindcss`)(`./styles/tailwind.config.js`),
+ require(`autoprefixer`),
+ ],
+};
diff --git a/spacebook/robots.md b/spacebook/robots.md
new file mode 100644
index 00000000..7d69aa86
--- /dev/null
+++ b/spacebook/robots.md
@@ -0,0 +1,5 @@
+---
+title: Robots
+permalink: /robots.txt
+layout: layouts/robots.njk
+---
diff --git a/spacebook/search-index.json.njk b/spacebook/search-index.json.njk
new file mode 100644
index 00000000..689e69a7
--- /dev/null
+++ b/spacebook/search-index.json.njk
@@ -0,0 +1,6 @@
+---
+permalink: /search-index.json
+---
+{{ collections.results | search | dump | safe }}
+
+
diff --git a/spacebook/styles/tailwind.config.js b/spacebook/styles/tailwind.config.js
new file mode 100644
index 00000000..a8da3d6c
--- /dev/null
+++ b/spacebook/styles/tailwind.config.js
@@ -0,0 +1,62 @@
+const autoprefixer = require('autoprefixer');
+
+module.exports = {
+ important: true,
+ future: {
+ removeDeprecatedGapUtilities: true,
+ purgeLayersByDefault: true,
+ },
+ purge: {
+ enabled: true,
+ content: ["_site/**/*.html"],
+ options: {
+ safelist: [],
+ },
+ },
+ darkMode: 'class',
+ theme: {
+ container: {
+ center: true,
+ },
+ extend: {
+ typography: {
+ DEFAULT: {
+ css: {
+ maxWidth: '100%',
+ a: {
+ color: '#1D4ED8',
+ '&:hover': {
+ color: '#1E3A8A',
+ },
+ },
+ '.prose a.edit, .tag a': {
+ color: '#333',
+ 'text-decoration': 'none',
+ },
+ 'ul.footer-nav': {
+ '::before': {
+ display: 'none',
+ 'text-decoration': 'none',
+ }
+ },
+ 'ul.contains-task-list': {
+ '::before': {
+ display: 'none',
+ }
+ },
+ 'ul.spacelog': {
+ '::before': {
+ display: 'none',
+ }
+ },
+ },
+ },
+ }
+ },
+ },
+ variants: {},
+ plugins: [
+ require('@tailwindcss/typography'),
+ require('@tailwindcss/forms'),
+ ],
+}
\ No newline at end of file
diff --git a/spacebook/styles/tailwind.css b/spacebook/styles/tailwind.css
new file mode 100644
index 00000000..43a33711
--- /dev/null
+++ b/spacebook/styles/tailwind.css
@@ -0,0 +1,130 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer base {
+
+ /* Set up some default image behavior for nicer images */
+ img {
+ @apply w-auto shadow-md border-2 border-transparent !important
+ }
+ img:hover {
+ @apply border-2 border-gray-100
+ }
+ /* Overrides for Tailwind Typography prose class */
+ .prose a {
+ @apply dark:text-gray-400
+ }
+ .prose a:hover {
+ @apply dark:text-gray-500
+ }
+ .prose h1, .prose h2, .prose h3, .prose h4, .prose h5, .prose h6 .prose hr, .prose strong {
+ @apply dark:text-gray-400
+ }
+ .prose h2, .prose h3, .prose h4, .prose h5, .prose h6 {
+ scroll-margin-top: 3.5em;
+ }
+ .prose pre code {
+ @apply overflow-x-auto !important
+ }
+ .prose .footer-nav a {
+ @apply no-underline !important
+ }
+ .prose ul.contains-task-list,
+ .prose ul.spacelog {
+ @apply list-none -ml-6 !important;
+ }
+ .prose ul.contains-task-list .task-list-item,
+ .prose ul.spacelog {
+ ::before {
+ @apply hidden !important;
+ }
+ }
+
+ /* Define blockquotes and some standard callout blocks */
+ blockquote {
+ @apply rounded-lg p-4 bg-gray-100 dark:bg-gray-500 border-gray-200 border-l-8 dark:border-gray-700;
+ }
+ .callout {
+ @apply px-8 py-4 mb-4 rounded-lg bg-yellow-50;
+ }
+ .callout, .callout strong, .callout em {
+ @apply dark:bg-gray-400 dark:text-gray-900;
+ }
+ .callout-blue {
+ @apply px-8 py-4 mb-4 rounded-lg bg-blue-50;
+ }
+ .callout-blue, .callout-blue strong, .callout-blue em {
+ @apply dark:text-gray-200 dark:bg-blue-900;
+ }
+ .callout-pink {
+ @apply px-8 py-4 mb-4 rounded-lg bg-pink-50;
+ }
+ .callout-pink, .callout-pink strong, .callout-pink em {
+ @apply dark:text-gray-200 dark:bg-pink-900;
+ }
+ .callout-green {
+ @apply px-8 py-4 mb-4 rounded-lg bg-green-50;
+ }
+ .callout-green, .callout-green strong, .callout-green em {
+ @apply dark:text-gray-200 dark:bg-green-900;
+ }
+ .warning {
+ @apply px-8 py-4 mb-4 rounded-lg bg-red-800 text-gray-50;
+ }
+ .warning, .warning strong, .warning em {
+ @apply text-gray-50 dark:bg-red-900 dark:text-gray-200;
+ }
+
+ /* Overrides for nav/Table of Contents block */
+ nav ul {
+ @apply ml-0 text-gray-500;
+ }
+ nav ul ul {
+ @apply ml-6 text-gray-500;
+ }
+ nav ul li a {
+ @apply mb-1 pt-2 pr-4 pb-1 pl-2 w-full block text-gray-500 dark:text-gray-500;
+ }
+ nav ul li a:hover {
+ @apply text-gray-900 dark:text-gray-400;
+ }
+ nav ul li a.active {
+ @apply font-semibold;
+ }
+ nav.toc ol li {
+ @apply pt-2 !important
+ }
+ nav.toc ol li li {
+ @apply pt-2 ml-4
+ }
+ nav.toc ol li a {
+ @apply text-gray-500
+ }
+ nav.toc ol li a:hover {
+ @apply text-gray-900
+ }
+ .prose .footer-nav a:hover {
+ @apply dark:text-gray-400 !important
+ }
+
+/* Utilities and misc */
+ .adjust p img, .adjust img, .adjust p iframe, .adjust iframe, .twitter-tweet {
+ @apply shadow-md ml-auto mr-auto p-2 !important
+ }
+ .adjust p img:hover, .adjust img:hover {
+ @apply shadow-xl
+ }
+ .adjust img.button {
+ @apply w-auto shadow-none !important
+ }
+ .text-align-center {
+ @apply flex justify-center;
+ }
+ .icon-spacer {
+ width: "24px";
+ }
+ input {
+ @apply dark:text-gray-400
+ }
+}
\ No newline at end of file
diff --git a/spacebook/uploads/uws2.png b/spacebook/uploads/uws2.png
new file mode 100644
index 00000000..4e54c5fa
Binary files /dev/null and b/spacebook/uploads/uws2.png differ
diff --git a/src/.eslintignore b/src/.eslintignore
new file mode 100644
index 00000000..f708b012
--- /dev/null
+++ b/src/.eslintignore
@@ -0,0 +1,6 @@
+node_modules
+dist
+*.js
+*.cjs
+leonardo-main
+
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 00000000..13e839a7
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1,2 @@
+/lib
+node_modules
\ No newline at end of file
diff --git a/src/.prettierignore b/src/.prettierignore
new file mode 100644
index 00000000..94ef8c0f
--- /dev/null
+++ b/src/.prettierignore
@@ -0,0 +1,6 @@
+**/node_modules
+**/color-maps
+**/lib
+.scripts
+webapp
+**/leonardo-main
\ No newline at end of file
diff --git a/src/adaptive.ts b/src/adaptive.ts
new file mode 100644
index 00000000..be86fcf8
--- /dev/null
+++ b/src/adaptive.ts
@@ -0,0 +1,76 @@
+/* eslint-disable @typescript-eslint/no-unused-vars */
+// @ts-nocheck
+import type { Color, AdaptivePaletteOptions } from '../types';
+// This module will make use of contrast ratio to create adaptive palettes
+
+// Things I need to understand first:
+// 1. What is adaptive color exactly
+// -> Adaptive color is meant to adjust to luminance changes in the surrounding colors (or simply background)
+
+// 2. What makes color adaptive
+// -> Being able to look perceptually similar between different themes or backgrounds. For example a certain color looks over saturated in a dark theme. We can change the color's luminance to compensate for this shortfall whilt maintaining the hue and saturation constraints in our design system.
+
+// 3. What is contrast and how does it affect adaptive color
+// -> Contrast is the perceived difference in color i.e the ability of one color to stand out from another. This allows colors to be readible on different backgrounds.
+
+// 4. What are the specifics of contrast ratio in relation to design elements
+// -> Contrast ratios allow us to define the contrast relationship between the colors we're working with. For example certain text must meet a minimum contrast ratio in order for it to be easily viewable to a wider userbase.
+
+// 5. What are the nuances of dark/light mode. How is the color corrected when we switch themes
+// -> Certain colors will diverge from our color system if we tune them up. For example yellow will turn dark yellow if we adjust it to meet WCAG requirements in light mode. Therefore such colors cannot be used for text.
+
+// 6. How can this be implemented in code
+
+// Providing a min luminance contrast ratio between text and background.
+//
+
+// adaptive returns an object of the adjusted colors
+// {lightMode/darkMode:Color[]}
+
+// Possible params
+// 1. backgroundColor -> The backgroundColor to compare against
+// 2. colors -> The colors to tune
+// 3. viewingConditions
+
+// First I need to declare some constants to serve as starting points
+////// Approach was adapted from Adobe's Leonardo tool
+
+/////// Rough concept of adaptive color
+
+// Dark theme factors to consider
+// 1. if color is darker than background. It means that color is not legible and will be tuned up
+// 2. if color is lighter than background. it means its legible
+
+// Light theme factors to consider
+// 1. if a color is lighter than background. This means color is not compliant and will be fixed up.
+// 2. if a color is darker than background. It means its legible
+
+// Constants
+// - baseluminance
+// - colorluminance
+
+// Find contrast colors
+// find contrast color pairs that are harmonius
+
+// The relative luminance returned should be compliant to the defined ratio
+
+// The correlates are channels in short "QJMCshH"
+
+// First convert cam to xyz then rgb
+
+const adaptivePalette = (colors: Color[], options?: AdaptivePaletteOptions) => {
+ // return customMystery;
+};
+
+// let { light, dark } = undefined || {};
+// let = options
+// light = checkArg(light, tailwindColors('gray')('100'));
+
+// // First get the contrast between the passed in color and the backgrounds
+// dark = checkArg(dark, tailwindColors('stone')('800'));
+// const lightContrast = getContrast(colors, light);
+// const darkContrast = getContrast(colors, dark);
+// const colorLuminance = getLuminance(color);
+// const lightLuminance = getLuminance(light);
+// const darkLuminance = getLuminance(dark);
+// colors = colors.map((color) => toJch(color));
diff --git a/src/color-maps/samples/modeRanges.ts b/src/color-maps/samples/modeRanges.ts
new file mode 100644
index 00000000..71d38837
--- /dev/null
+++ b/src/color-maps/samples/modeRanges.ts
@@ -0,0 +1,23 @@
+export default {
+ cubehelix: {
+ s: [0, 4.614],
+ l: [0, 1],
+ },
+ lab: {
+ l: [0, 100],
+ },
+ dlch: { l: [0, 100], c: [0, 51.484] },
+ jab: {
+ j: [0, 0.222],
+ },
+ jch: {
+ j: [0, 0.221],
+ c: [0, 0.19],
+ },
+ lch: { l: [0, 100], c: [0, 150] },
+ lch65: { l: [0, 100], c: [0, 133.807] },
+ lchuv: { l: [0, 100], c: [0, 176.956] },
+ luv: { l: [0, 100] },
+ oklab: { l: [0, 1] },
+ oklch: { l: [0, 1], c: [0, 0.4] },
+};
diff --git a/src/colors.ts b/src/colors.ts
new file mode 100644
index 00000000..eee4ff0c
--- /dev/null
+++ b/src/colors.ts
@@ -0,0 +1,1659 @@
+/*
+ * @license
+ * colors.ts - Colors and schemes for huetiful-js.
+ * Contains colors from TailwindCSS released under the MIT permissive licence.
+ * Contains parts of chroma.js released under the Apache-2.0 license.
+Copyright 2023 Dean Tarisai.
+This file is licensed to you under the Apache License, Version 2.0 (the 'License');
+you may not use this file except in compliance with the License. You may obtain a copy
+of the License at http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software distributed under
+the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+OF ANY KIND, either express or implied. See the License for the specific language
+governing permissions and limitations under the License.
+*/
+
+import tailwindHues from './color-maps/swatches/tailwind.ts';
+import type {
+ SequentialScheme,
+ DivergingScheme,
+ QualitativeScheme,
+ TailwindColorFamilies,
+ ScaleValues,
+ ColorDistanceOptions,
+ ColorToken,
+ HueColorSpaces,
+ InterpolatorOptions,
+ ColorOptions,
+ EarthtoneOptions,
+ HueFamily,
+ HueShiftOptions,
+ PairedSchemeOptions
+} from './types';
+
+import * as filterBy from './filterBy';
+import * as sortBy from './sortBy';
+import {
+ discoverPalettes as nativeDiscoverPalettes,
+ getFarthestHue as nativeMaxHue,
+ getNearestHue as nativeMinHue,
+ getNearestLightness as nativeMaxLightness,
+ getFarthestLightness as nativeMinLightness,
+ alpha as nativeAlpha,
+ brighten as nativeBrighten,
+ darken as nativeDarken,
+ isAchromatic as nativeIsAchromatic,
+ isCool as nativeIsCool,
+ isWarm as nativeIsWarm,
+ getFarthestChroma as nativeGetFarthestChroma,
+ getFarthestHue as nativeGetFarthestHue,
+ getFarthestLightness as nativeGetFarthestLightness,
+ getNearestHue as nativeGetNearestHue,
+ getNearestChroma as nativeGetNearestChroma,
+ getNearestLightness as nativeGetNearestLightness,
+ overtone as nativeOvertone,
+ toHex as nativeToHex,
+ getChannel as nativeGetChannel,
+ getContrast,
+ getLuminance,
+ setChannel as nativeSetChannel,
+ setLuminance,
+ checkArg,
+ matchChromaChannel,
+ scheme as nativeScheme,
+ pastel as nativePastel,
+ hueShift as nativeHueShift,
+ getHueFamily as nativeGetHue,
+ pairedScheme as nativePairedScheme,
+ earthtone as nativeEarthtone,
+ getComplimentaryHue as nativeGetComplimentaryHue,
+ colorDeficiency as nativeColorDeficiency,
+ interpolator,
+ interpolateSpline as nativeInterpolatorSpline
+} from './index';
+
+import { interpolatorConfig } from './helpers';
+
+class ColorArray {
+ constructor(colors: ColorToken[]) {
+ this['colors'] = colors;
+ return this;
+ }
+
+ /**
+ *
+ * Returns a spline based interpolator function with customizable interpolation methods (passed in as 'kind') and optional channel specific overrides.If a color has a falsy channel for example black has an undefined hue channel some interpolation methods may return NaN affecting the final result.
+ * @param colorspace The colorspace to perform the color space in. Prefer uniform color spaces for better results such as Lch or Jch.
+ * @param kind The type of the spline interpolation method. Default is basis.
+ * @param closed Optional parameter to return the 'closed' variant of the 'kind' of interpolation method which can be useful for cyclical color scales. Default is false
+ * @param options Optional channel specific overrides.
+ * @returns A hexadecimal representation of the resultant color.
+ */
+ interpolateSpline(
+ colorspace?: HueColorSpaces,
+ samples?: number,
+ kind?: 'natural' | 'monotone' | 'basis',
+ closed?: boolean,
+ options?: InterpolatorOptions
+ ): ColorToken[] {
+ this['colors'] = nativeInterpolatorSpline(
+ this['colors'],
+ colorspace,
+ samples,
+ kind,
+ closed,
+ options
+ );
+ // @ts-ignore
+ return this;
+ }
+
+ /**
+ *
+ * Takes an array of colors and finds the best matches for a set of predefined palettes. The function does not work on achromatic colors, you may use isAchromatic to filter grays from your collection before passing it to the function.
+ * @param schemeType (Optional) The palette type you want to return.
+ * @returns An array of colors if the scheme parameter is specified else it returns an object of all the palette types as keys and their values as an array of colors. If no colors are valid for the palette types it returns an empty array for the palette results.
+ * @example
+ *
+ * import { discoverPalettes } from 'huetiful-js'
+
+let sample = [
+ "#ffff00",
+ "#00ffdc",
+ "#00ff78",
+ "#00c000",
+ "#007e00",
+ "#164100",
+ "#720000",
+ "#600000",
+ "#4e0000",
+ "#3e0000",
+ "#310000",
+]
+
+console.log(load(sample).discoverPalettes(sample, "tetradic").output())
+// [ '#ffff00ff', '#00ffdcff', '#310000ff', '#720000ff' ]
+ */
+ discoverPalettes(
+ schemeType?: 'analogous' | 'triadic' | 'tetradic' | 'complementary'
+ ): ColorToken[] | object {
+ this['colors'] = nativeDiscoverPalettes(this['colors'], schemeType);
+ return this;
+ }
+
+ /**
+ *
+ * Gets the largest hue value from the passed in colors.
+ * @param colorSpace The mode color space to perform the computation in.
+ * @param colorObj Optional boolean that makes the function return a custom object with factor (hue) and name of the color as keys. Default is false.
+ * @returns The largest hue value in the colors passed in or a custom object.
+ * @example
+ *
+ * import { getFarthestHue } from 'huetiful-js'
+let sample = ['b2c3f1', '#a1bd2f', '#f3bac1']
+
+console.log(load(output).getFarthestHue('lch'))
+// 273.54920266436477
+ */
+ getFarthestHue(
+ colorSpace?: HueColorSpaces,
+ colorObj = false
+ ): number | { factor: number; color: ColorToken } {
+ return nativeMaxHue(this['colors'], colorSpace, colorObj);
+ }
+
+ /**
+ *
+ * Gets the smallest hue value from the passed in colors.
+ * @param colors The array of colors to query the color with the smallest hue value.
+ * @param colorSpace The mode color space to perform the computation in.
+ * @param colorObj Optional boolean that makes the function return a custom object with factor (hue) and name of the color as keys. Default is false.
+ * @returns The smallest hue value in the colors passed in or a custom object.
+ * @example
+ *
+ * import { getNearestHue } from 'huetiful-js'
+
+let sample = ['b2c3f1', '#a1bd2f', '#f3bac1']
+
+console.log(load(sample).getNearestHue('lch'))
+// 12.462831644544274
+ */
+ getNearestHue(
+ colorSpace?: HueColorSpaces,
+ colorObj = false
+ ): number | { factor: number; color: ColorToken } {
+ return nativeMinHue(this['colors'], colorSpace, colorObj);
+ }
+
+ /**
+ *
+ * Gets the smallest lightness value from the passed in colors.
+ * @param colorObj Optional boolean that makes the function return a custom object with factor (lightness) and name of the color as keys. Default is false.
+ * @returns The smallest lightness value in the colors passed in or a custom object.
+ * @example
+ *
+ * import { minLightness } from 'huetiful-js'
+
+let sample = ["b2c3f1", "#a1bd2f", "#f3bac1"]
+
+console.log(load(sample).getNearestLightness('lch', true))
+
+// { lightness: 72.61647882089876, name: '#a1bd2f' }
+
+ */
+ getNearestLightness(
+ colorspace?: HueColorSpaces,
+ colorObj = false
+ ): number | { factor: number; color: ColorToken } {
+ return nativeMinLightness(this['colors'], colorspace, colorObj);
+ }
+
+ /**
+ *
+ * Gets the largest lightness value from the passed in colors.
+ * @param colors The array of colors to query the color with the largest lightness value.
+ * @param colorObj Optional boolean that makes the function return a custom object with factor (lightness) and name of the color as keys. Default is false.
+ * @returns The largest lightness value in the colors passed in or a custom object.
+ * @example
+ *
+ * import { maxLightness } from 'huetiful-js'
+
+let sample = ["b2c3f1", "#a1bd2f", "#f3bac1"]
+
+console.log(load(sample).getFarthestLightness('lch', true))
+
+// { lightness: 80.94668903360088, name: '#f3bac1' }
+
+ */
+ getFarthestLightness(
+ colorspace?: HueColorSpaces,
+ colorObj = false
+ ): number | { factor: number; color: ColorToken } {
+ return nativeMaxLightness(this['colors'], colorspace, colorObj);
+ }
+
+ /**
+ *
+ * Returns an array of colors in the specified saturation range. The range is normalised to [0,1].
+ * @param startSaturation The minimum end of the saturation range.
+ * @param endSaturation The maximum end of the saturation range.
+ * @param mode The color space to fetch the saturation value from. Any color space with a chroma channel e.g 'lch' or 'hsl' will do.
+ * @returns Array of filtered colors.
+ * @example
+ * import { filterByContrast } from 'huetiful-js'
+
+let sample = [
+ '#00ffdc',
+ '#00ff78',
+ '#00c000',
+ '#007e00',
+ '#164100',
+ '#ffff00',
+ '#310000',
+ '#3e0000',
+ '#4e0000',
+ '#600000',
+ '#720000',
+]
+
+console.log(filterByContrast(sample, 'green', '>=3'))
+// [ '#00ffdc', '#00ff78', '#ffff00', '#310000', '#3e0000', '#4e0000' ]
+ */
+
+ filterBySaturation(
+ startSaturation = 0.05,
+ endSaturation = 1,
+ mode?: HueColorSpaces
+ ): ColorArray {
+ // @ts-ignore
+
+ this['colors'] = filterBy.filterBySaturation(
+ this['colors'],
+ startSaturation,
+ endSaturation,
+ mode
+ );
+ return this;
+ }
+
+ /**
+ *
+ * Returns an array of colors in the specified lightness range. The range is between 0 and 100.
+ * @param startLightness The minimum end of the lightness range.
+ * @param endLightness The maximum end of the lightness range.
+ * @returns Array of filtered colors.
+ * @example
+ *
+ * import { filterByLightness } from 'huetiful-js'
+let sample = [
+ '#00ffdc',
+ '#00ff78',
+ '#00c000',
+ '#007e00',
+ '#164100',
+ '#ffff00',
+ '#310000',
+ '#3e0000',
+ '#4e0000',
+ '#600000',
+ '#720000',
+]
+
+filterByLightness(sample, 20, 80)
+
+// [ '#00c000', '#007e00', '#164100', '#720000' ]
+ */
+ filterByLightness(startLightness = 5, endLightness = 100): ColorArray {
+ this['colors'] = filterBy.filterByLightness(
+ this['colors'],
+ startLightness,
+ endLightness
+ );
+ return this;
+ }
+ /**
+ *
+ * Returns an array of colors with the specified distance range. The distance is tested against a comparison color (the 'against' param) and the specified distance ranges.
+ * @param startDistance The minimum end of the distance range.
+ * @param endDistance The maximum end of the distance range.
+ * @param weights The weighting values to pass to the Euclidean function. Default is [1,1,1,0].
+ * @param mode The color space to calculate the distance in .
+ * @returns Array of filtered colors.
+ * @example
+ * import { filterByDistance } from 'huetiful-js'
+
+let sample = [
+ "#ffff00",
+ "#00ffdc",
+ "#00ff78",
+ "#00c000",
+ "#007e00",
+ "#164100",
+ "#720000",
+ "#600000",
+]
+
+console.log(filterByDistance(sample, "yellow", 0.1))
+// [ '#ffff00' ]
+ */
+
+ filterByDistance(
+ against: ColorToken,
+ startDistance = 0.05,
+ endDistance?: number
+ ): ColorArray {
+ this['colors'] = filterBy.filterByDistance(
+ this['colors'],
+ against,
+ startDistance,
+ endDistance
+ );
+ return this;
+ }
+
+ /**
+ *
+ *
+ * Returns an array of colors with the specified contrast range. The contrast is tested against a comparison color (the 'against' param) and the specified contrast ranges.
+ * @param startContrast The minimum end of the contrast range.
+ * @param endContrast The maximum end of the contrast range.
+ * @returns Array of filtered colors.
+ *
+ * @example
+ *
+ * import { filterByContrast } from 'huetiful-js'
+
+let sample = [
+ '#00ffdc',
+ '#00ff78',
+ '#00c000',
+ '#007e00',
+ '#164100',
+ '#ffff00',
+ '#310000',
+ '#3e0000',
+ '#4e0000',
+ '#600000',
+ '#720000',
+]
+
+console.log(filterByContrast(sample, 'green', '>=3'))
+// [ '#00ffdc', '#00ff78', '#ffff00', '#310000', '#3e0000', '#4e0000' ]
+ */
+
+ filterByContrast(
+ against: ColorToken,
+ startContrast = 0.05,
+ endContrast?: number
+ ): ColorArray {
+ this['colors'] = filterBy.filterByContrast(
+ this['colors'],
+ against,
+ startContrast,
+ endContrast
+ );
+
+ return this;
+ }
+ /**
+ *
+ * Returns colors in the specified hue ranges between 0 to 360.
+ * @param startHue The minimum end of the hue range.
+ * @param endHue The maximum end of the hue range.
+ * @returns Array of the filtered colors.
+ * @example
+ * let sample = [
+ '#00ffdc',
+ '#00ff78',
+ '#00c000',
+ '#007e00',
+ '#164100',
+ '#ffff00',
+ '#310000',
+ '#3e0000',
+ '#4e0000',
+ '#600000',
+ '#720000',
+]
+
+filterByHue(sample, 20, 80)
+
+// [ '#310000', '#3e0000', '#4e0000', '#600000', '#720000' ]
+ */
+
+ filterByHue(startHue = 0, endHue = 360): ColorArray {
+ this['colors'] = filterBy.filterByHue(this['colors'], startHue, endHue);
+ return this;
+ }
+ /**
+ *
+ * Returns an array of colors in the specified luminance range. The range is normalised to [0,1].
+ * @param startLuminance The minimum end of the luminance range.
+ * @param endLuminance The maximum end of the luminance range.
+ * @returns Array of filtered colors.
+ * @example
+ *
+ * import { filterByLuminance } from 'huetiful-js'
+let sample = [
+ '#00ffdc',
+ '#00ff78',
+ '#00c000',
+ '#007e00',
+ '#164100',
+ '#ffff00',
+ '#310000',
+ '#3e0000',
+ '#4e0000',
+ '#600000',
+ '#720000',
+]
+
+filterByLuminance(sample, 0.4, 0.9)
+
+// [ '#00ffdc', '#00ff78' ]
+ */
+
+ filterByLuminance(startLuminance = 0.05, endLuminance = 1): ColorArray {
+ this['colors'] = filterBy.filterByLuminance(
+ this['colors'],
+ startLuminance,
+ endLuminance
+ );
+ return this;
+ }
+
+ /**
+ *
+ * Sorts colors according to their lightness.
+ * @param colors The array of colors to sort
+ * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')
+ * @returns An array of the sorted color values.
+ * @example
+ * import { sortByLightness } from "huetiful-js";
+
+let sample = [
+ "#00ffdc",
+ "#00ff78",
+ "#00c000",
+ "#007e00",
+ "#164100",
+ "#ffff00",
+ "#310000",
+ "#3e0000",
+ "#4e0000",
+ "#600000",
+ "#720000",
+]
+
+sortByLightness(sample)
+
+// [
+ '#310000', '#3e0000',
+ '#4e0000', '#600000',
+ '#720000', '#164100',
+ '#007e00', '#00c000',
+ '#00ff78', '#00ffdc',
+ '#ffff00'
+]
+
+
+sortByLightness(sample,'desc')
+
+// [
+ '#ffff00', '#00ffdc',
+ '#00ff78', '#00c000',
+ '#007e00', '#164100',
+ '#720000', '#600000',
+ '#4e0000', '#3e0000',
+ '#310000'
+]
+
+ */
+
+ sortByLightness(order?: 'asc' | 'desc'): ColorArray {
+ // @ts-ignore
+
+ this['colors'] = sortBy.sortByLightness(this['colors'], order);
+ return this;
+ }
+ /**
+ *
+ * Sorts colors according to their Euclidean distance. The distance factor is determined by the color space used (some color spaces are not symmetrical meaning that the distance between colorA and colorB is not equal to the distance between colorB and colorA ). The distance is computed from against a color which is used for comparison for all the colors in the array i.e it sorts the colors against the dist
+ * @param against The color to compare the distance with. All the distances are calculated between this color and the ones in the colors array.
+ * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')
+ * @param weights The weighting values to pass to the Euclidean function. Default is [1,1,1,0].
+ * @param mode The color space to calculate the distance in . The default is the cylindrical variant of the CIELUV colorspace ('lchuv')
+ * @returns An array of the sorted color values.
+ * @example
+ * import { sortByDistance } from 'huetiful-js'
+
+let sample = ['purple', 'green', 'red', 'brown']
+console.log(
+ sortByDistance(sample, 'yellow', 'asc', {
+ mode: 'lch',
+ })
+)
+
+// [ 'brown', 'red', 'green', 'purple' ]
+
+let sample = ['purple', 'green', 'red', 'brown']
+console.log(
+ sortByDistance(sample, 'yellow', 'asc', {
+ mode: 'lch',
+ })
+)
+
+// [ 'green', 'brown', 'red', 'purple' ]
+ */
+
+ sortByDistance(
+ against: ColorToken,
+ order?: 'asc' | 'desc',
+ options?: ColorDistanceOptions
+ ): ColorArray {
+ this['colors'] = sortBy.sortByDistance(
+ this['colors'],
+ against,
+ order,
+ options
+ );
+
+ return this;
+ }
+
+ /**
+ *
+ * Sorts colors according to the relative brightness as defined by WCAG definition.
+ * @param colors The array of colors to sort
+ * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')
+ * @returns An array of the sorted color values.
+ * @example
+ * import { sortByLuminance } from "huetiful-js";
+let sample = [
+ "#00ffdc",
+ "#00ff78",
+ "#00c000",
+ "#007e00",
+ "#164100",
+ "#ffff00",
+ "#310000",
+ "#3e0000",
+ "#4e0000",
+ "#600000",
+ "#720000",
+];
+
+
+
+let sorted = sortByLuminance(sample)
+console.log(sorted)
+// [
+ '#310000', '#3e0000',
+ '#4e0000', '#600000',
+ '#720000', '#164100',
+ '#007e00', '#00c000',
+ '#00ff78', '#00ffdc',
+ '#ffff00'
+]
+
+let sortedDescending = sortByLuminance(sample, "desc");
+console.log(sortedDescending)
+// [
+ '#ffff00', '#00ffdc',
+ '#00ff78', '#00c000',
+ '#007e00', '#164100',
+ '#720000', '#600000',
+ '#4e0000', '#3e0000',
+ '#310000'
+]
+
+
+ */
+
+ sortByLuminance(order?: 'asc' | 'desc'): ColorArray {
+ this['colors'] = sortBy.sortByLuminance(this['colors'], order);
+ return this;
+ }
+ /**
+ *
+ * Sorts colors according to their saturation.
+ * @param colors The array of colors to sort
+ * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')
+ * @param mode The mode color space to compute the saturation value in. The default is jch .
+ * @returns An array of the sorted color values.
+ * @example
+ * import { sortBySaturation } from "huetiful-js";
+let sample = [
+ "#00ffdc",
+ "#00ff78",
+ "#00c000",
+ "#007e00",
+ "#164100",
+ "#ffff00",
+ "#310000",
+ "#3e0000",
+ "#4e0000",
+ "#600000",
+ "#720000",
+];
+
+let sorted = sortBySaturation(sample);
+console.log(sorted);
+
+// [
+ '#310000', '#3e0000',
+ '#164100', '#4e0000',
+ '#600000', '#720000',
+ '#00ffdc', '#007e00',
+ '#00ff78', '#00c000',
+ '#ffff00'
+]
+
+let sortedDescending = sortBySaturation(sample,'desc');
+console.log(sortedDescending)
+// [
+ '#ffff00', '#00c000',
+ '#00ff78', '#007e00',
+ '#00ffdc', '#720000',
+ '#600000', '#4e0000',
+ '#164100', '#3e0000',
+ '#310000'
+]
+
+ */
+
+ sortBySaturation(order: 'asc' | 'desc', mode?: HueColorSpaces): ColorArray {
+ this['colors'] = sortBy.sortBySaturation(this['colors'], order, mode);
+ return this;
+ }
+
+ /**
+ *
+ * Sorts colors according to their contrast value as defined by WCAG. The contrast is tested against a comparison color (the 'against' param)
+ * @param colors The array of colors to sort
+ * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')
+ * @returns An array of the sorted color values.
+ * @example
+ *
+ * import { sortByContrast } from 'huetiful-js'
+
+let sample = ['purple', 'green', 'red', 'brown']
+console.log(sortByContrast(sample, 'yellow'))
+// [ 'red', 'green', 'brown', 'purple' ]
+
+console.log(sortByContrast(sample, 'yellow', 'desc'))
+// [ 'purple', 'brown', 'green', 'red' ]
+
+ */
+
+ sortByContrast(against: ColorToken, order?: 'asc' | 'desc'): ColorArray {
+ this['colors'] = sortBy.sortByContrast(this['colors'], against, order);
+ return this;
+ }
+ /**
+ *
+ * Sorts colors according to hue values. It works with any color space with a hue channel. Note that hue values between HSL and Lch do not align. Achromatic colors are not supported
+ * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')
+* @param colorspace The color space to compute the color distances in. All colors within the collection will be converted to mode. Also note that because differences in hue mapping certain color spaces such as HSL and LCH hue values do not align. Keep such quirks in mind to avoid weird results.
+* @returns An array of the sorted color values.
+ * @example
+ * let sample = [
+ "#00ffdc",
+ "#00ff78",
+ "#00c000",
+ "#007e00",
+ "#164100",
+ "#ffff00",
+ "#310000",
+ "#3e0000",
+ "#4e0000",
+ "#600000",
+ "#720000",
+];
+
+
+let sorted = sortByHue(sample);
+console.log(sorted)
+// [
+ '#310000', '#3e0000',
+ '#4e0000', '#600000',
+ '#720000', '#ffff00',
+ '#164100', '#00c000',
+ '#007e00', '#00ff78',
+ '#00ffdc'
+]
+
+let sortedDescending = sortByHue(sample,'desc');
+console.log(sortedDescending)
+// [
+ '#00ffdc', '#00ff78',
+ '#007e00', '#00c000',
+ '#164100', '#ffff00',
+ '#720000', '#600000',
+ '#4e0000', '#3e0000',
+ '#310000'
+]
+
+ */
+
+ // Todo: Add the mode param so that users can select mode to work with. The default is lch
+ sortByHue(order: 'asc' | 'desc', colorspace: HueColorSpaces): ColorArray {
+ this['colors'] = sortBy.sortByHue(this['colors'], order, colorspace);
+ return this;
+ }
+
+ /**
+ * @method
+ * @returns Returns the result value from the chain.
+ */
+ output(): ColorToken {
+ return this['colors'];
+ }
+}
+
+/**
+ * @class
+ * A class that takes an array of colors and exposes all the utilities that handle collections of colors as methods. The methods can be chained as long as `this` being returned can be iterated on. Works like Array object.
+ * @param colors An array of colors to chain the array methods on. Every element in the array will be parsed as a color token.
+ */
+function load(colors: ColorToken[]): ColorArray {
+ return new ColorArray(colors);
+}
+
+//Check if the scheme object has the passed in scheme
+function schemeMapper(scheme: string, schemesObject: object): ColorToken[] {
+ const cb = (str: string) => str.toLowerCase();
+ const { keys } = Object;
+ // Map all schemes keys to lower case
+ const schemeOptions = keys(schemesObject).map(cb);
+
+ scheme = cb(scheme);
+ // Check if passed in scheme is available
+ if (schemeOptions.indexOf(scheme) > -1) {
+ return schemesObject[scheme];
+ } else {
+ // Else throw error:Invalid scheme
+ throw Error(`${scheme} is an invalid scheme option.`);
+ }
+}
+
+/**
+ *
+ * A wrapper function for ColorBrewer's map of sequential color schemes.
+ * @param scheme The name of the scheme
+ * @returns An array of colors in hex represantation.
+ * @example
+ * import { sequential } from 'huetiful-js
+
+
+console.log(sequential("OrRd"))
+
+// [
+ '#fff7ec', '#fee8c8',
+ '#fdd49e', '#fdbb84',
+ '#fc8d59', '#ef6548',
+ '#d7301f', '#b30000',
+ '#7f0000'
+]
+
+
+
+ */
+function sequential(scheme: SequentialScheme): ColorToken[] {
+ const schemes = {
+ OrRd: [
+ '#fff7ec',
+ '#fee8c8',
+ '#fdd49e',
+ '#fdbb84',
+ '#fc8d59',
+ '#ef6548',
+ '#d7301f',
+ '#b30000',
+ '#7f0000'
+ ],
+ PuBu: [
+ '#fff7fb',
+ '#ece7f2',
+ '#d0d1e6',
+ '#a6bddb',
+ '#74a9cf',
+ '#3690c0',
+ '#0570b0',
+ '#045a8d',
+ '#023858'
+ ],
+ BuPu: [
+ '#f7fcfd',
+ '#e0ecf4',
+ '#bfd3e6',
+ '#9ebcda',
+ '#8c96c6',
+ '#8c6bb1',
+ '#88419d',
+ '#810f7c',
+ '#4d004b'
+ ],
+ Oranges: [
+ '#fff5eb',
+ '#fee6ce',
+ '#fdd0a2',
+ '#fdae6b',
+ '#fd8d3c',
+ '#f16913',
+ '#d94801',
+ '#a63603',
+ '#7f2704'
+ ],
+ BuGn: [
+ '#f7fcfd',
+ '#e5f5f9',
+ '#ccece6',
+ '#99d8c9',
+ '#66c2a4',
+ '#41ae76',
+ '#238b45',
+ '#006d2c',
+ '#00441b'
+ ],
+ YlOrBr: [
+ '#ffffe5',
+ '#fff7bc',
+ '#fee391',
+ '#fec44f',
+ '#fe9929',
+ '#ec7014',
+ '#cc4c02',
+ '#993404',
+ '#662506'
+ ],
+ YlGn: [
+ '#ffffe5',
+ '#f7fcb9',
+ '#d9f0a3',
+ '#addd8e',
+ '#78c679',
+ '#41ab5d',
+ '#238443',
+ '#006837',
+ '#004529'
+ ],
+ Reds: [
+ '#fff5f0',
+ '#fee0d2',
+ '#fcbba1',
+ '#fc9272',
+ '#fb6a4a',
+ '#ef3b2c',
+ '#cb181d',
+ '#a50f15',
+ '#67000d'
+ ],
+ RdPu: [
+ '#fff7f3',
+ '#fde0dd',
+ '#fcc5c0',
+ '#fa9fb5',
+ '#f768a1',
+ '#dd3497',
+ '#ae017e',
+ '#7a0177',
+ '#49006a'
+ ],
+ Greens: [
+ '#f7fcf5',
+ '#e5f5e0',
+ '#c7e9c0',
+ '#a1d99b',
+ '#74c476',
+ '#41ab5d',
+ '#238b45',
+ '#006d2c',
+ '#00441b'
+ ],
+ YlGnBu: [
+ '#ffffd9',
+ '#edf8b1',
+ '#c7e9b4',
+ '#7fcdbb',
+ '#41b6c4',
+ '#1d91c0',
+ '#225ea8',
+ '#253494',
+ '#081d58'
+ ],
+ Purples: [
+ '#fcfbfd',
+ '#efedf5',
+ '#dadaeb',
+ '#bcbddc',
+ '#9e9ac8',
+ '#807dba',
+ '#6a51a3',
+ '#54278f',
+ '#3f007d'
+ ],
+ GnBu: [
+ '#f7fcf0',
+ '#e0f3db',
+ '#ccebc5',
+ '#a8ddb5',
+ '#7bccc4',
+ '#4eb3d3',
+ '#2b8cbe',
+ '#0868ac',
+ '#084081'
+ ],
+ Greys: [
+ '#ffffff',
+ '#f0f0f0',
+ '#d9d9d9',
+ '#bdbdbd',
+ '#969696',
+ '#737373',
+ '#525252',
+ '#252525',
+ '#000000'
+ ],
+ YlOrRd: [
+ '#ffffcc',
+ '#ffeda0',
+ '#fed976',
+ '#feb24c',
+ '#fd8d3c',
+ '#fc4e2a',
+ '#e31a1c',
+ '#bd0026',
+ '#800026'
+ ],
+ PuRd: [
+ '#f7f4f9',
+ '#e7e1ef',
+ '#d4b9da',
+ '#c994c7',
+ '#df65b0',
+ '#e7298a',
+ '#ce1256',
+ '#980043',
+ '#67001f'
+ ],
+ Blues: [
+ '#f7fbff',
+ '#deebf7',
+ '#c6dbef',
+ '#9ecae1',
+ '#6baed6',
+ '#4292c6',
+ '#2171b5',
+ '#08519c',
+ '#08306b'
+ ],
+ PuBuGn: [
+ '#fff7fb',
+ '#ece2f0',
+ '#d0d1e6',
+ '#a6bddb',
+ '#67a9cf',
+ '#3690c0',
+ '#02818a',
+ '#016c59',
+ '#014636'
+ ],
+ Viridis: [
+ '#440154',
+ '#482777',
+ '#3f4a8a',
+ '#31678e',
+ '#26838f',
+ '#1f9d8a',
+ '#6cce5a',
+ '#b6de2b',
+ '#fee825'
+ ]
+ };
+
+ return schemeMapper(scheme, schemes);
+}
+
+/**
+ *
+ * A wrapper function for ColorBrewer's map of diverging color schemes.
+ * @param scheme The name of the scheme.
+ * @returns An array of colors in hex represantation.
+ * @example
+ *
+ * import { diverging } from 'huetiful-js'
+
+
+
+console.log(diverging("Spectral"))
+//[
+ '#7fc97f', '#beaed4',
+ '#fdc086', '#ffff99',
+ '#386cb0', '#f0027f',
+ '#bf5b17', '#666666'
+]
+ */
+
+function diverging(scheme: DivergingScheme): ColorToken[] {
+ const schemes = {
+ Spectral: [
+ '#9e0142',
+ '#d53e4f',
+ '#f46d43',
+ '#fdae61',
+ '#fee08b',
+ '#ffffbf',
+ '#e6f598',
+ '#abdda4',
+ '#66c2a5',
+ '#3288bd',
+ '#5e4fa2'
+ ],
+ RdYlGn: [
+ '#a50026',
+ '#d73027',
+ '#f46d43',
+ '#fdae61',
+ '#fee08b',
+ '#ffffbf',
+ '#d9ef8b',
+ '#a6d96a',
+ '#66bd63',
+ '#1a9850',
+ '#006837'
+ ],
+ RdBu: [
+ '#67001f',
+ '#b2182b',
+ '#d6604d',
+ '#f4a582',
+ '#fddbc7',
+ '#f7f7f7',
+ '#d1e5f0',
+ '#92c5de',
+ '#4393c3',
+ '#2166ac',
+ '#053061'
+ ],
+ PiYG: [
+ '#8e0152',
+ '#c51b7d',
+ '#de77ae',
+ '#f1b6da',
+ '#fde0ef',
+ '#f7f7f7',
+ '#e6f5d0',
+ '#b8e186',
+ '#7fbc41',
+ '#4d9221',
+ '#276419'
+ ],
+ PRGn: [
+ '#40004b',
+ '#762a83',
+ '#9970ab',
+ '#c2a5cf',
+ '#e7d4e8',
+ '#f7f7f7',
+ '#d9f0d3',
+ '#a6dba0',
+ '#5aae61',
+ '#1b7837',
+ '#00441b'
+ ],
+ RdYlBu: [
+ '#a50026',
+ '#d73027',
+ '#f46d43',
+ '#fdae61',
+ '#fee090',
+ '#ffffbf',
+ '#e0f3f8',
+ '#abd9e9',
+ '#74add1',
+ '#4575b4',
+ '#313695'
+ ],
+ BrBG: [
+ '#543005',
+ '#8c510a',
+ '#bf812d',
+ '#dfc27d',
+ '#f6e8c3',
+ '#f5f5f5',
+ '#c7eae5',
+ '#80cdc1',
+ '#35978f',
+ '#01665e',
+ '#003c30'
+ ],
+ RdGy: [
+ '#67001f',
+ '#b2182b',
+ '#d6604d',
+ '#f4a582',
+ '#fddbc7',
+ '#ffffff',
+ '#e0e0e0',
+ '#bababa',
+ '#878787',
+ '#4d4d4d',
+ '#1a1a1a'
+ ],
+ PuOr: [
+ '#7f3b08',
+ '#b35806',
+ '#e08214',
+ '#fdb863',
+ '#fee0b6',
+ '#f7f7f7',
+ '#d8daeb',
+ '#b2abd2',
+ '#8073ac',
+ '#542788',
+ '#2d004b'
+ ]
+ };
+
+ return schemeMapper(scheme, schemes);
+}
+
+/**
+ *
+ * A wrapper function for ColorBrewer's map of qualitative color schemes.
+ * @param scheme The name of the scheme
+ * @returns An array of colors in hex represantation.
+ * @example
+ *
+ * import { qualitative } from 'huetiful-js'
+
+
+console.log(qualitative("Accent"))
+// [
+ '#7fc97f', '#beaed4',
+ '#fdc086', '#ffff99',
+ '#386cb0', '#f0027f',
+ '#bf5b17', '#666666'
+]
+
+ */
+
+function qualitative(scheme: QualitativeScheme): ColorToken[] {
+ const schemes = {
+ Set2: [
+ '#66c2a5',
+ '#fc8d62',
+ '#8da0cb',
+ '#e78ac3',
+ '#a6d854',
+ '#ffd92f',
+ '#e5c494',
+ '#b3b3b3'
+ ],
+ Accent: [
+ '#7fc97f',
+ '#beaed4',
+ '#fdc086',
+ '#ffff99',
+ '#386cb0',
+ '#f0027f',
+ '#bf5b17',
+ '#666666'
+ ],
+ Set1: [
+ '#e41a1c',
+ '#377eb8',
+ '#4daf4a',
+ '#984ea3',
+ '#ff7f00',
+ '#ffff33',
+ '#a65628',
+ '#f781bf',
+ '#999999'
+ ],
+ Set3: [
+ '#8dd3c7',
+ '#ffffb3',
+ '#bebada',
+ '#fb8072',
+ '#80b1d3',
+ '#fdb462',
+ '#b3de69',
+ '#fccde5',
+ '#d9d9d9',
+ '#bc80bd',
+ '#ccebc5',
+ '#ffed6f'
+ ],
+ Dark2: [
+ '#1b9e77',
+ '#d95f02',
+ '#7570b3',
+ '#e7298a',
+ '#66a61e',
+ '#e6ab02',
+ '#a6761d',
+ '#666666'
+ ],
+ Paired: [
+ '#a6cee3',
+ '#1f78b4',
+ '#b2df8a',
+ '#33a02c',
+ '#fb9a99',
+ '#e31a1c',
+ '#fdbf6f',
+ '#ff7f00',
+ '#cab2d6',
+ '#6a3d9a',
+ '#ffff99',
+ '#b15928'
+ ],
+ Pastel2: [
+ '#b3e2cd',
+ '#fdcdac',
+ '#cbd5e8',
+ '#f4cae4',
+ '#e6f5c9',
+ '#fff2ae',
+ '#f1e2cc',
+ '#cccccc'
+ ],
+ Pastel1: [
+ '#fbb4ae',
+ '#b3cde3',
+ '#ccebc5',
+ '#decbe4',
+ '#fed9a6',
+ '#ffffcc',
+ '#e5d8bd',
+ '#fddaec',
+ '#f2f2f2'
+ ]
+ };
+ return schemeMapper(scheme, schemes);
+}
+
+/**
+ *
+ * A wrapper function for the default Tailwind palette. If called with both parameters it return the hex code at the specified shade and value. Else, if called with the shade parameter as "all" it will return all colors from the shades in the palette map at the specified value (if value is undefined it will default to "500"). When called with the shade parameter only it will return all the colors from 100 to 900 of the specified shade.
+ * @param shade Any shade in the default TailwindCSS palette e.g amber,blue.
+ * @param value Any value from 100 to 900 in increments of 100 e.g "200".
+ * @returns color Returns a hex code string or array of hex codes depending on how the function is called.
+ * @example
+ *
+ * import { colors } from "huetiful-js";
+
+let all300 = colors("all", 300);
+
+console.log(all300)
+//[
+ '#cbd5e1', '#d1d5db', '#d4d4d8',
+ '#d4d4d4', '#d6d3d1', '#fca5a5',
+ '#fdba74', '#fcd34d', '#fde047',
+ '#bef264', '#86efac', '#6ee7b7',
+ '#5eead4', '#7dd3fc', '#93c5fd',
+ '#c4b5fd', '#d8b4fe', '#f0abfc',
+ '#f9a8d4', '#fda4af'
+]
+
+let red = colors("red");
+console.log(red);
+
+// [
+ '#fef2f2', '#fee2e2',
+ '#fecaca', '#fca5a5',
+ '#f87171', '#ef4444',
+ '#dc2626', '#b91c1c',
+ '#991b1b', '#7f1d1d'
+]
+
+let red100 = colors("red", 100);
+
+console.log(red100)
+// #fee2e2
+ */
+function colors(
+ shade: TailwindColorFamilies | 'all',
+ value?: ScaleValues
+): ColorToken | ColorToken[] {
+ const { keys } = Object;
+ const defaultHue = 'all';
+ const hueKeys = keys(tailwindHues);
+
+ // @ts-ignore
+ shade = shade.toLowerCase();
+ // First do an AND check on hue and val params. If true return the hue at the specified value.
+ // If only the hue is defined return the whole array of hex codes for that color.
+ // If only the value is defined return that color value for every hue.
+ // @ts-ignore
+ if (shade === defaultHue) {
+ return hueKeys.map((color) => tailwindHues[color][value || '500']);
+ } else if (hueKeys.some((hue) => hue === shade) && value) {
+ return tailwindHues[shade][value];
+ } else if (shade && typeof value == 'undefined') {
+ const keyVals = keys(tailwindHues[shade]);
+ return keyVals.map((key) => tailwindHues[shade][key]);
+ } else if (typeof shade && typeof value == 'undefined') {
+ throw Error(`Both shade and value cannot be undefined`);
+ }
+}
+
+/**
+ *
+ * Wrapper function that returns TailwindCSS color value(s) of the specified shade. If invoked with no parameters it returns an array of colors from 100 to 900. If invoked with parameter will return the specified shade vale,
+ * @param val The tone value of the shade. Values are in incrementals of 100. Both numeric (100) and its string equivalent ('100') are valid.
+ * @returns color A hex string value or array of hex strings.
+ * @example
+ *
+ * import { tailwindColors } from "huetiful-js";
+
+// We pass in red as the target hue.
+// It returns a function that can be called with an optional value parameter
+let red = tailwindColors("red");
+console.log(red());
+// [
+ '#fef2f2', '#fee2e2',
+ '#fecaca', '#fca5a5',
+ '#f87171', '#ef4444',
+ '#dc2626', '#b91c1c',
+ '#991b1b', '#7f1d1d'
+]
+
+
+console.log(red(100));
+// '#fee2e2'
+
+console.log(red('900'));
+// '#7f1d1d'
+
+
+ *
+ */
+function tailwindColors(shade: keyof TailwindColorFamilies) {
+ return (val?: ScaleValues): string | string[] => {
+ // This is a curried func that takes in the shade and returns a function that takes in a value from 100 thru 900
+ // @ts-ignore
+ shade = shade.toLowerCase();
+ const { keys } = Object;
+ // We check if the shade is a valid Tailwind shade if not we return pure black.
+ let targetHue: object;
+
+ if (keys(tailwindHues).indexOf(shade as string) != -1) {
+ targetHue = tailwindHues[shade];
+ } else {
+ throw Error(
+ `${
+ shade as string
+ } is not a valid shade in the default Tailwind palette`
+ );
+ }
+
+ if (typeof val === 'undefined') {
+ return keys(targetHue).map((value) => targetHue[value]);
+ } else if (keys(targetHue).indexOf(val) > -1) {
+ return targetHue[val];
+ } else {
+ throw Error(
+ `${val} is not a valid scale value. Values are in increments of 100 up to 900 e.g "200"`
+ );
+ }
+ };
+}
+
+class Color {
+ constructor(c: ColorToken, options?: ColorOptions) {
+ let {
+ alpha,
+ colorspace,
+ luminance,
+ saturation,
+ lightMode,
+ darkMode,
+ lightness
+ } = checkArg(options, {}) as ColorOptions;
+ c = checkArg(c, '#000') as ColorToken;
+
+ // Set the alpha of the color if its not explicitly passed in.
+ //@ts-ignore
+ this['alpha'] = checkArg(alpha, nativeAlpha(c));
+
+ // if the color is undefined we cast pure black
+
+ this['_color'] = c;
+
+ // set the color's luminance if its not explicitly passed in
+ this['_luminance'] = checkArg(luminance, getLuminance(c));
+
+ // set the color's lightness if its not explicitly passed in the default lightness is in Lch but will be refactored soon
+ this['lightness'] = checkArg(lightness, nativeGetChannel('lch.l')(c));
+
+ // set the default color space as jch if a color space is not specified. TODO: get the mode from object and array
+ this['colorspace'] = checkArg(colorspace, 'jch');
+
+ // set the default saturation to that of the passed in color if the value is not explicitly set
+ this['_saturation'] = checkArg(
+ saturation,
+ nativeGetChannel(
+ `${this['colorspace']}.${matchChromaChannel(this['colorspace'])}`
+ )(c)
+ );
+
+ // light mode default is gray-100
+ this['lightMode'] = checkArg(lightMode, colors('gray', '100'));
+
+ // dark mode default is gray-800
+ this['darkMode'] = checkArg(darkMode, colors('gray', '800'));
+ }
+
+ alpha(amount?: number | string): Color | number {
+ if (amount) {
+ this['_color'] = nativeAlpha(this['_color'], amount);
+ return this;
+ } else {
+ return nativeAlpha(this['_color']);
+ }
+ }
+ getChannel(channel: string) {
+ return nativeGetChannel(`${this['colorspace']}.${channel.toLowerCase()}`)(
+ this['_color']
+ );
+ }
+ setChannel(modeChannel: string, value: number | string): Color {
+ this['_color'] = nativeSetChannel(modeChannel)(this['_color'], value);
+ return this;
+ }
+
+ via(origin: ColorToken, t?: number, options?: typeof interpolatorConfig) {
+ const result = interpolator(
+ [origin, this['_color']],
+ this['colorspace'],
+ options
+ );
+
+ return nativeToHex(result(t));
+ }
+
+ brighten(amount: number | string, colorspace) {
+ this['_color'] = nativeBrighten(this['_color'], amount, colorspace);
+ return this;
+ }
+ darken(amount: number | string) {
+ this['_color'] = nativeDarken(this['_color'], amount);
+ return this;
+ }
+
+ toHex(): Color {
+ this['_color'] = nativeToHex(this['_color']);
+ return this['_color'];
+ }
+ pastel(): Color {
+ this['_color'] = nativePastel(this['_color']);
+ return this;
+ }
+ pairedScheme(options?: PairedSchemeOptions): ColorArray {
+ // @ts-ignore
+ this['colors'] = nativePairedScheme(this['_color'], options);
+
+ return new ColorArray(this['colors']);
+ }
+ hueShift(options?: HueShiftOptions): ColorArray {
+ // @ts-ignore
+ this['colors'] = nativeHueShift(this['_color'], options);
+
+ return new ColorArray(this['colors']);
+ }
+ getComplimentaryHue(
+ mode?: HueColorSpaces,
+ colorObj?: boolean
+ ): { hue: HueFamily; color: ColorToken } | ColorToken {
+ this['_color'] = nativeGetComplimentaryHue(this['_color'], mode, colorObj);
+ return this['_color'];
+ }
+ earthtone(options?: EarthtoneOptions): ColorArray | ColorToken {
+ // @ts-ignore
+ this['colors'] = nativeEarthtone(this['_color'], checkArg(options, {}));
+
+ return this['colors'];
+ }
+ contrast(against: 'lightMode' | 'darkMode' | Color) {
+ let result: number;
+ switch (against) {
+ case 'lightMode':
+ result = getContrast(this['_color'], this['background']['lightMode']);
+
+ break;
+ case 'darkMode':
+ result = getContrast(this['_color'], this['background']['darkMode']);
+ break;
+ default:
+ result = getContrast(this['_color'], this['background']['custom']);
+ break;
+ }
+ return result;
+ }
+ luminance(amount?: number): number {
+ if (amount) {
+ this['_luminance'] = amount;
+ this['_color'] = setLuminance(this['_color'], this['_color']);
+ // @ts-ignore
+ return this;
+ }
+ return getLuminance(this['_color']);
+ }
+
+ output() {
+ return this['_color'];
+ }
+
+ saturation(amount?: string | number) {
+ this['_saturation'] = nativeGetChannel(
+ `${this['colorspace']}.${matchChromaChannel(this['colorspace'])}`
+ )(this['_color']);
+ if (amount) {
+ this['_color'] = nativeSetChannel(
+ `${this['colorspace']}.${matchChromaChannel(this['colorspace'])}`
+ )(this['_color'], amount);
+
+ return this;
+ } else {
+ return this['_saturation'];
+ }
+ }
+ isAchromatic() {
+ return nativeIsAchromatic(this['_color']);
+ }
+ isWarm() {
+ return nativeIsWarm(this['_color']);
+ }
+ isCool() {
+ return nativeIsCool(this['_color']);
+ }
+
+ /**
+ *
+ * Returns the color as a simulation of the passed in type of color vision deficiency with the deficiency filter's intensity determined by the severity value.
+ * @param deficiencyType The type of color vision deficiency. To avoid writing the long types, the expected parameters are simply the colors that are hard to perceive for the type of color blindness. For example those with 'tritanopia' are unable to perceive 'blue' light. Default is 'red' when the defeciency parameter is undefined or any falsy value.
+ * @see For a deep dive on color vision deficiency go to
+ * @param color The color to return its deficiency simulated variant.
+ * @param severity The intensity of the filter. The exepected value is between [0,1]. For example 0.5
+ * @returns The color as its simulated variant as a hexadecimal string.
+ * @example
+ *
+ * import { colorDeficiency, toHex } from 'huetiful-js'
+
+// Here we are simulating color blindness of tritanomaly or we can't see 'blue'.
+// We are passing in our color as an array of channel values in the mode "rgb". The severity is set to 0.1
+let tritanomaly = colorDeficiency('blue')
+console.log(tritanomaly(['rgb', 230, 100, 50, 0.5], 0.1))
+// #dd663680
+
+// Here we are simulating color blindness of tritanomaly or we can't see 'red'. The severity is not explicitly set so it defaults to 1
+let protanopia = colorDeficiency('red')
+console.log(protanopia({ h: 20, w: 50, b: 30, mode: 'hwb' }))
+// #9f9f9f
+ */
+ deficiency(
+ deficiencyType?: 'red' | 'blue' | 'green' | 'monochromacy',
+ severity = 1
+ ): ColorToken {
+ this['_color'] = nativeColorDeficiency(deficiencyType)(
+ this['_color'],
+ severity
+ );
+ return this;
+ }
+
+ getFarthestHue(colors: ColorToken[], colorObj?: boolean) {
+ return nativeGetFarthestHue(colors, this['colorspace'], colorObj);
+ }
+ getNearestHue(colors: ColorToken[], colorObj?: boolean) {
+ return nativeGetNearestHue(colors, this['colorspace'], colorObj);
+ }
+ getNearestChroma(colors: ColorToken[]) {
+ return nativeGetNearestChroma(colors, this['colorspace']);
+ }
+ getNearestLightness(colors: ColorToken[], colorObj?: boolean) {
+ return nativeGetNearestLightness(colors, this['colorspace'], colorObj);
+ }
+ getFarthestChroma(colors: ColorToken[], colorObj?: boolean) {
+ return nativeGetFarthestChroma(colors, colorObj);
+ }
+ getFarthestLightness(colors: ColorToken[], colorObj?: boolean) {
+ return nativeGetFarthestLightness(colors, this['colorspace'], colorObj);
+ }
+ ovetone() {
+ return nativeOvertone(this['_color']);
+ }
+ getHue() {
+ return nativeGetHue(this['_color']);
+ }
+ scheme(
+ scheme: 'analogous' | 'triadic' | 'tetradic' | 'complementary',
+ easingFunc?: (t: number) => number
+ ): ColorToken[] | ColorArray {
+ return load(nativeScheme(scheme)(this['_color'], easingFunc));
+ }
+}
+
+/**
+ * Wrapper function over the Color class that returns a new Color method chain.
+ * @param color The color token to bind.
+ * @returns A new Color class with all the utilities that take a single color as the first parameter bound to its prototype.
+ */
+function color(color: ColorToken) {
+ return new Color(color);
+}
+
+export {
+ diverging,
+ qualitative,
+ sequential,
+ colors,
+ tailwindColors,
+ ColorArray,
+ load,
+ Color,
+ color
+};
diff --git a/src/colors/achromatic.ts b/src/colors/achromatic.ts
deleted file mode 100644
index 32061959..00000000
--- a/src/colors/achromatic.ts
+++ /dev/null
@@ -1,70 +0,0 @@
-// @ts-nocheck
-import type { Color } from '../paramTypes.ts';
-import { getChannel } from '../getters_and_setters/get.ts';
-
-/**
- * @function
- * @description Checks if a color is achromatic(without hue or simply grayscale).
- * @param color The color to test if it is achromatic or not.
- * @returns boolean Returns true if the color is achromatic else false
- * @example
- *
- * import { isAchromatic } from "huetiful-js";
-import { formatHex8, interpolate, samples } from "culori"
-
-
-isAchromatic('pink')
-// false
-
-let sample = [
- "#164100",
- "#ffff00",
- "#310000",
- 'pink'
-];
-
-console.log(map(sample, isAchromatic));
-
-// [false, false, false,false]
-
-isAchromatic('gray')
-// Returns true
-
-
-console.log(map(sample, isAchromatic));
-
-
-// we create an interpolation using black and white
-let f = interpolate(["black", "white"]);
-
-//We then create 12 evenly spaced samples and pass them to f as the `t` param required by an interpolating function.
-// Lastly we convert the color to hex for brevity for this example (otherwise color objects work fine too.)
-let grays = map(samples(12), (c) => formatHex8(f(c)));
-console.log(map(grays, isAchromatic));
-
-//
- [ false, true, true,
- true, true, true,
- true, true, true,
- true, true, false
-]
-
- */
-const isAchromatic = (color: Color): boolean => {
- const cb = (mc: string) => getChannel(mc)(color);
-
- // Store the value of chroma and saturation channels.
- const checkHsl = cb('hsl.s');
- const checkLch = cb('lch.c');
-
- // 2) Check if the saturation channel is zero or falsy for color spaces with saturation/chroma channel
- // OR
- // 2)Check if the color's numerical represantation is within the defined ranges.
- if ((checkHsl || checkLch) === (undefined || null || 0)) {
- return true;
- } else {
- return false;
- }
-};
-
-export { isAchromatic };
diff --git a/src/colors/chroma.ts b/src/colors/chroma.ts
deleted file mode 100644
index 314650d3..00000000
--- a/src/colors/chroma.ts
+++ /dev/null
@@ -1,193 +0,0 @@
-// @ts-nocheck
-// This module contains minChroma,maxChroma,getFarthestChroma,getNearestChroma
-
-import { Color, HueColorSpaces, Factor } from '../paramTypes';
-import { getChannel } from '../getters_and_setters/get.ts';
-import { matchChromaChannel } from '../fp/string/matchChromaChannel.ts';
-import { sortedArr } from '../fp/array/sortedArr.ts';
-
-// I must test if the passed in mode has a chroma/saturation channel. Should I use RegExp ?
-
-// The callback to invoke per color in the passed in collection.
-// The subtrahend is each color in the collection
-//This means that the color object with the smallest chroma value is the nearest chroma.
-// First check which value is greater and then act accordingly. Refactor hue.ts so that it returns negative
-const chromaDiff =
- (color: Color, colorSpace: HueColorSpaces | string) =>
- (subtrahend: Color) => {
- const cs = matchChromaChannel(colorSpace);
-
- if (getChannel(cs)(color) < getChannel(cs)(subtrahend)) {
- return getChannel(cs)(subtrahend) - getChannel(cs)(color);
- } else {
- return getChannel(cs)(color) - getChannel(cs)(subtrahend);
- }
- };
-
-// If the predicate returns undefined or false on the chroma channel then it means that it is an achromatic color.
-// Callback func for the minHue and maxHue utils. The funny thing is that most of the code is similar with minor changes here and there
-const predicate = (colorSpace: HueColorSpaces) => (color: Color) =>
- getChannel(matchChromaChannel(colorSpace))(color) || undefined;
-
-/**
- *
- * @function
- * @description Returns the smallest chroma/saturation difference between the passed in color and each element in the colors collection. Achromatic colors are excluded from the final result array. Use isAchromatic with Array.map to remove grays from your color collection.
- * @param color The color to use its chroma/saturation value as the minuend.
- * @param colors The collection of colors to compare against.
- * @param colorSpace The mode color space to perform the computation in.
- * @returns The chroma/saturation value from the color with the smallest chroma distance. If the colors are achromatic, it returns undefined.
- * @example
- *
- * import { getFarthestChroma } from 'huetiful-js'
-
-let sample = ['b2c3f1', '#a1bd2f', '#f3bac1']
-
-console.log(getFarthestChroma('lime', sample, 'lch'))
-// 90.87480913244802
- */
-const getNearestChroma = (
- color: Color,
- colors: Color[],
- colorSpace?: HueColorSpaces
-): number => {
- // The factor being investigated.
-
- const factor: Factor = 'saturation';
- const cb = chromaDiff(color, colorSpace || 'lch');
- const sortedObjArr = sortedArr(
- factor,
- cb,
- 'asc',
- true
- )(colors).filter((el) => el[factor] !== undefined);
-
- return sortedObjArr[0][factor];
-};
-
-/**
- *
- * @function
- * @description Returns the largest chroma/saturation difference between the passed in color and each element in the colors collection. Achromatic colors are excluded from the final result array. Use isAchromatic with Array.map to remove grays from your color collection.
- * @param color The color to use its chroma/saturation value as the minuend.
- * @param colors The collection of colors to compare against.
- * @param colorSpace The mode color space to perform the computation in.
- * @returns The chroma/saturation value from the color with the largest saturation distance. If the colors are achromatic, it returns undefined.
- * @example
- *
- * import { getFarthestChroma } from 'huetiful-js'
-
-let sample = ['b2c3f1', '#a1bd2f', '#f3bac1']
-
-console.log(getFarthestChroma('lime', sample, 'lch'))
-// 90.87480913244802
- */
-
-const getFarthestChroma = (
- color: Color,
- colors: Color[],
- colorSpace?: HueColorSpaces
-): number => {
- // The factor being investigated.
-
- const factor: Factor = 'saturation';
- const cb = chromaDiff(color, colorSpace || 'lch');
- const sortedObjArr = sortedArr(
- factor,
- cb,
- 'desc',
- true
- )(colors).filter((el) => el[factor] !== undefined);
-
- return sortedObjArr[0][factor];
-};
-
-/**
- *@function
- * @description Gets the smallest chroma/saturation value from the passed in colors.
- * @param colors The array of colors to query the color with the smallest chroma/saturation value.
- * @param colorSpace The mode color space to perform the computation in.
- * @param colorObj Optional boolean that makes the function return a custom object with factor (saturation) and name of the color as keys. Default is false.
- * @returns The smallest chroma/saturation value in the colors passed in or a custom object.
- * @example
- *
- * import { minChroma } from 'huetiful-js'
-
-let sample = ['b2c3f1', '#a1bd2f', '#f3bac1']
-
-console.log(minChroma(sample, 'lch'))
-// 22.45669293295522
- */
-const minChroma = (
- colors: Color[],
- colorSpace?: HueColorSpaces,
- colorObj = false
-): number | { factor: number; color: Color } => {
- // The factor being investigated.
-
- const factor: Factor = 'saturation';
- const result: Array<{ factor: number; name: Color }> = sortedArr(
- factor,
- predicate(colorSpace || 'lch'),
- 'asc',
- true
- )(colors).filter((el) => el[factor] !== undefined);
-
- let value: number | { factor: number; name: Color };
-
- if (result.length > 0) {
- if (colorObj) {
- value = result[0];
- } else {
- value = result[0][factor];
- }
- }
-
- return value;
-};
-
-/**
- *@function
- * @description Gets the largest saturation value from the passed in colors.
- * @param colors The array of colors to query the color with the largest saturation value.
- * @param colorSpace The mode color space to perform the computation in.
- * @param colorObj Optional boolean that makes the function return a custom object with factor (saturation) and name of the color as keys. Default is false.
- * @returns The largest saturation value in the colors passed in or a custom object.
- * @example
- *
- * import { maxChroma } from 'huetiful-js'
-
-let sample = ['b2c3f1', '#a1bd2f', '#f3bac1']
-
-console.log(maxChroma(sample, 'lch'))
-// 67.22120855010492
- */
-const maxChroma = (
- colors: Color[],
- colorSpace?: HueColorSpaces,
- colorObj = false
-): number | { factor: number; color: Color } => {
- // The factor being investigated.
-
- const factor: Factor = 'saturation';
- const result: Array<{ factor: number; name: Color }> = sortedArr(
- factor,
- predicate(colorSpace || 'lch'),
- 'desc',
- true
- )(colors).filter((el) => el[factor] !== undefined);
-
- let value: { factor: number; name: Color } | number;
-
- if (result.length > 0) {
- if (colorObj) {
- value = result[0];
- } else {
- value = result[0][factor];
- }
- }
-
- return value;
-};
-
-export { getFarthestChroma, getNearestChroma, maxChroma, minChroma };
diff --git a/src/colors/colorBrewer.ts b/src/colors/colorBrewer.ts
deleted file mode 100644
index a8ec8189..00000000
--- a/src/colors/colorBrewer.ts
+++ /dev/null
@@ -1,526 +0,0 @@
-//@ts-nocheck
-import type {
- SequentialScheme,
- DivergingScheme,
- QualitativeScheme,
- Color
-} from '../paramTypes';
-
-// Convert all the keys to lowercase
-const cb = (str: string) => str.toLowerCase();
-
-//Check if the scheme object has the passed in scheme
-const schemeMapper = (scheme: string, schemesObject: object): Color[] => {
- const { keys } = Object;
- // Map all schemes keys to lower case
- const schemeOptions = keys(schemesObject).map(cb);
-
- scheme = cb(scheme);
- // Check if passed in scheme is available
- if (schemeOptions.indexOf(scheme) > -1) {
- return schemesObject[scheme];
- } else {
- // Else throw error:Invalid scheme
- throw Error(`${scheme} is an invalid scheme option.`);
- }
-};
-
-/**
- * @function
- * @description A wrapper function for ColorBrewer's map of sequential color schemes.
- * @param scheme The name of the scheme
- * @returns An array of colors in hex represantation.
- * @example
- * import { sequential } from 'huetiful-js
-
-
-console.log(sequential("OrRd"))
-
-// [
- '#fff7ec', '#fee8c8',
- '#fdd49e', '#fdbb84',
- '#fc8d59', '#ef6548',
- '#d7301f', '#b30000',
- '#7f0000'
-]
-
-
-
- */
-const sequential = (scheme: SequentialScheme): Color[] => {
- const schemes = {
- OrRd: [
- '#fff7ec',
- '#fee8c8',
- '#fdd49e',
- '#fdbb84',
- '#fc8d59',
- '#ef6548',
- '#d7301f',
- '#b30000',
- '#7f0000'
- ],
- PuBu: [
- '#fff7fb',
- '#ece7f2',
- '#d0d1e6',
- '#a6bddb',
- '#74a9cf',
- '#3690c0',
- '#0570b0',
- '#045a8d',
- '#023858'
- ],
- BuPu: [
- '#f7fcfd',
- '#e0ecf4',
- '#bfd3e6',
- '#9ebcda',
- '#8c96c6',
- '#8c6bb1',
- '#88419d',
- '#810f7c',
- '#4d004b'
- ],
- Oranges: [
- '#fff5eb',
- '#fee6ce',
- '#fdd0a2',
- '#fdae6b',
- '#fd8d3c',
- '#f16913',
- '#d94801',
- '#a63603',
- '#7f2704'
- ],
- BuGn: [
- '#f7fcfd',
- '#e5f5f9',
- '#ccece6',
- '#99d8c9',
- '#66c2a4',
- '#41ae76',
- '#238b45',
- '#006d2c',
- '#00441b'
- ],
- YlOrBr: [
- '#ffffe5',
- '#fff7bc',
- '#fee391',
- '#fec44f',
- '#fe9929',
- '#ec7014',
- '#cc4c02',
- '#993404',
- '#662506'
- ],
- YlGn: [
- '#ffffe5',
- '#f7fcb9',
- '#d9f0a3',
- '#addd8e',
- '#78c679',
- '#41ab5d',
- '#238443',
- '#006837',
- '#004529'
- ],
- Reds: [
- '#fff5f0',
- '#fee0d2',
- '#fcbba1',
- '#fc9272',
- '#fb6a4a',
- '#ef3b2c',
- '#cb181d',
- '#a50f15',
- '#67000d'
- ],
- RdPu: [
- '#fff7f3',
- '#fde0dd',
- '#fcc5c0',
- '#fa9fb5',
- '#f768a1',
- '#dd3497',
- '#ae017e',
- '#7a0177',
- '#49006a'
- ],
- Greens: [
- '#f7fcf5',
- '#e5f5e0',
- '#c7e9c0',
- '#a1d99b',
- '#74c476',
- '#41ab5d',
- '#238b45',
- '#006d2c',
- '#00441b'
- ],
- YlGnBu: [
- '#ffffd9',
- '#edf8b1',
- '#c7e9b4',
- '#7fcdbb',
- '#41b6c4',
- '#1d91c0',
- '#225ea8',
- '#253494',
- '#081d58'
- ],
- Purples: [
- '#fcfbfd',
- '#efedf5',
- '#dadaeb',
- '#bcbddc',
- '#9e9ac8',
- '#807dba',
- '#6a51a3',
- '#54278f',
- '#3f007d'
- ],
- GnBu: [
- '#f7fcf0',
- '#e0f3db',
- '#ccebc5',
- '#a8ddb5',
- '#7bccc4',
- '#4eb3d3',
- '#2b8cbe',
- '#0868ac',
- '#084081'
- ],
- Greys: [
- '#ffffff',
- '#f0f0f0',
- '#d9d9d9',
- '#bdbdbd',
- '#969696',
- '#737373',
- '#525252',
- '#252525',
- '#000000'
- ],
- YlOrRd: [
- '#ffffcc',
- '#ffeda0',
- '#fed976',
- '#feb24c',
- '#fd8d3c',
- '#fc4e2a',
- '#e31a1c',
- '#bd0026',
- '#800026'
- ],
- PuRd: [
- '#f7f4f9',
- '#e7e1ef',
- '#d4b9da',
- '#c994c7',
- '#df65b0',
- '#e7298a',
- '#ce1256',
- '#980043',
- '#67001f'
- ],
- Blues: [
- '#f7fbff',
- '#deebf7',
- '#c6dbef',
- '#9ecae1',
- '#6baed6',
- '#4292c6',
- '#2171b5',
- '#08519c',
- '#08306b'
- ],
- PuBuGn: [
- '#fff7fb',
- '#ece2f0',
- '#d0d1e6',
- '#a6bddb',
- '#67a9cf',
- '#3690c0',
- '#02818a',
- '#016c59',
- '#014636'
- ],
- Viridis: [
- '#440154',
- '#482777',
- '#3f4a8a',
- '#31678e',
- '#26838f',
- '#1f9d8a',
- '#6cce5a',
- '#b6de2b',
- '#fee825'
- ]
- };
-
- return schemeMapper(scheme, schemes);
-};
-
-/**
- * @function
- * @description A wrapper function for ColorBrewer's map of diverging color schemes.
- * @param scheme The name of the scheme.
- * @returns An array of colors in hex represantation.
- * @example
- *
- * import { diverging } from 'huetiful-js'
-
-
-
-console.log(diverging("Spectral"))
-//[
- '#7fc97f', '#beaed4',
- '#fdc086', '#ffff99',
- '#386cb0', '#f0027f',
- '#bf5b17', '#666666'
-]
- */
-
-const diverging = (scheme: DivergingScheme): Color[] => {
- const schemes = {
- Spectral: [
- '#9e0142',
- '#d53e4f',
- '#f46d43',
- '#fdae61',
- '#fee08b',
- '#ffffbf',
- '#e6f598',
- '#abdda4',
- '#66c2a5',
- '#3288bd',
- '#5e4fa2'
- ],
- RdYlGn: [
- '#a50026',
- '#d73027',
- '#f46d43',
- '#fdae61',
- '#fee08b',
- '#ffffbf',
- '#d9ef8b',
- '#a6d96a',
- '#66bd63',
- '#1a9850',
- '#006837'
- ],
- RdBu: [
- '#67001f',
- '#b2182b',
- '#d6604d',
- '#f4a582',
- '#fddbc7',
- '#f7f7f7',
- '#d1e5f0',
- '#92c5de',
- '#4393c3',
- '#2166ac',
- '#053061'
- ],
- PiYG: [
- '#8e0152',
- '#c51b7d',
- '#de77ae',
- '#f1b6da',
- '#fde0ef',
- '#f7f7f7',
- '#e6f5d0',
- '#b8e186',
- '#7fbc41',
- '#4d9221',
- '#276419'
- ],
- PRGn: [
- '#40004b',
- '#762a83',
- '#9970ab',
- '#c2a5cf',
- '#e7d4e8',
- '#f7f7f7',
- '#d9f0d3',
- '#a6dba0',
- '#5aae61',
- '#1b7837',
- '#00441b'
- ],
- RdYlBu: [
- '#a50026',
- '#d73027',
- '#f46d43',
- '#fdae61',
- '#fee090',
- '#ffffbf',
- '#e0f3f8',
- '#abd9e9',
- '#74add1',
- '#4575b4',
- '#313695'
- ],
- BrBG: [
- '#543005',
- '#8c510a',
- '#bf812d',
- '#dfc27d',
- '#f6e8c3',
- '#f5f5f5',
- '#c7eae5',
- '#80cdc1',
- '#35978f',
- '#01665e',
- '#003c30'
- ],
- RdGy: [
- '#67001f',
- '#b2182b',
- '#d6604d',
- '#f4a582',
- '#fddbc7',
- '#ffffff',
- '#e0e0e0',
- '#bababa',
- '#878787',
- '#4d4d4d',
- '#1a1a1a'
- ],
- PuOr: [
- '#7f3b08',
- '#b35806',
- '#e08214',
- '#fdb863',
- '#fee0b6',
- '#f7f7f7',
- '#d8daeb',
- '#b2abd2',
- '#8073ac',
- '#542788',
- '#2d004b'
- ]
- };
-
- return schemeMapper(scheme, schemes);
-};
-
-/**
- * @function
- * @description A wrapper function for ColorBrewer's map of qualitative color schemes.
- * @param scheme The name of the scheme
- * @returns An array of colors in hex represantation.
- * @example
- *
- * import { qualitative } from 'huetiful-js'
-
-
-console.log(qualitative("Accent"))
-// [
- '#7fc97f', '#beaed4',
- '#fdc086', '#ffff99',
- '#386cb0', '#f0027f',
- '#bf5b17', '#666666'
-]
-
- */
-
-const qualitative = (scheme: QualitativeScheme): Color[] => {
- const schemes = {
- Set2: [
- '#66c2a5',
- '#fc8d62',
- '#8da0cb',
- '#e78ac3',
- '#a6d854',
- '#ffd92f',
- '#e5c494',
- '#b3b3b3'
- ],
- Accent: [
- '#7fc97f',
- '#beaed4',
- '#fdc086',
- '#ffff99',
- '#386cb0',
- '#f0027f',
- '#bf5b17',
- '#666666'
- ],
- Set1: [
- '#e41a1c',
- '#377eb8',
- '#4daf4a',
- '#984ea3',
- '#ff7f00',
- '#ffff33',
- '#a65628',
- '#f781bf',
- '#999999'
- ],
- Set3: [
- '#8dd3c7',
- '#ffffb3',
- '#bebada',
- '#fb8072',
- '#80b1d3',
- '#fdb462',
- '#b3de69',
- '#fccde5',
- '#d9d9d9',
- '#bc80bd',
- '#ccebc5',
- '#ffed6f'
- ],
- Dark2: [
- '#1b9e77',
- '#d95f02',
- '#7570b3',
- '#e7298a',
- '#66a61e',
- '#e6ab02',
- '#a6761d',
- '#666666'
- ],
- Paired: [
- '#a6cee3',
- '#1f78b4',
- '#b2df8a',
- '#33a02c',
- '#fb9a99',
- '#e31a1c',
- '#fdbf6f',
- '#ff7f00',
- '#cab2d6',
- '#6a3d9a',
- '#ffff99',
- '#b15928'
- ],
- Pastel2: [
- '#b3e2cd',
- '#fdcdac',
- '#cbd5e8',
- '#f4cae4',
- '#e6f5c9',
- '#fff2ae',
- '#f1e2cc',
- '#cccccc'
- ],
- Pastel1: [
- '#fbb4ae',
- '#b3cde3',
- '#ccebc5',
- '#decbe4',
- '#fed9a6',
- '#ffffcc',
- '#e5d8bd',
- '#fddaec',
- '#f2f2f2'
- ]
- };
- return schemeMapper(scheme, schemes);
-};
-
-export { diverging, qualitative, sequential };
diff --git a/src/colors/colors.ts b/src/colors/colors.ts
deleted file mode 100644
index 5166c4b5..00000000
--- a/src/colors/colors.ts
+++ /dev/null
@@ -1,67 +0,0 @@
-//@ts-nocheck
-import shades from '../color-maps/swatches/tailwind.ts';
-import type { HueMap, ScaleValues, Color } from '../paramTypes.ts';
-
-/**
- * @function
- * @description A wrapper function for the default Tailwind palette. If called with both parameters it return the hex code at the specified shade and value. Else, if called with the shade parameter as "all" it will return all colors from the shades in the palette map at the specified value (if value is undefined it will default to "500"). When called with the shade parameter only it will return all the colors from 100 to 900 of the specified shade.
- * @param shade Any shade in the default TailwindCSS palette e.g amber,blue.
- * @param val Any value from 100 to 900 in increments of 100 e.g "200".
- * @returns color Returns a hex code string or array of hex codes depending on how the function is called.
- * @example
- *
- * import { colors } from "huetiful-js";
-
-let all300 = colors("all", 300);
-
-console.log(all300)
-//[
- '#cbd5e1', '#d1d5db', '#d4d4d8',
- '#d4d4d4', '#d6d3d1', '#fca5a5',
- '#fdba74', '#fcd34d', '#fde047',
- '#bef264', '#86efac', '#6ee7b7',
- '#5eead4', '#7dd3fc', '#93c5fd',
- '#c4b5fd', '#d8b4fe', '#f0abfc',
- '#f9a8d4', '#fda4af'
-]
-
-let red = colors("red");
-console.log(red);
-
-// [
- '#fef2f2', '#fee2e2',
- '#fecaca', '#fca5a5',
- '#f87171', '#ef4444',
- '#dc2626', '#b91c1c',
- '#991b1b', '#7f1d1d'
-]
-
-let red100 = colors("red", 100);
-
-console.log(red100)
-// #fee2e2
- */
-const colors = (shade: keyof HueMap, val: ScaleValues): Color | Color[] => {
- const { keys } = Object;
- const defaultHue = 'all';
- const hueKeys = keys(shades);
-
- shade = shade.toLowerCase();
- // First do an AND check on hue and val params. If true return the hue at the specified value.
- // If only the hue is defined return the whole array of hex codes for that color.
-
- // If only the value is defined return that color value for every hue.
-
- if (shade === defaultHue) {
- return shades.map((color) => color[val || '500']);
- } else if (hueKeys.some((hue) => hue === shade) && val) {
- return shades[shade][val];
- } else if (shade && typeof val == 'undefined') {
- const keyVals = keys(shades[shade]);
- return keyVals.map((key) => shades[shade][key]);
- } else if (typeof shade && typeof val == 'undefined') {
- throw Error(`Both shade and value cannot be undefined`);
- }
-};
-
-export { colors };
diff --git a/src/colors/hue.ts b/src/colors/hue.ts
deleted file mode 100644
index 37915613..00000000
--- a/src/colors/hue.ts
+++ /dev/null
@@ -1,189 +0,0 @@
-//This module contains getNearestHue,getFarthestHue,minHue and maxHue which are collection based utils that return the color with the queried factor.
-// @ts-nocheck
-
-import { getChannel } from '../getters_and_setters/get.ts';
-import { sortedArr } from '../fp/array/sortedArr.ts';
-import type { Color, HueColorSpaces, Factor } from '../paramTypes';
-
-// Globals
-
-const { abs } = Math;
-
-const factor: Factor = 'hue';
-const mode = (colorSpace: HueColorSpaces): string => `${colorSpace || 'lch'}.h`;
-// The hue value of our color which we are using for comparison
-const targetHue = (color: Color, colorSpace: HueColorSpaces): number =>
- getChannel(mode(colorSpace))(color);
-
-// The callback to invoke per color in the passed in collection.
-// Return the absolute value since hue is a cyclic value which can either be in clockwise/anti-clockwise.
-//This means that the color object with the smallest hue value is the nearest color/hue.
-const cb =
- (color: Color, colorSpace: HueColorSpaces | string) =>
- (subtrahend: Color) => {
- return abs(
- targetHue(color, colorSpace) - getChannel(mode(colorSpace))(subtrahend)
- );
- };
-
-// Callback func for the minHue and maxHue utils. The funny thing is that most of the code is similar with minor changes here and there
-const predicate = (colorSpace: HueColorSpaces) => (color: Color) => {
- return getChannel(mode(colorSpace))(color) || undefined;
-};
-
-/**
- *
- * @function
- * @description Returns the smallest hue difference between the passed in color and each element in the colors collection. Achromatic colors are excluded from the final result array. Use isAchromatic to remove grays from your color collection as a predicate to Array.map()
- * @param color The color to use its hue value as the minuend.
- * @param colors The collection of colors to compare against.
- * @param colorSpace The mode color space to perform the computation in.
- * @returns The hue value from the color with the smallest hue distance. If the colors are achromatic, it returns undefined.
- * @example
- *
- * import { getNearestHue } from 'huetiful-js'
-
-let sample = ['b2c3f1', '#a1bd2f', '#f3bac1']
-
-console.log(getNearestHue('lime', sample, 'lch'))
-
-// 23.962083662849253
- */
-const getNearestHue = (
- color: Color,
- colors: Color[],
- colorSpace?: HueColorSpaces
-): number => {
- return sortedArr(
- factor,
- cb(color, mode(colorSpace)),
- 'asc',
- true
- )(colors).filter((el) => el[factor] !== undefined)[0][factor];
-};
-
-/**
- *
- * @function
- * @description Returns the largest hue difference between the passed in color and each element in the colors collection. Achromatic colors are excluded from the final result array. Use isAchromatic to remove grays from your color collection as a predicate to Array.map()
- * @param color The color to use its hue value as the minuend.
- * @param colors The collection of colors to compare against.
- * @param colorSpace The mode color space to perform the computation in.
- * @returns The hue value from the color with the largest hue distance. If the colors are achromatic, it returns undefined.
- *@example
-import { getFarthestHue } from 'huetiful-js'
-
- let sample = [
- '#00ffdc',
- '#00ff78',
- '#00c000',
- '#007e00',
- '#164100',
- '#ffff00',
- '#310000',
- '#3e0000',
- '#4e0000',
- '#600000',
- '#720000',
-]
-
-console.log(getFarthestHue('lime', sample, 'lch'))
-// 139.16534433552653
-
-*/
-
-const getFarthestHue = (
- color: Color,
- colors: Color[],
- colorSpace?: HueColorSpaces
-): number => {
- return sortedArr(
- factor,
- cb(color, mode(colorSpace)),
- 'desc',
- true
- )(colors).filter((el) => el[factor] !== undefined)[0][factor];
-};
-
-/**
- *@function
- * @description Gets the smallest hue value from the passed in colors.
- * @param colors The array of colors to query the color with the smallest hue value.
- * @param colorSpace The mode color space to perform the computation in.
- * @param colorObj Optional boolean that makes the function return a custom object with factor (hue) and name of the color as keys. Default is false.
- * @returns The smallest hue value in the colors passed in or a custom object.
- * @example
- *
- * import { minHue } from 'huetiful-js'
-
-let sample = ['b2c3f1', '#a1bd2f', '#f3bac1']
-
-console.log(minHue(sample, 'lch'))
-// 12.462831644544274
- */
-const minHue = (
- colors: Color[],
- colorSpace?: HueColorSpaces,
- colorObj = false
-): number | { factor: number; color: Color } => {
- const result: Array<{ factor: number; name: Color }> = sortedArr(
- factor,
- predicate(colorSpace),
- 'asc',
- true
- )(colors).filter((el) => el[factor] !== undefined);
-
- let value;
-
- if (result.length > 0) {
- if (colorObj) {
- value = result[0];
- } else {
- value = result[0][factor];
- }
- }
-
- return value;
-};
-
-/**
- *@function
- * @description Gets the largest hue value from the passed in colors.
- * @param colors The array of colors to query the color with the largest hue value.
- * @param colorSpace The mode color space to perform the computation in.
- * @param colorObj Optional boolean that makes the function return a custom object with factor (hue) and name of the color as keys. Default is false.
- * @returns The largest hue value in the colors passed in or a custom object.
- * @example
- *
- * import { maxHue } from 'huetiful-js'
-let sample = ['b2c3f1', '#a1bd2f', '#f3bac1']
-
-console.log(maxHue(sample, 'lch'))
-// 273.54920266436477
- */
-const maxHue = (
- colors: Color[],
- colorSpace?: HueColorSpaces,
- colorObj = false
-): number | { factor: number; color: Color } => {
- const result: Array<{ factor: number; name: Color }> = sortedArr(
- factor,
- predicate(colorSpace),
- 'desc',
- true
- )(colors).filter((el) => el[factor] !== undefined);
-
- let value;
-
- if (result.length > 0) {
- if (colorObj) {
- value = result[0];
- } else {
- value = result[0][factor];
- }
- }
-
- return value;
-};
-
-export { getFarthestHue, getNearestHue, maxHue, minHue };
diff --git a/src/colors/index.ts b/src/colors/index.ts
deleted file mode 100644
index dbde2c79..00000000
--- a/src/colors/index.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-export {
- getFarthestChroma,
- getNearestChroma,
- maxChroma,
- minChroma
-} from './chroma';
-export { diverging, qualitative, sequential } from './colorBrewer';
-export { colors } from './colors';
-export {
- getFarthestLightness,
- getNearestLightness,
- maxLightness,
- minLightness
-} from './lightness';
-export { getFarthestHue, getNearestHue, maxHue, minHue } from './hue';
-export { overtone } from './overtone';
-export { tailwindColors } from './tailwindColors';
-export { isCool, isWarm, maxTemp, minTemp } from './temperature';
-export { isAchromatic } from './achromatic';
diff --git a/src/colors/lightness.ts b/src/colors/lightness.ts
deleted file mode 100644
index 578aa2e8..00000000
--- a/src/colors/lightness.ts
+++ /dev/null
@@ -1,166 +0,0 @@
-// @ts-nocheck
-
-import { getChannel } from '../getters_and_setters/get.ts';
-import { sortedArr } from '../fp/array/sortedArr.ts';
-import type { Color, Factor } from '../paramTypes';
-
-const lightness = 'lab.l';
-
-// The subtrahend is each color in the collection
-//This means that the color object with the smallest lightness value is the nearest lightness.
-// First check which value is greater and then act accordingly. Refactor hue.ts so that it returns negative
-const lightnessDiff = (color: Color) => (subtrahend: Color) => {
- if (getChannel(lightness)(color) < getChannel(lightness)(subtrahend)) {
- return getChannel(lightness)(subtrahend) - getChannel(lightness)(color);
- } else {
- return getChannel(lightness)(color) - getChannel(lightness)(subtrahend);
- }
-};
-
-/**
- *
- * @function
- * @description Returns the smallest lightness difference between the passed in color and each element in the colors collection. Alightnesstic colors are excluded from the final result array. Use isAlightnesstic with Array.map to remove grays from your color collection.
- * @param color The color to use its lightness value as the minuend.
- * @param colors The collection of colors to compare against.
- * @returns The lightness value from the color with the smallest lightness distance. If the colors are alightnesstic, it returns undefined.
- * @example
- *
- * import { getNearestLightness } from 'huetiful-js'
-
-let sample = ["b2c3f1", "#a1bd2f", "#f3bac1"]
-
-console.log(getNearestLightness("green", sample))
-
-//26.338769793418493
- *
- */
-const getNearestLightness = (color: Color, colors: Color[]): number => {
- // The factor being investigated.
-
- const factor: Factor = 'lightness';
- const cb = lightnessDiff(color);
- const sortedObjArr = sortedArr(factor, cb, 'asc', true)(colors);
- return sortedObjArr[0][factor];
-};
-
-/**
- *
- * @function
- * @description Returns the largest lightness difference between the passed in color and each element in the colors collection. Alightnesstic colors are excluded from the final result array. Use isAlightnesstic with Array.map to remove grays from your color collection.
- * @param color The color to use its lightness value as the minuend.
- * @param colors The collection of colors to compare against.
- * @returns The lightness value from the color with the largest lightness distance.
- * @example
- *
- * import { getFarthestLightness } from 'huetiful-js'
-
-let sample = ["b2c3f1", "#a1bd2f", "#f3bac1"]
-
-console.log(getFarthestLightness("green", sample))
-
-// 34.668980006120606
-
- */
-
-const getFarthestLightness = (color: Color, colors: Color[]): number => {
- // The factor being investigated.
-
- const factor: Factor = 'lightness';
- const cb = lightnessDiff(color);
- const sortedObjArr = sortedArr(factor, cb, 'desc', true)(colors);
- return sortedObjArr[0][factor];
-};
-
-/**
- *@function
- * @description Gets the smallest lightness value from the passed in colors.
- * @param colors The array of colors to query the color with the smallest lightness value.
- * @param colorObj Optional boolean that makes the function return a custom object with factor (lightness) and name of the color as keys. Default is false.
- * @returns The smallest lightness value in the colors passed in or a custom object.
- * @example
- *
- * import { minLightness } from 'huetiful-js'
-
-let sample = ["b2c3f1", "#a1bd2f", "#f3bac1"]
-
-console.log(minLightness(sample, true))
-
-// { lightness: 72.61647882089876, name: '#a1bd2f' }
-
- */
-const minLightness = (
- colors: Color[],
- colorObj = false
-): number | { factor: number; color: Color } => {
- // The factor being investigated.
-
- const factor: Factor = 'lightness';
- const cb = getChannel(lightness);
- const result: Array<{ factor: number; name: Color }> = sortedArr(
- factor,
- cb,
- 'asc',
- true
- )(colors);
- let value: number | { factor: number; name: Color };
-
- if (gt(result.length, 0)) {
- if (colorObj) {
- value = result[0];
- } else {
- value = result[0][factor];
- }
- }
- return value;
-};
-
-/**
- *@function
- * @description Gets the largest lightness value from the passed in colors.
- * @param colors The array of colors to query the color with the largest lightness value.
- * @param colorObj Optional boolean that makes the function return a custom object with factor (lightness) and name of the color as keys. Default is false.
- * @returns The largest lightness value in the colors passed in or a custom object.
- * @example
- *
- * import { maxLightness } from 'huetiful-js'
-
-let sample = ["b2c3f1", "#a1bd2f", "#f3bac1"]
-
-console.log(maxLightness(sample, true))
-
-// { lightness: 80.94668903360088, name: '#f3bac1' }
-
- */
-const maxLightness = (
- colors: Color[],
- colorObj = false
-): number | { factor: number; color: Color } => {
- // The factor being investigated.
-
- const factor: Factor = 'lightness';
- const cb = getChannel(lightness);
- const result: Array<{ factor: number; name: Color }> = sortedArr(
- factor,
- cb,
- 'desc',
- true
- )(colors);
- let value: number | { factor: number; name: Color };
-
- if (gt(result.length, 0)) {
- if (colorObj) {
- value = result[0];
- } else {
- value = result[0][factor];
- }
- }
- return value;
-};
-
-export {
- getFarthestLightness,
- getNearestLightness,
- maxLightness,
- minLightness
-};
diff --git a/src/colors/overtone.ts b/src/colors/overtone.ts
deleted file mode 100644
index f1dcd2c6..00000000
--- a/src/colors/overtone.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-//@ts-nocheck
-import { getChannel } from '../getters_and_setters/get.ts';
-import hueTempMap from '../color-maps/samples/hueTemperature';
-import { isAchromatic } from './achromatic.ts';
-import { customFindKey } from '../fp/object/customFindKey.ts';
-import type { Color } from '../paramTypes.ts';
-
-/**
- * @function
- * @description Returns the hue which is biasing the passed in color
- * @param color The color to query its overtone.
- * @returns The name of the overtone hue. If an achromatic color is passed in it return the string gray otherwise if the color has no bias it returns false.
- * @example
- *
- * import { overtone } from "huetiful-js";
- *
-console.log(overtone("fefefe"))
-// 'gray'
-
-console.log(overtone("cyan"))
-// 'green'
-
-console.log(overtone("blue"))
-// false
- */
-const overtone = (color: Color): string | boolean => {
- const factor = getChannel('lch.h')(color);
- let hue = customFindKey(hueTempMap, factor);
-
- // We check if the color can be found in the defined ranges
-
- if (isAchromatic(color)) {
- return 'gray';
- } else if (/-/.test(hue)) {
- hue = hue.split('-');
- return hue[1];
- } else {
- return false;
- }
-};
-
-export { overtone };
diff --git a/src/colors/tailwindColors.ts b/src/colors/tailwindColors.ts
deleted file mode 100644
index adf4dd69..00000000
--- a/src/colors/tailwindColors.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-//@ts-nocheck
-
-import tailwindHues from '../color-maps/swatches/tailwind';
-import type { HueMap, ScaleValues } from '../paramTypes.ts';
-
-/**
- * @function
- * @description Wrapper function that returns TailwindCSS color value(s) of the specified shade. If invoked with no parameters it returns an array of colors from 100 to 900. If invoked with parameter will return the specified shade vale,
- * @param val The tone value of the shade. Values are in incrementals of 100. Both numeric (100) and its string equivalent ('100') are valid.
- * @returns color A hex string value or array of hex strings.
- * @example
- *
- * import { tailwindColors } from "huetiful-js";
-
-// We pass in red as the target hue.
-// It returns a function that can be called with an optional value parameter
-let red = tailwindColors("red");
-console.log(red());
-// [
- '#fef2f2', '#fee2e2',
- '#fecaca', '#fca5a5',
- '#f87171', '#ef4444',
- '#dc2626', '#b91c1c',
- '#991b1b', '#7f1d1d'
-]
-
-
-console.log(red(100));
-// '#fee2e2'
-
-console.log(red('900'));
-// '#7f1d1d'
-
-
- *
- */
-const tailwindColors =
- (shade: keyof HueMap) =>
- (val?: ScaleValues): string | string[] => {
- // This is a curried func that takes in the shade and returns a function that takes in a value from 100 thru 900
- shade = shade.toLowerCase();
- const { keys } = Object;
- // We check if the shade is a valid Tailwind shade if not we return pure black.
- let targetHue: object;
-
- if (keys(tailwindHues).indexOf(shade) != -1) {
- targetHue = tailwindHues[shade];
- } else {
- throw Error(
- `${shade} is not a valid shade in the default Tailwind palette`
- );
- }
-
- if (typeof val === 'undefined') {
- return keys(targetHue).map((value) => targetHue[value]);
- } else if (keys(targetHue).indexOf(val) > -1) {
- return targetHue[val];
- } else {
- throw Error(
- `${val} is not a valid scale value. Values are in increments of 100 up to 900 e.g "200"`
- );
- }
- };
-
-export { tailwindColors };
diff --git a/src/colors/temperature.ts b/src/colors/temperature.ts
deleted file mode 100644
index 68386c1a..00000000
--- a/src/colors/temperature.ts
+++ /dev/null
@@ -1,165 +0,0 @@
-//@ts-nocheck
-
-import { getChannel } from '../getters_and_setters/get.ts';
-import hueTempMap from '../color-maps/samples/hueTemperature';
-import { getTemp } from '../converters/getTemp.ts';
-import { floorCeil } from '../fp/number/floorCeil.ts';
-import { inRange } from '../fp/number/inRange.ts';
-import { min, max } from '../fp/array/min_max.ts';
-import { customConcat } from '../fp/object/customConcat.ts';
-import { customFindKey } from '../fp/object/customFindKey.ts';
-import type { Color } from '../paramTypes.ts';
-
-const predicate = (factor: number, temp: 'warm' | 'cool'): boolean => {
- const hueKeys = Object.keys(hueTempMap);
- if (
- hueKeys.some((val) =>
- inRange(
- floorCeil(factor),
- hueTempMap[val][temp][0],
- hueTempMap[val][temp][1]
- )
- )
- ) {
- return true;
- } else {
- return false;
- }
-};
-/**
- * @function
- * @description Checks if a color can be roughly classified as a cool color. Returns true if color is a cool color else false.
- * @param color The color to check the temperature.
- * @returns True or false.
- * @example
- *
- * import { isCool } from 'huetiful-js'
-
-let sample = [
- "#00ffdc",
- "#00ff78",
- "#00c000"
-];
-
-
-console.log(isCool(sample[7]));
-// false
-
-console.log(map(sample, isCool));
-
-// [ true, false, truee]
-
-
-
- */
-const isCool = (color: Color): boolean => {
- // First we need to get the hue value which we'll pass to the predicate
- const factor = getChannel('lch.h')(color);
-
- return predicate(factor, 'cool');
-};
-
-/**
- * @function
- * @description Checks if a color can be roughly classified as a warm color. Returns true if color is a warm color else false.
- * @param color The color to check the temperature.
- * @returns True or false.
- * @example import { isWarm } from 'huetiful-js'
-
-let sample = [
- "#00ffdc",
- "#00ff78",
- "#00c000"
-];
-
-
-
-console.log(isWarm(sample[7]));
-//true
-
-console.log(map(sample, isWarm));
-
-
-// [ false, true, false]
-
- */
-const isWarm = (color: Color): boolean => {
- const factor = getChannel('lch.h')(color);
-
- return predicate(factor, 'cool');
-};
-
-/**
- * @function
- * @description Checks the approximate maximum temperature that a color can have without losing its original hue. Does not take into account overtones (for now)
- * @param color The color to check its maximum temperature.
- * @returns The maximum temperature in Kelvins.
- * @example
- *
- * import { maxTemp } from "huetiful-js";
- *
- * console.log(maxTemp("#a1bd2f"))
-// 7926
-
-console.log(maxTemp("b2c3f1"))
-// 9570
- */
-const maxTemp = (color: Color): number => {
- // Get the hue value of the color
- const factor = getChannel('lch.h')(color);
-
- // Then we check to see in what hue family it is and check the highest hue value for that family
- const hue: string = customFindKey(hueTempMap, factor);
-
- // Get accurate hue start/ends in HSL
-
- const maxHue: number = max(...customConcat(hueTempMap[hue]));
-
- const result = getTemp({
- l: getChannel('lch.l')(color),
- c: getChannel('lch.c')(color),
- h: maxHue,
- mode: 'lch'
- });
-
- return result;
-};
-
-/**
- * @function
- * @description Checks the approximate minimum temperature that a color can have without losing its original hue. Does not take into account overtones (for now)
- * @param color The color to check its minimum temperature.
- * @returns The minimum temperature in Kelvins.
- * @example
- *
- * import { minTemp } from 'huetiful-js'
- *
- * console.log(minTemp("#a1bd2f"))
-// 2528
-
-console.log(minTemp("b2c3f1"))
-// 20107
- *
- */
-const minTemp = (color: Color): number => {
- // Get the hue value of the color
- // eslint-disable-next-line prefer-const
- let factor = getChannel('lch.h')(color);
-
- // Then we check to see in what hue family it is and check the highest hue value for that family
- const hue: string = customFindKey(hueTempMap, factor);
-
- const minHue: number = min(...customConcat(hueTempMap[hue]));
-
- // Get accurate hue start/ends in HSL
-
- const result = getTemp({
- l: getChannel('lch.l')(color),
- c: getChannel('lch.c')(color),
- h: minHue,
- mode: 'lch'
- });
-
- return result;
-};
-export { isCool, isWarm, maxTemp, minTemp };
diff --git a/src/converters.ts b/src/converters.ts
new file mode 100644
index 00000000..b2c56e9e
--- /dev/null
+++ b/src/converters.ts
@@ -0,0 +1,275 @@
+/* eslint-disable no-ternary */
+/*
+ * @license
+ * converters.ts - Converter functions for huetiful-js.
+ * Contains parts of chroma.js released under the same license.
+Copyright 2023 Dean Tarisai.
+This file is licensed to you under the Apache License, Version 2.0 (the 'License');
+you may not use this file except in compliance with the License. You may obtain a copy
+of the License at http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software distributed under
+the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+OF ANY KIND, either express or implied. See the License for the specific language
+governing permissions and limitations under the License.
+*/
+
+import {
+ useMode,
+ modeRgb,
+ converter,
+ modeDlch,
+ modeJch,
+ modeLch,
+ modeLch65,
+ modeLchuv,
+ modeOklch,
+ formatHex
+} from 'culori/fn';
+import type {
+ ColorTuple,
+ Colorspaces,
+ ColorToken,
+ UniformColorSpaces
+} from './types';
+import 'culori/all';
+import { formatHex8, colorsNamed } from 'culori/fn';
+import { getModeChannel } from './helpers';
+
+/**
+ * Converter function with mode definitions for uniform color spaces. The function is curried to return a converter in the passed colospace.
+ * @param colorspace The mode converter to return.
+ * @returns The converter function in the mode colorspace.
+ */
+function ucsConverter(colorspace: UniformColorSpaces) {
+ const ucsDefinitions = {
+ jch: modeJch,
+ lch: modeLch,
+ dlch: modeDlch,
+ lchuv: modeLchuv,
+ oklch: modeOklch,
+ lch65: modeLch65
+ };
+
+ return useMode(ucsDefinitions[colorspace.toLowerCase()]);
+}
+/**
+ *
+ Converts a wide range of color tokens which are color objects, and CSS named colors (for example 'red'), numbers from 0 to 166,777,215 and arrays in the form of [string,number,number,number,numer?] the first element in the array being the mode color space and the fourth optional number element as the opacity value to hexadecimal.
+ * @param color The color to convert to hexadecimal. Works on color objects and CSS named colors.
+ * @returns A hexadecimal representation of the passed in color.
+ * @example
+ * import { toHex } from "huetiful-js";
+ *
+console.log(toHex({ l: 50, c: 31, h: 100, alpha: 0.5, mode: "lch" }))
+// #7b794180
+
+console.log(toHex({ l: 50, c: 31, h: 100, mode: "lch" }))
+// #7b7941
+ */
+function toHex(color: ColorToken): string {
+ // the result to return at the end of the function
+ var output;
+ // if its of type string and not a CSS named color then its probably hex so we don't convert it
+ if (typeof color === 'string') {
+ // @ts-ignore
+ if (!Object.keys(colorsNamed).some((el) => el === color.toLowerCase())) {
+ return color;
+ }
+ return formatHex(color);
+ } else if (typeof color === 'boolean') {
+ return (color !== true && '#ffffff') || '#000000';
+ } else if (typeof color === 'number') {
+ output = num2rgb(color, true) as string;
+ } else {
+ // Get the mode variable
+ const mode = Array.isArray(color)
+ ? (color[0] as ColorTuple[0])
+ : (color['mode'] as Colorspaces);
+ const alpha =
+ Array.isArray(color) && color.length === 5
+ ? (color[color.length - 1] as ColorTuple[4])
+ : (color['alpha'] as number);
+
+ var channelKeys = getModeChannel(mode).split('');
+ var colorTupleToObj = (colorTuple: number[]) => {
+ var channels = colorTuple.slice(0, 3);
+
+ if (
+ ['rgb', 'lrgb'].indexOf(mode) !== -1 &&
+ channels.some((ch: number) => 1 < Math.abs(ch))
+ ) {
+ // @ts-ignore
+ channels = channels.map((ch) => ch / 255);
+ }
+
+ let output = {
+ [channelKeys[0]]: channels[0],
+ [channelKeys[1]]: channels[1],
+ [channelKeys[2]]: channels[2],
+ mode: mode,
+ alpha: alpha
+ };
+
+ return output;
+ };
+ if (mode) {
+ // coerce color tuple to object
+ if (Array.isArray(color)) {
+ // @ts-ignore
+ output = colorTupleToObj(color.slice(1));
+ } else {
+ output = colorTupleToObj(
+ toColorTuple(color, mode as Colorspaces).slice(1) as number[]
+ );
+ }
+ }
+ }
+
+ return (output['alpha'] < 1 && formatHex8(output)) || formatHex(output);
+}
+
+// Ported from chroma-js with slight modifications
+/**
+ *
+ * Returns the RGB color equivalent of any number between 0 and 16,777,215.
+ * @param num The number to convert to RGB
+ * @returns color An RGB color object or hex string.
+ * @example
+ *
+ * import { num2rgb } from 'huetiful-js'
+
+console.log(num2rgb(900, true))
+// #000384
+ */
+
+function num2rgb(num: number, hex = false): ColorToken {
+ if (typeof num === 'number' && num >= 0 && num <= 0xffffff) {
+ const r = num >> 16;
+ const g = (num >> 8) & 0xff;
+ const b = num & 0xff;
+
+ const output = {
+ r: r / 255,
+ g: g / 255,
+ b: b / 255,
+ mode: 'lrgb'
+ };
+ // @ts-ignore
+ return (hex && formatHex(output)) || output;
+ } else {
+ throw Error('unknown num color: ' + num);
+ }
+}
+
+/**
+ *
+ * Returns the numerical equivalent of a color.
+ * @param color The color to convert to its numerical equivalent.
+ * @returns value The numerical value of the color from 0 to 16,777,215.
+ * @example
+ *
+ * import { rgb2num } from 'huetiful-js'
+
+console.log(rgb2num("b2c3f1"))
+// 11715569
+ */
+
+function rgb2num(color: ColorToken): number {
+ // @ts-ignore
+ const rgb: ColorToken = useMode(modeRgb)(toHex(color));
+ return ((255 * rgb['r']) << 16) + ((255 * rgb['g']) << 8) + 255 * rgb['b'];
+}
+
+/* eslint-disable no-ternary */
+
+//ported from chroma-js
+
+/**
+ *
+ * Converts the temperature value (in Kelvins) to an RGB color.
+ * @param kelvin The number of Kelvins. From 0 to 30,000 .
+ * @param hex Optional boolean parameter to either return an RGB color object or hexadecimal string. Default is true.
+ * @returns color The color as a hexadecimal or RGB color object.
+ * @example
+ *
+ * import { temp2Color } from 'huetiful-js'
+
+console.log(temp2Color(2542))
+// #ffa44a
+ */
+
+function temp2Color(kelvin: number, hex = false): ColorToken {
+ const { log } = Math;
+ const temp = kelvin / 100;
+
+ var r: number, g: number, b: number;
+ if (temp < 66) {
+ r = 255;
+ g =
+ temp < 6
+ ? 0
+ : -155.25485562709179 -
+ 0.44596950469579133 * (g = temp - 2) +
+ 104.49216199393888 * log(g);
+ b =
+ temp < 20
+ ? 0
+ : -254.76935184120902 +
+ 0.8274096064007395 * (b = temp - 10) +
+ 115.67994401066147 * log(b);
+ } else {
+ r =
+ 351.97690566805693 +
+ 0.114206453784165 * (r = temp - 55) -
+ 40.25366309332127 * log(r);
+ g =
+ 325.4494125711974 +
+ 0.07943456536662342 * (g = temp - 50) -
+ 28.0852963507957 * log(g);
+ b = 255;
+ }
+ const result = {
+ r: r / 255,
+ g: g / 255,
+ b: b / 255,
+ mode: 'rgb'
+ };
+
+ // @ts-ignore
+ return (hex && formatHex(result)) || result;
+}
+
+/**
+ *
+ * Returns an array of channel values in the mode color space. It does not mutate the values of the passed in color token.
+ * @param color Expects the color to be in hexadecimal represantation or as a plain color object.
+ * @param mode The mode color space to return channel values for
+ * @returns An array of channel values with the colorspace as first element and the alpha channel if its explicitly defined in the passed in color.
+ * @example
+ *
+ *
+let rgbColor = {
+ r: 0.4,
+ g: 0.3,
+ b: 0.7,
+ mode: "rgb",
+};
+console.log(toColorTuple(rgbColor,'rgb'));
+
+// [ 'rgb', 0.4, 0.3, 0.7 ]
+
+ */
+
+function toColorTuple(color: string | object, mode: Colorspaces) {
+ // @ts-ignore
+ const colorObject: ColorToken = converter(mode)(color);
+
+ var arr = Object.keys(colorObject)
+ .filter((ch) => ch !== 'mode')
+ .map((key) => colorObject[key]);
+ arr.unshift(mode);
+
+ return arr;
+}
+
+export { num2rgb, rgb2num, temp2Color, toColorTuple, ucsConverter, toHex };
diff --git a/src/converters/getTemp.ts b/src/converters/getTemp.ts
deleted file mode 100644
index 77cbc624..00000000
--- a/src/converters/getTemp.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Based on implementation by Neil Bartlett
- * https://github.com/neilbartlett/color-temperature and chroma-js
- **/
-
-// @ts-nocheck
-import { useMode, modeLrgb } from 'culori/fn';
-import { temp2Color } from './temp2Color.ts';
-import type { Color } from '../paramTypes.js';
-import { toHex } from './toHex.ts';
-
-/**
- * @description Returns the temperature value in Kelvins of the passed in color.
- * @param color The color to query its temperature value
- * @returns The color's temperature in Kelvins.
- * @example
- *
- * import { getTemp } from 'huetiful-js'
-
-console.log(getTemp('#a1bd2f'))
-// 2542
- */
-
-const getTemp = (color: Color): number => {
- const { round } = Math;
- //Store the color in an object with the RGB channels normalized to [0,1]
- // Add a color obj for rgb using culori
- const toRgb = useMode(modeLrgb);
- const src = toRgb(toHex(color));
- // Allocate the red and blue channels to variables
- const r: number = src['r'],
- b: number = src['b'];
- let minTemp = 1000;
- let maxTemp = 40000;
- const eps = 0.4;
- let temp: number;
- while (maxTemp - minTemp > eps) {
- temp = (maxTemp + minTemp) * 0.5;
- const rgb = temp2Color(temp, false);
- if (rgb['b'] / rgb['r'] >= b / r) {
- maxTemp = temp;
- } else {
- minTemp = temp;
- }
- }
- return round(temp);
-};
-
-export { getTemp };
diff --git a/src/converters/index.ts b/src/converters/index.ts
deleted file mode 100644
index 03946ac2..00000000
--- a/src/converters/index.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export { temp2Color } from './temp2Color';
-export { toHex } from './toHex';
-export { rgb2num } from './rgb2num';
-export { num2rgb } from './num2rgb';
-export { getTemp } from './getTemp';
diff --git a/src/converters/num2rgb.ts b/src/converters/num2rgb.ts
deleted file mode 100644
index 29da156e..00000000
--- a/src/converters/num2rgb.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-//@ts-nocheck
-
-import 'culori/css';
-import type { Color } from '../paramTypes.ts';
-import { toHex } from './toHex.ts';
-// If the value is a floating point then we treat the decimal value as the opacity of the color.
-
-// If the value passedin is a float then the decimal is treated as opacity
-// The outputn is rounded up when you pass in an integer
-
-/**
- * @function
- * @description Returns the RGB color equivalent of any number between 0 and 16,777,215.
- * @param num The number to convert to RGB
- * @returns color An RGB color object or hex string.
- * @example
- *
- * import { num2rgb } from 'huetiful-js'
-
-console.log(num2rgb(900, true))
-// #000384
- */
-const num2rgb = (num: number, hex = false): Color => {
- if (typeof num === 'number' && num >= 0 && num <= 0xffffff) {
- const r = num >> 16;
- const g = (num >> 8) & 0xff;
- const b = num & 0xff;
-
- const output = {
- r: r / 255,
- g: g / 255,
- b: b / 255,
- mode: 'rgb'
- };
-
- if (hex) {
- return toHex(output);
- } else {
- return output;
- }
- } else {
- throw Error('unknown num color: ' + num);
- }
-};
-
-export { num2rgb };
diff --git a/src/converters/rgb2num.ts b/src/converters/rgb2num.ts
deleted file mode 100644
index 9997d9bb..00000000
--- a/src/converters/rgb2num.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-//@ts-nocheck
-
-import { useMode, modeRgb } from 'culori/fn';
-import { toHex } from './toHex.ts';
-import type { Color } from '../paramTypes.ts';
-
-/**
- * @function
- * @description Returns the numerical equivalent of a color.
- * @param color The color to convert to its numerical equivalent.
- * @returns value The numerical value of the color from 0 to 16,777,215.
- * @example
- *
- * import { rgb2num } from 'huetiful-js'
-
-console.log(rgb2num("b2c3f1"))
-// 11715569
- */
-
-const rgb2num = (color: Color): number => {
- const toRgb = useMode(modeRgb);
- const rgb: Color = toRgb(toHex(color));
-
- return ((255 * rgb['r']) << 16) + ((255 * rgb['g']) << 8) + 255 * rgb['b'];
-};
-
-export { rgb2num };
diff --git a/src/converters/temp2Color.ts b/src/converters/temp2Color.ts
deleted file mode 100644
index 3e7a9d88..00000000
--- a/src/converters/temp2Color.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-// @ts-nocheck
-
-//ported from chroma-js
-
-import { toHex } from './toHex';
-import type { Color } from '../paramTypes';
-/**
- * @function
- * @description Converts the temperature value (in Kelvins) to an RGB color.
- * @param kelvin The number of Kelvins. From 0 to 30,000 .
- * @param hex Optional boolean parameter to either return an RGB color object or hexadecimal string. Default is true.
- * @returns color The color as a hexadecimal or RGB color object.
- * @example
- *
- * import { temp2Color } from 'huetiful-js'
-
-console.log(temp2Color(2542))
-// #ffa44a
- */
-const temp2Color = (kelvin: number, hex = false): Color => {
- //Hue change starts at approx 655 Kelvins ???
- const { log } = Math;
- // Checking if the passed in value is within a problematic range that returns negative values on the blue channel.
- //inRange(kelvin, 400, 650) ? (kelvin = eps) : kelvin;
- //
- const temp = kelvin / 100;
-
- let r: number, g: number, b: number;
- if (temp < 66) {
- r = 255;
- g =
- temp < 6
- ? 0
- : -155.25485562709179 -
- 0.44596950469579133 * (g = temp - 2) +
- 104.49216199393888 * log(g);
- b =
- temp < 20
- ? 0
- : -254.76935184120902 +
- 0.8274096064007395 * (b = temp - 10) +
- 115.67994401066147 * log(b);
- } else {
- r =
- 351.97690566805693 +
- 0.114206453784165 * (r = temp - 55) -
- 40.25366309332127 * log(r);
- g =
- 325.4494125711974 +
- 0.07943456536662342 * (g = temp - 50) -
- 28.0852963507957 * log(g);
- b = 255;
- }
- const result = {
- r: r / 255,
- g: g / 255,
- b: b / 255,
- mode: 'rgb'
- };
-
- if (hex) {
- return toHex(result);
- } else {
- return result;
- }
-};
-
-export { temp2Color };
diff --git a/src/converters/toHex.ts b/src/converters/toHex.ts
deleted file mode 100644
index d1ccf77b..00000000
--- a/src/converters/toHex.ts
+++ /dev/null
@@ -1,95 +0,0 @@
-// @ts-nocheck
-
-import 'culori/css';
-import { formatHex8, formatHex, colorsNamed } from 'culori/fn';
-import { num2rgb } from './num2rgb';
-import type { Color } from '../paramTypes';
-
-/**
- *@function
- @description Converts a wide range of color tokens which are color objects, and CSS named colors (for example 'red'), numbers from 0 to 166,777,215 and arrays in the form of [string,number,number,number,numer?] the first element in the array being the mode color space and the fourth optional number element as the opacity value to hexadecimal.
- * @param color The color to convert to hexadecimal. Works on color objects and CSS named colors.
- * @returns A hexadecimal representation of the passed in color.
- * @example
- * import { toHex } from "huetiful-js";
- *
-console.log(toHex({ l: 50, c: 31, h: 100, alpha: 0.5, mode: "lch" }))
-// #7b794180
-
-console.log(toHex({ l: 50, c: 31, h: 100, mode: "lch" }))
-// #7b7941
- */
-const toHex = (color: Color): Color => {
- // the result to return at the end of the function
- let src = {};
-
- // if its of type string and not a CSS named color then its probably hex so we don't convert it
- if (
- typeof color === 'string' &&
- !Object.keys(colorsNamed).some((el) => el === color)
- ) {
- return color;
- } else {
- // If our color is an array
- if (Array.isArray(color)) {
- // capture the mode
- const mode: string = color[0];
-
- // set mode to a substring which trims the string at an index that is the reslt of length - 3
- const modeChannels = mode.substring(mode.length - 3);
- // Gets the channel key from the passed in mode
- const getModeChan = (mode: string, key: number) => mode.charAt(key);
-
- // Store the channels excluding alpha
- const channels = (
- src: object,
- colorArr: [string, number, number, number?]
- ) => {
- // Remove the mode element
- colorArr.shift();
- if (colorArr.length === 4) {
- colorArr = colorArr.slice(0, 3);
- }
- return colorArr;
- };
-
- /**
- * Returns a color object with normalized RGB values or just maps value to keys and return the resultant object
- * @param src The object to manipulate
- * @param mode The color space.
- * @param colorArr The array of the color's channel values excluding the alpha/opacity channel
- * @returns A color object
- */
- const channelMapper = (
- src = {},
- mode: string,
- colorArr: [number, number, number]
- ): number[] => {
- src['mode'] = mode;
- // If our mode is rgb...
- if (src['mode'] === 'rgb') {
- // if our rgb values are [0,255] we normalize them to [0,1]
- // for Culori to make sense of the channel values else it defaults o white
- if (colorArr.some((ch) => Math.abs(ch) > 1)) {
- colorArr.map((ch, key) => (src[getModeChan(mode, key)] = ch / 255));
- }
- } else {
- colorArr.map((ch, key) => (src[getModeChan(mode, key)] = ch));
- }
- return src;
- };
- src['alpha'] = color[4] || 1;
- src = channelMapper(src, modeChannels, channels(src, color));
- src = (src['alpha'] < 1 && formatHex8(src)) || formatHex(src);
- }
- // if its a number use num2rgb
- else if (typeof color === 'number') {
- src = num2rgb(color, true);
- } else {
- src = (color['alpha'] && formatHex8(color)) || formatHex(color);
- }
- return src;
- }
-};
-
-export { toHex };
diff --git a/src/filterBy.ts b/src/filterBy.ts
new file mode 100644
index 00000000..3d34e697
--- /dev/null
+++ b/src/filterBy.ts
@@ -0,0 +1,341 @@
+/**
+ * @license
+ * filterBy.ts - Utilities for filtering collections of colors.
+Copyright 2023 Dean Tarisai.
+This file is licensed to you under the Apache License, Version 2.0 (the 'License');
+you may not use this file except in compliance with the License. You may obtain a copy
+of the License at http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software distributed under
+the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+OF ANY KIND, either express or implied. See the License for the specific language
+governing permissions and limitations under the License.
+*/
+
+import { differenceHyab } from 'culori/fn';
+import { toHex } from './converters';
+import type { ColorToken, HueColorSpaces, Factor } from './types';
+import { getLuminance, getContrast, getChannel } from './utils';
+import {
+ matchChromaChannel,
+ matchLightnessChannel,
+ filteredArr,
+ normalize,
+ matchComparator,
+ matchDigits
+} from './helpers';
+
+/**
+ * @internal
+ *
+ * @param factor The color property in query.
+ * @param cb The predicate to get the equatable value used during comparison
+ * @param colors The collection to map over. Can either be an array or object whose values are valid color tokens.
+ * @param start The minimum end of the filtering range.
+ * @param end The maximum end of the filtering range. It can also be omitted and all colors greater than the starting value of the factor being queried will be returned.
+ * @returns An array of colors
+ */
+function baseFilterBy(
+ factor: Factor,
+ cb: (color: ColorToken) => number,
+ collection: ColorToken[] | object | object,
+ start: string | number,
+ end?: number,
+ colorspace?: HueColorSpaces
+) {
+ const obj = {
+ saturation: matchChromaChannel,
+ lightness: matchLightnessChannel
+ };
+
+ var [sym, val, normalVal] = [
+ matchComparator(start as string),
+ matchDigits(start as string),
+ normalize(Number(val), obj[factor](colorspace))
+ ];
+ start =
+ (typeof start === 'string' && sym && normalVal.toString().concat(sym)) ||
+ start;
+ end = normalize(end, obj[factor](colorspace));
+ return filteredArr(factor, cb)(collection as Array, start, end);
+}
+
+/**
+ *
+ * Returns an array of colors in the specified saturation range. The range is normalised to [0,1].
+ * @param colors The array of colors to filter.
+ * @param startSaturation The minimum end of the saturation range.
+ * @param endSaturation The maximum end of the saturation range.
+ * @param colorspace The color space to fetch the saturation value from. Any color space with a chroma channel e.g 'lch' or 'hsl' will do.
+ * @returns Array of filtered colors.
+ * @example
+ * import { filterByContrast } from 'huetiful-js'
+
+let sample = [
+ '#00ffdc',
+ '#00ff78',
+ '#00c000',
+ '#007e00',
+ '#164100',
+ '#ffff00',
+ '#310000',
+ '#3e0000',
+ '#4e0000',
+ '#600000',
+ '#720000',
+]
+
+console.log(filterByContrast(sample, 'green', '>=3'))
+// [ '#00ffdc', '#00ff78', '#ffff00', '#310000', '#3e0000', '#4e0000' ]
+ */
+
+function filterBySaturation(
+ collection: ColorToken[] | object,
+ startSaturation = 0.05,
+ endSaturation = 1,
+ colorspace?: HueColorSpaces
+): ColorToken[] {
+ const modeChannel = matchChromaChannel(colorspace);
+ // Normalize properly later
+ const factor: Factor = 'saturation';
+
+ return baseFilterBy(
+ factor,
+ getChannel(modeChannel),
+ collection,
+ startSaturation,
+ endSaturation,
+ colorspace
+ );
+}
+
+/**
+ *
+ * Returns an array of colors in the specified luminance range. The range is normalised to [0,1].
+ * @param colors The array of colors to filter.
+ * @param startLuminance The minimum end of the luminance range.
+ * @param endLuminance The maximum end of the luminance range.
+ * @returns Array of filtered colors.
+ * @example
+ *
+ * import { filterByLuminance } from 'huetiful-js'
+let sample = [
+ '#00ffdc',
+ '#00ff78',
+ '#00c000',
+ '#007e00',
+ '#164100',
+ '#ffff00',
+ '#310000',
+ '#3e0000',
+ '#4e0000',
+ '#600000',
+ '#720000',
+]
+
+filterByLuminance(sample, 0.4, 0.9)
+
+// [ '#00ffdc', '#00ff78' ]
+ */
+
+function filterByLuminance(
+ collection: ColorToken[] | object,
+ startLuminance = 0.05,
+ endLuminance = 1
+): ColorToken[] {
+ return baseFilterBy(
+ 'luminance',
+ getLuminance,
+ collection,
+ startLuminance,
+ endLuminance
+ );
+}
+
+/**
+ *
+ * Returns an array of colors in the specified lightness range. The range is between 0 and 100.
+ * @param colors The array of colors to filter.
+ * @param startLightness The minimum end of the lightness range.
+ * @param endLightness The maximum end of the lightness range.
+ * @param colorspace The mode colorspace to retrieve the lightness value from. The default is lch65
+ * @returns Array of filtered colors.
+ * @example
+ *
+ * import { filterByLightness } from 'huetiful-js'
+let sample = [
+ '#00ffdc',
+ '#00ff78',
+ '#00c000',
+ '#007e00',
+ '#164100',
+ '#ffff00',
+ '#310000',
+ '#3e0000',
+ '#4e0000',
+ '#600000',
+ '#720000',
+]
+
+filterByLightness(sample, 20, 80)
+
+// [ '#00c000', '#007e00', '#164100', '#720000' ]
+ */
+
+function filterByLightness(
+ collection: ColorToken[] | object,
+ startLightness = 5,
+ endLightness = 100,
+ colorspace?: HueColorSpaces
+): ColorToken[] {
+ const factor: Factor = 'lightness';
+
+ return baseFilterBy(
+ factor,
+ getChannel(matchLightnessChannel(colorspace)),
+ collection,
+ startLightness,
+ endLightness,
+ colorspace
+ );
+}
+
+//filterByHue takes an array of colors and
+/**
+ *
+ * Returns colors in the specified hue ranges between 0 to 360.
+ * @param colors The array of colors to filter.
+ * @param startHue The minimum end of the hue range.
+ * @param endHue The maximum end of the hue range.
+ * @returns Array of the filtered colors.
+ * @example
+ * let sample = [
+ '#00ffdc',
+ '#00ff78',
+ '#00c000',
+ '#007e00',
+ '#164100',
+ '#ffff00',
+ '#310000',
+ '#3e0000',
+ '#4e0000',
+ '#600000',
+ '#720000',
+]
+
+filterByHue(sample, 20, 80)
+
+// [ '#310000', '#3e0000', '#4e0000', '#600000', '#720000' ]
+ */
+
+function filterByHue(
+ collection: ColorToken[] | object,
+ startHue = 0,
+ endHue = 360,
+ colorspace?: HueColorSpaces
+): ColorToken[] {
+ return baseFilterBy(
+ 'hue',
+ getChannel(`${colorspace}.h`),
+ collection,
+ startHue,
+ endHue
+ );
+}
+
+/**
+ *
+ * Returns an array of colors with the specified distance range. The distance is tested against a comparison color (the 'against' param) and the specified distance ranges. Uses the differenceHyab metric for calculating the distances.
+ * @param colors The array of colors to filter.
+ * @param startDistance The minimum end of the distance range.
+ * @param endDistance The maximum end of the distance range.
+ * @returns Array of filtered colors.
+ * @example
+ * import { filterByDistance } from 'huetiful-js'
+
+let sample = [
+ "#ffff00",
+ "#00ffdc",
+ "#00ff78",
+ "#00c000",
+ "#007e00",
+ "#164100",
+ "#720000",
+ "#600000",
+]
+
+console.log(filterByDistance(sample, "yellow", 0.1))
+// [ '#ffff00' ]
+ */
+
+function filterByDistance(
+ collection: ColorToken[] | object,
+ against: ColorToken,
+ startDistance = 0.05,
+ endDistance?: number
+): ColorToken[] {
+ const cb = (against) => (color) => differenceHyab()(against, color);
+
+ return baseFilterBy(
+ 'distance',
+ cb(toHex(against)),
+ collection,
+ startDistance,
+ endDistance
+ );
+}
+
+/**
+ *
+ * Returns an array of colors with the specified contrast range. The contrast is tested against a comparison color (the 'against' param) and the specified contrast ranges.
+ * @param colors The array of colors to filter.
+ * @param startContrast The minimum end of the contrast range.
+ * @param endContrast The maximum end of the contrast range.
+ * @returns Array of filtered colors.
+ *
+ * @example
+ *
+ * import { filterByContrast } from 'huetiful-js'
+
+let sample = [
+ '#00ffdc',
+ '#00ff78',
+ '#00c000',
+ '#007e00',
+ '#164100',
+ '#ffff00',
+ '#310000',
+ '#3e0000',
+ '#4e0000',
+ '#600000',
+ '#720000',
+]
+
+console.log(filterByContrast(sample, 'green', '>=3'))
+// [ '#00ffdc', '#00ff78', '#ffff00', '#310000', '#3e0000', '#4e0000' ]
+ */
+
+function filterByContrast(
+ collection: ColorToken[] | object,
+ against: ColorToken,
+ startContrast = 1,
+ endContrast = 21
+): ColorToken[] {
+ const cb = (against: ColorToken) => (color: ColorToken) =>
+ getContrast(color, against);
+ return baseFilterBy(
+ 'contrast',
+ cb(against),
+ collection,
+ startContrast,
+ endContrast
+ );
+}
+
+export {
+ filterByContrast,
+ filterByDistance,
+ filterByLuminance,
+ filterBySaturation,
+ filterByHue,
+ filterByLightness
+};
diff --git a/src/filterBy/filterByContrast.ts b/src/filterBy/filterByContrast.ts
deleted file mode 100644
index 66e756ac..00000000
--- a/src/filterBy/filterByContrast.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-// @ts-nocheck
-import { filteredArr } from '../fp/array/filteredArr.ts';
-import { wcagContrast } from 'culori/fn';
-
-import type { Color, Factor } from '../paramTypes.ts';
-import { toHex } from '../converters/toHex.ts';
-
-/**
- * @function
- * Returns an array of colors with the specified contrast range. The contrast is tested against a comparison color (the 'against' param) and the specified contrast ranges.
- * @param colors The array of colors to filter.
- * @param startContrast The minimum end of the contrast range.
- * @param endContrast The maximum end of the contrast range.
- * @returns Array of filtered colors.
- *
- * @example
- *
- * import { filterByContrast } from 'huetiful-js'
-
-let sample = [
- '#00ffdc',
- '#00ff78',
- '#00c000',
- '#007e00',
- '#164100',
- '#ffff00',
- '#310000',
- '#3e0000',
- '#4e0000',
- '#600000',
- '#720000',
-]
-
-console.log(filterByContrast(sample, 'green', '>=3'))
-// [ '#00ffdc', '#00ff78', '#ffff00', '#310000', '#3e0000', '#4e0000' ]
- */
-
-const filterByContrast = (
- colors: Color[],
- against: Color,
- startContrast = 0.05,
- endContrast?: number
-): Color[] => {
- // Formatting color tokens to parseable type
- // Create an object that has the contrast and name of color as properties.
- const factor: Factor = 'contrast';
- const cb = (against: Color) => (color: Color) =>
- wcagContrast(...[color, against].map(toHex));
-
- return filteredArr(factor, cb(against))(colors, startContrast, endContrast);
-};
-
-export { filterByContrast };
diff --git a/src/filterBy/filterByDistance.ts b/src/filterBy/filterByDistance.ts
deleted file mode 100644
index d325621f..00000000
--- a/src/filterBy/filterByDistance.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-// @ts-nocheck
-
-import { filteredArr } from '../fp/array/filteredArr.ts';
-import { differenceEuclidean } from 'culori/fn';
-import type { Color, Factor, ColorSpaces } from '../paramTypes.ts';
-import { toHex } from '../converters/toHex.ts';
-/**
- * @function
- * Returns an array of colors with the specified distance range. The distance is tested against a comparison color (the 'against' param) and the specified distance ranges.
- * @param colors The array of colors to filter.
- * @param startDistance The minimum end of the distance range.
- * @param endDistance The maximum end of the distance range.
- * @param weights The weighting values to pass to the Euclidean function. Default is [1,1,1,0].
- * @param mode The color space to calculate the distance in .
- * @returns Array of filtered colors.
- * @example
- * import { filterByDistance } from 'huetiful-js'
-
-let sample = [
- "#ffff00",
- "#00ffdc",
- "#00ff78",
- "#00c000",
- "#007e00",
- "#164100",
- "#720000",
- "#600000",
-]
-
-console.log(filterByDistance(sample, "yellow", 0.1))
-// [ '#ffff00' ]
- */
-
-const filterByDistance = (
- colors: Color[],
- against: Color,
- startDistance = 0.05,
- endDistance?: number,
- mode?: ColorSpaces,
- weights?: [number, number, number, number]
-): Color[] => {
- // Formatting color tokens to parseable type
- // How do I get the distance
-
- // Create an object that has the distance and name of color as properties.
- const factor: Factor = 'distance';
- const cb = (against: Color, mode: ColorSpaces) => (color: Color) =>
- differenceEuclidean(
- mode || 'lch',
- weights || [1, 1, 1, 0]
- )(...[against, color].map(toHex));
-
- return filteredArr(factor, cb(against, mode))(
- colors,
- startDistance,
- endDistance
- );
-};
-
-export { filterByDistance };
-
-// Make modes lower case and string keys
-// Debug 'Cannot destructure mode'
diff --git a/src/filterBy/filterByHue.ts b/src/filterBy/filterByHue.ts
deleted file mode 100644
index de76bc7a..00000000
--- a/src/filterBy/filterByHue.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import { filteredArr } from '../fp/array/filteredArr.ts';
-import { getChannel } from '../getters_and_setters/get.ts';
-import type { Color, Factor } from '../paramTypes.ts';
-
-//filterByHue takes an array of colors and
-/**
- * @function
- * Returns colors in the specified hue ranges between 0 to 360.
- * @param colors The array of colors to filter.
- * @param startHue The minimum end of the hue range.
- * @param endHue The maximum end of the hue range.
- * @returns Array of the filtered colors.
- * @example
- * let sample = [
- '#00ffdc',
- '#00ff78',
- '#00c000',
- '#007e00',
- '#164100',
- '#ffff00',
- '#310000',
- '#3e0000',
- '#4e0000',
- '#600000',
- '#720000',
-]
-
-filterByHue(sample, 20, 80)
-
-// [ '#310000', '#3e0000', '#4e0000', '#600000', '#720000' ]
- */
-
-const filterByHue = (colors: Color[], startHue = 0, endHue = 360): Color[] => {
- const factor: Factor = 'hue';
- const cb = getChannel('lch.h');
-
- return filteredArr(factor, cb)(colors, startHue, endHue);
-};
-
-export { filterByHue };
diff --git a/src/filterBy/filterByLightness.ts b/src/filterBy/filterByLightness.ts
deleted file mode 100644
index 3c558c9d..00000000
--- a/src/filterBy/filterByLightness.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import { filteredArr } from '../fp/array/filteredArr.ts';
-import { getChannel } from '../getters_and_setters/get.ts';
-import { Color, Factor } from '../paramTypes.ts';
-/**
- * @function
- * @description Returns an array of colors in the specified lightness range. The range is between 0 and 100.
- * @param colors The array of colors to filter.
- * @param startLightness The minimum end of the lightness range.
- * @param endLightness The maximum end of the lightness range.
- * @returns Array of filtered colors.
- * @example
- *
- * import { filterByLightness } from 'huetiful-js'
-let sample = [
- '#00ffdc',
- '#00ff78',
- '#00c000',
- '#007e00',
- '#164100',
- '#ffff00',
- '#310000',
- '#3e0000',
- '#4e0000',
- '#600000',
- '#720000',
-]
-
-filterByLightness(sample, 20, 80)
-
-// [ '#00c000', '#007e00', '#164100', '#720000' ]
- */
-
-const filterByLightness = (
- colors: Color[],
- startLightness = 5,
- endLightness = 100
-): Color[] => {
- // Formatting color tokens to parseable type
- // Create an object that has the lightness and name of color as properties.
- const factor: Factor = 'lightness';
- const cb = getChannel('lch.l');
-
- return filteredArr(factor, cb)(colors, startLightness, endLightness);
-};
-
-export { filterByLightness };
diff --git a/src/filterBy/filterByLuminance.ts b/src/filterBy/filterByLuminance.ts
deleted file mode 100644
index 02d687f2..00000000
--- a/src/filterBy/filterByLuminance.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import { filteredArr } from '../fp/array/filteredArr.ts';
-import { Color } from '../paramTypes.ts';
-import { getLuminance } from '../getters_and_setters/luminance.ts';
-import type { Factor } from '../paramTypes';
-/**
- * @function
- * @description Returns an array of colors in the specified luminance range. The range is normalised to [0,1].
- * @param colors The array of colors to filter.
- * @param startLuminance The minimum end of the luminance range.
- * @param endLuminance The maximum end of the luminance range.
- * @returns Array of filtered colors.
- * @example
- *
- * import { filterByLuminance } from 'huetiful-js'
-let sample = [
- '#00ffdc',
- '#00ff78',
- '#00c000',
- '#007e00',
- '#164100',
- '#ffff00',
- '#310000',
- '#3e0000',
- '#4e0000',
- '#600000',
- '#720000',
-]
-
-filterByLuminance(sample, 0.4, 0.9)
-
-// [ '#00ffdc', '#00ff78' ]
- */
-
-const filterByLuminance = (
- colors: Color[],
- startLuminance = 0.05,
- endLuminance = 1
-): Color[] => {
- // Formatting color tokens to parseable type
- // Create an object that has the luminance and name of color as properties.
- const factor: Factor = 'luminance';
- const cb = getLuminance;
-
- return filteredArr(factor, cb)(colors, startLuminance, endLuminance);
-};
-
-export { filterByLuminance };
diff --git a/src/filterBy/filterBySaturation.ts b/src/filterBy/filterBySaturation.ts
deleted file mode 100644
index 70b00396..00000000
--- a/src/filterBy/filterBySaturation.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-// @ts-nocheck
-import { getChannel } from '../getters_and_setters/get.ts';
-import { filteredArr } from '../fp/array/filteredArr.ts';
-import type { Color, Factor } from '../paramTypes';
-
-/**
- * @function
- * @description Returns an array of colors in the specified saturation range. The range is normalised to [0,1].
- * @param colors The array of colors to filter.
- * @param startSaturation The minimum end of the saturation range.
- * @param endSaturation The maximum end of the saturation range.
- * @returns Array of filtered colors.
- * @example
- * import { filterByContrast } from 'huetiful-js'
-
-let sample = [
- '#00ffdc',
- '#00ff78',
- '#00c000',
- '#007e00',
- '#164100',
- '#ffff00',
- '#310000',
- '#3e0000',
- '#4e0000',
- '#600000',
- '#720000',
-]
-
-console.log(filterByContrast(sample, 'green', '>=3'))
-// [ '#00ffdc', '#00ff78', '#ffff00', '#310000', '#3e0000', '#4e0000' ]
- */
-
-const filterBySaturation = (
- colors: Color[],
- startSaturation = 0.05,
- endSaturation = 1
-): Color[] => {
- const factor: Factor = 'saturation';
- const cb = getChannel('lch.c');
-
- // Normalize saturation ranges later
- return filteredArr(factor, cb)(
- colors,
- 100 * startSaturation,
- 100 * endSaturation
- );
-};
-
-export { filterBySaturation };
diff --git a/src/filterBy/filterByTemp.ts b/src/filterBy/filterByTemp.ts
deleted file mode 100644
index 143f62ff..00000000
--- a/src/filterBy/filterByTemp.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-import { getTemp } from '../converters/getTemp.ts';
-import { filteredArr } from '../fp/array/filteredArr.ts';
-import type { Color, Factor } from '../paramTypes.ts';
-
-/**
- * @function
- * @description Returns an array of colors in the specified temperature range between 0 and 30,000 Kelvins.
- * @param colors The array of colors to filter.
- * @param startTemp The minimum end of the temperature range.
- * @param endTemp The maximum end of the temperature range.
- * @returns Array of the filtered colors.
- * @see Based on Neil Bartlett's implementation https://github.com/neilbartlett/color-temperature
- * @example
- *
- * import { filterByTemp } from "huetiful-js";
-let sample = [
-"#00ffdc",
-"#00ff78",
-"#00c000",
-"#007e00",
-"#164100",
-"#ffff00",
-"#310000",
-"#3e0000",
-"#4e0000",
-"#600000",
-"#720000",
-];
-
-
-filterByTemp(sample, 1000, 20000);
-
-// [
-'#00c000', '#007e00',
-'#164100', '#ffff00',
-'#310000', '#3e0000',
-'#4e0000', '#600000',
-'#720000'
-]
- */
-
-const filterByTemp = (
- colors: Color[],
- startTemp = 1000,
- endTemp = 6000
-): Color[] => {
- // This variable stores the array that matches the filtering criteria defined by the start and end hues
- const factor: Factor = 'temp';
- const cb = getTemp;
-
- return filteredArr(factor, cb)(colors, startTemp, endTemp);
-};
-
-export { filterByTemp };
-
-//TODO Could also specify warm|cool to quickly return the filtered array. This param overrides startTemp and endTemp.
diff --git a/src/filterBy/index.ts b/src/filterBy/index.ts
deleted file mode 100644
index ca3653c0..00000000
--- a/src/filterBy/index.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export { filterByTemp } from './filterByTemp';
-export { filterBySaturation } from './filterBySaturation';
-export { filterByLuminance } from './filterByLuminance';
-export { filterByHue } from './filterByHue';
-export { filterByLightness } from './filterByLightness';
-export { filterByDistance } from './filterByDistance';
-export { filterByContrast } from './filterByContrast';
diff --git a/src/fp/array/colorObjArr.ts b/src/fp/array/colorObjArr.ts
deleted file mode 100644
index 71e6c246..00000000
--- a/src/fp/array/colorObjArr.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-// @ts-nocheck
-
-import { colorObj } from '../object/colorObj';
-import { Factor, Color } from '../../paramTypes';
-
-/*
- * @function
- * @private
- * Creates a custom object with a factor to pass to the predicate function.
- * @param factor The quality being queried.
- * @param cb The callback function for computing the factor's start.
- * @param colors The array of colors to iterate over.
- * @returns An array of objects.
- */
-export const colorObjArr =
- (factor: Factor, callback) =>
- (colors: Color[]): Array<{ factor: Factor; name: Color }> => {
- const cb = colorObj(factor, callback);
- return colors.map((color) => cb(color));
- };
diff --git a/src/fp/array/customSort.ts b/src/fp/array/customSort.ts
deleted file mode 100644
index d14a5483..00000000
--- a/src/fp/array/customSort.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-//@ts-nocheck
-import { Factor, Order } from '../../paramTypes';
-
-/**
- * @description Helper function for native sorting method for arrays.
- * @param factor The property to query.
- * @param order Either ascending or descending.
- * @returns A sorted array.
- */
-export const customSort = (order: Order, factor?: Factor) => {
- // Special thanks to deechris27 on youtube
- // a-b gives asc order & b-a gives desc order
- factor = factor || 'factor';
- return (a, b) => {
- if (order === 'asc') {
- return a[factor] - b[factor];
- } else if (order === 'desc') {
- return b[factor] - a[factor];
- }
- };
-};
diff --git a/src/fp/array/filteredArr.ts b/src/fp/array/filteredArr.ts
deleted file mode 100644
index 1b0f111e..00000000
--- a/src/fp/array/filteredArr.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-// @ts-nocheck
-import { Factor, Color, callback } from '../../paramTypes';
-import { gt, gte, lt, lte } from '../number/comparison';
-import { inRange } from '../number/inRange';
-import { colorObjArr } from './colorObjArr';
-
-/**
- * @description Filters an array according to the value of a color's queried factor
- * @param factor The property to query and use as filtering criteria
- * @param cb The function to use for comparison
- * @returns The filtered array
- */
-const filteredArr =
- (factor: Factor, cb?: callback) =>
- (colors: Color[], start: number | string, end: number): Color[] => {
- let result: Color[];
-
- if (typeof start === 'number') {
- result = colorObjArr(
- factor,
- cb
- )(colors)
- .filter((color) => inRange(color[factor], start, end))
- .map((color) => color['name']);
-
- return result;
-
- // If string split the the string to an array of signature [sign,value] with sign being the type of predicate returned to mapFilter.
- } else if (typeof start === 'string') {
- //The pattern to match
- const reOperator = /^(>=|<=|<|>)/;
-
- const value = /[0-9]*\.?[0-9]+/;
-
- // Array
- const val = value.exec(start),
- op = reOperator.exec(start);
-
- const mapFilter = (test: (x: number, y: number) => boolean): Color[] => {
- return colorObjArr(
- factor,
- cb
- )(colors)
- .filter((el) => test(el[factor], parseFloat(val['0'])))
- .map((el) => el['name']);
- };
- switch (op['0']) {
- case '<':
- result = mapFilter(lt);
-
- break;
- case '>':
- result = mapFilter(gt);
- break;
- case '<=':
- result = mapFilter(lte);
- break;
- case '>=':
- result = mapFilter(gte);
- break;
- }
- }
- return result;
- };
-
-export { filteredArr };
diff --git a/src/fp/array/index.ts b/src/fp/array/index.ts
deleted file mode 100644
index 42a2e514..00000000
--- a/src/fp/array/index.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-// This module has array methods
-// @ts-nocheck
-export { min, max } from './min_max';
-export { colorObjArr } from './colorObjArr';
-export { customSort } from './customSort';
-export { filteredArr } from './filteredArr';
-export { sortedArr } from './sortedArr';
diff --git a/src/fp/array/min_max.ts b/src/fp/array/min_max.ts
deleted file mode 100644
index b1ea4c0b..00000000
--- a/src/fp/array/min_max.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-// @ts-nocheck
-
-import { gt, lt } from '../number/comparison';
-
-// from the lodash implementation of _.min and _.max
-const identity = (value) => {
- return value;
-};
-const baseExtremum = (array: number[], iteratee, comparator) => {
- var index = -1,
- length = array.length;
-
- while (++index < length) {
- var value = array[index],
- current = iteratee(value);
-
- if (
- current != null &&
- (computed === undefined
- ? current === current
- : comparator(current, computed))
- ) {
- var computed = current,
- result = value;
- }
- }
- return result;
-};
-/**
- * @description Gets the smallest value in an array
- * @param array The array to retrieve minimum value
- * @returns The smallest number in the array
- */
-const min = (array: number[]): number => {
- return array && array.length ? baseExtremum(array, identity, lt) : undefined;
-};
-/**
- * @description Gets the largest value in an array
- * @param array The array to retrieve maximum value
- * @returns The largest number in the array
- */
-const max = (array: number[]): number => {
- return array && array.length ? baseExtremum(array, identity, gt) : undefined;
-};
-
-export { min, max };
diff --git a/src/fp/array/sortedArr.ts b/src/fp/array/sortedArr.ts
deleted file mode 100644
index 0ae00f57..00000000
--- a/src/fp/array/sortedArr.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { Factor, Color, callback, Order } from '../../paramTypes';
-import { colorObjArr } from './colorObjArr';
-import { customSort } from './customSort';
-
-/**
- * Filters an array of color objects with a "factor" property whose value is determined by a predicate or getter via the cb param.
- * @param factor The property to query
- * @param callback The function to use for comparison.
- * @returns An array of colors or color objects.
- */
-const sortedArr =
- (factor: Factor, callback: callback, order: Order, colorObj = false) =>
- (colors: Color[]) => {
- const results: Color[] | Array<{ factor: number; name: Color }> =
- colorObjArr(factor, callback)(colors);
- // (colorObj && color) || color['name'];
- // Assign the value of colorObj to results variable
- // Sort the array using our customSort helper function
- results.sort(customSort(order, factor));
-
- // colorObj parameter is true return the array of color objects
- // else just return the color's name value.
- if (colorObj) {
- return results;
- } else {
- return results.map((color) => color['name']);
- }
- };
-
-export { sortedArr };
diff --git a/src/fp/index.ts b/src/fp/index.ts
deleted file mode 100644
index 2f921de2..00000000
--- a/src/fp/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export * from './number';
-export * from './array';
-export * from './object';
-export * from './string';
diff --git a/src/fp/number/adjustHue.ts b/src/fp/number/adjustHue.ts
deleted file mode 100644
index 38ef9e56..00000000
--- a/src/fp/number/adjustHue.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-// @ts-nocheck
-
-const adjustHue = (value = 0) => {
- if (value > 0) {
- return (value += Math.ceil(-value / 360) * 360);
- } else {
- return value % 360;
- }
-};
-
-export { adjustHue };
diff --git a/src/fp/number/comparison.ts b/src/fp/number/comparison.ts
deleted file mode 100644
index 743598dd..00000000
--- a/src/fp/number/comparison.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-// @ts-nocheck
-
-// Comparison operators
-const gt = (x: number, y: number): boolean => x > y;
-const lt = (x: number, y: number): boolean => x < y;
-const gte = (x: number, y: number): boolean => x >= y;
-const lte = (x: number, y: number): boolean => x <= y;
-
-export { lt, gt, gte, lte };
diff --git a/src/fp/number/floorCeil.ts b/src/fp/number/floorCeil.ts
deleted file mode 100644
index 32580ff6..00000000
--- a/src/fp/number/floorCeil.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { isInt } from './isInt';
-
-const { ceil, floor } = Math;
-/**
- * @function
- * Rounds up or down a number based on the float value.
- * @param num The number to round up or down.
- * @returns An integer
- */
-const floorCeil = (num: number): number => {
- if (isInt(num)) {
- return num;
- } else {
- const strArr = num.toString().split('.');
- const float = strArr[1];
-
- //If the decimal value is .4 and below it will be rounded down else it will be rounded up.
- const reFloorCeil = (float: string) => /^[0-4]$/.test(float.charAt(0));
-
- if (reFloorCeil(float)) {
- num = floor(num);
- } else {
- num = ceil(num);
- }
- }
- return num;
-};
-
-export { floorCeil };
diff --git a/src/fp/number/inRange.ts b/src/fp/number/inRange.ts
deleted file mode 100644
index e98bfca1..00000000
--- a/src/fp/number/inRange.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-// @ts-nocheck
-
-const inRange = (number: number, start: number, end?: number): boolean => {
- /* Built-in method references for those with the same name as other `lodash` methods. */
- var nativeMax = Math.max,
- nativeMin = Math.min;
- return number >= nativeMin(start, end) && number < nativeMax(start, end);
-};
-
-export { inRange };
diff --git a/src/fp/number/index.ts b/src/fp/number/index.ts
deleted file mode 100644
index 9895d298..00000000
--- a/src/fp/number/index.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-// This module has number methods
-// @ts-nocheck
-
-export { lt, gt, gte, lte } from './comparison';
-export { random } from './random';
-export { isInt } from './isInt';
-export { normalize } from './normalize';
-export { floorCeil } from './floorCeil';
-export { adjustHue } from './adjustHue';
-export { inRange } from './inRange';
diff --git a/src/fp/number/isInt.ts b/src/fp/number/isInt.ts
deleted file mode 100644
index 2377046e..00000000
--- a/src/fp/number/isInt.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-/**
- * Checks if a number is a float.
- * @param num The number to query
- * @returns True if the number is an integer else false
- */
-const isInt = (num: number) => {
- const reInt = /^-?[0-9]+$/;
- return reInt.test(num.toString());
-};
-
-export { isInt };
diff --git a/src/fp/number/normalize.ts b/src/fp/number/normalize.ts
deleted file mode 100644
index 743639a9..00000000
--- a/src/fp/number/normalize.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-/**
- * @description Normalizes passed in values to 0 and 1
- * @param start
- * @param end
- */
-const normalize = (num: number, start: number, end: number): number => {
- return num * (end - start);
-};
-
-export { normalize };
diff --git a/src/fp/number/random.ts b/src/fp/number/random.ts
deleted file mode 100644
index d29ea33a..00000000
--- a/src/fp/number/random.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-const random = (min: number, max: number): number => {
- if (min > max) {
- const mn = min;
- const mx = max;
- max = mn;
- min = mx;
- } else {
- return Math.random() * (max - min) + min;
- }
-};
-
-export { random };
diff --git a/src/fp/object/colorObj.ts b/src/fp/object/colorObj.ts
deleted file mode 100644
index 240fbed1..00000000
--- a/src/fp/object/colorObj.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import type { callback, Factor, Color } from '../../paramTypes';
-
-// @ts-nocheck
-const colorObj = (factor: Factor, callback: callback) => (color: Color) => {
- return { [factor]: callback(color), name: color };
-};
-
-export { colorObj };
diff --git a/src/fp/object/customConcat.ts b/src/fp/object/customConcat.ts
deleted file mode 100644
index 922c7496..00000000
--- a/src/fp/object/customConcat.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-// @ts-nocheck
-
-const customConcat = (hue: object) => {
- const res: number[] = [];
- const { keys } = Object;
- if (typeof hue == 'object') {
- const hueKeys = keys(hue);
-
- //@ts-ignore
- res.push(...hueKeys.map((key) => hue[key]));
- }
- return res;
-};
-
-export { customConcat };
diff --git a/src/fp/object/customFindKey.ts b/src/fp/object/customFindKey.ts
deleted file mode 100644
index 2e8f58b6..00000000
--- a/src/fp/object/customFindKey.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { inRange } from '../number/inRange.js';
-import { max, min } from '../array/min_max.js';
-import { customConcat } from '../object/customConcat.js';
-
-/**
-
- *
- * @private
- * @param {Array|Object} collection The collection to inspect.
- * @param {Function} predicate The function invoked per iteration.
- * @param {number} factor The value to compare against
- * @returns {*} Returns the found element or its key, else `undefined`.
- */
-export const customFindKey = (collection: object, factor: number) => {
- // If the color is achromatic return the string gray
- const propKeys = Object.keys(collection);
-
- const result: string = propKeys
- .filter((key) => {
- const hueVals = customConcat(collection[key]);
- // @ts-ignore
- const minVal = min(...hueVals);
- // @ts-ignore
- const maxVal = max(...hueVals);
- // Capture the min and max values and see if the passed in color is within that range
- return inRange(factor, minVal, maxVal);
- })
- .toString();
-
- return result;
-};
diff --git a/src/fp/object/index.ts b/src/fp/object/index.ts
deleted file mode 100644
index bea29240..00000000
--- a/src/fp/object/index.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-// This module has object methods
-
-export { colorObj } from './colorObj';
-export { customConcat } from './customConcat';
-export { customFindKey } from './customFindKey';
diff --git a/src/fp/string/expressionParser.ts b/src/fp/string/expressionParser.ts
deleted file mode 100644
index f87b85d9..00000000
--- a/src/fp/string/expressionParser.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-// @ts-nocheck
-import { Color } from '../../paramTypes';
-
-/**
- * Performs arithmetic operations on colors by passing the arithmetic operator from the value if it is a string. It requires the src variable to be declared in the global scope of the invoking func.
- * @param src The color object.
- * @param channel The channel to set.
- * @param value The value to apply.
- */
-function expressionParser(src: Color, channel: string, value: string): number {
- // regExp to match arithmetic operator and the value
- const reOperator = /^(\*|\+|\-|\/)/;
- const reValue = /[0-9]*\.?[0-9]+/;
-
- // Storing the arithmetic sign and value
- const sign = reOperator.exec(value);
- const amt = reValue.exec(value);
-
- const cb = (amt: string) => parseFloat(amt);
-
- // Match an operator against the first truthy case and perform the relevant math operation
- switch (sign['0']) {
- case '+':
- src[channel] += +cb(amt['0']);
- break;
- case '-':
- src[channel] -= +cb(amt['0']);
- break;
- case '*':
- src[channel] *= +cb(amt['0']);
- break;
- case '/':
- src[channel] /= +cb(amt['0']);
- break;
- default:
- src[channel] = +cb(amt['0']);
- }
- return src;
-}
-export { expressionParser };
diff --git a/src/fp/string/index.ts b/src/fp/string/index.ts
deleted file mode 100644
index a45c8a6d..00000000
--- a/src/fp/string/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export { matchChromaChannel } from './matchChromaChannel';
-export { expressionParser } from './expressionParser';
diff --git a/src/fp/string/matchChromaChannel.ts b/src/fp/string/matchChromaChannel.ts
deleted file mode 100644
index 8d37c40d..00000000
--- a/src/fp/string/matchChromaChannel.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { HueColorSpaces } from '../../paramTypes';
-
-/**
- * @function
- * Matches the chroma/saturation channel of any compliant color space
- * @param colorSpace The color space to match saturation/chroma channel.
- * @returns The mode channel string passed to getChannel()
- */
-const matchChromaChannel = (colorSpace: HueColorSpaces | string): string => {
- // Matches any string with c or s
- const reChroma = /(s|c)/;
- const ch = reChroma.exec(colorSpace);
-
- if (reChroma.test(colorSpace)) {
- return `${colorSpace}.${ch[0]}`;
- } else {
- throw Error(
- `The color space ${colorSpace} has no chroma/saturation channel.`
- );
- }
-};
-
-export { matchChromaChannel };
diff --git a/src/generators.ts b/src/generators.ts
new file mode 100644
index 00000000..a5c84415
--- /dev/null
+++ b/src/generators.ts
@@ -0,0 +1,581 @@
+/* eslint-disable prefer-const */
+/**
+ * @license
+ * generators.ts - Palette utilities for generating color scales for huetiful-js.
+ * Contains colors from TailwindCSS released under the MIT permissive licence.
+ * Contains parts of chroma.js released under the Apache-2.0 license.
+Copyright 2023 Dean Tarisai.
+This file is licensed to you under the Apache License, Version 2.0 (the 'License');
+you may not use this file except in compliance with the License. You may obtain a copy
+of the License at http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software distributed under
+the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+OF ANY KIND, either express or implied. See the License for the specific language
+governing permissions and limitations under the License.
+*/
+
+import {
+ useMode,
+ modeLch,
+ easingSmoothstep,
+ samples as nativeSamples,
+ modeJch,
+ averageNumber,
+ differenceEuclidean,
+ interpolate,
+ interpolatorSplineBasis,
+ interpolatorSplineBasisClosed,
+ interpolatorSplineMonotone,
+ interpolatorSplineMonotoneClosed,
+ interpolatorSplineNatural,
+ interpolatorSplineNaturalClosed,
+ modeHsv,
+ nearest,
+ Color
+} from 'culori/fn';
+
+import type {
+ ColorToken,
+ EarthtoneOptions,
+ HueColorSpaces,
+ HueShiftOptions,
+ InterpolatorOptions,
+ PairedSchemeOptions,
+ UniformColorSpaces
+} from './types';
+import {
+ adjustHue,
+ random,
+ checkArg,
+ matchChromaChannel,
+ matchLightnessChannel,
+ max,
+ min
+} from './helpers';
+
+import { toHex, ucsConverter } from './converters';
+import { setChannel } from './utils';
+
+/**
+ *
+ * Generates a randomised classic color scheme from a single base color.
+ * @param schemeType Any classic color scheme either "analogous"|"triadic"|"tetradic"|"complementary"|"splitComplementary".
+ * @returns An array of 8 character hex codes. Elements in the array depend on the number of sample colors in the targeted scheme.
+ * @example
+ *
+ import { base } from 'huetiful-js'
+
+console.log(base("triadic")("#a1bd2f", true))
+// [ '#a1bd2fff', '#00caffff', '#ff78c9ff' ]
+ */
+
+function scheme(
+ schemeType: 'analogous' | 'triadic' | 'tetradic' | 'complementary' | string
+) {
+ /**
+ * @param color The color to use as the starting point.
+ * @param easingFunc Optional parameter to pass in a custom easing function. The default is smoothstep
+ */
+ return (
+ color: ColorToken,
+ easingFunc?: (t: number) => number
+ ): ColorToken[] => {
+ const cb = (iterations: number, distance: number, color: ColorToken) =>
+ nativeSamples(iterations).map((val) =>
+ adjustHue((color['h'] + distance) * (val * easingSmoothstep(val)))
+ );
+ schemeType = schemeType.toLowerCase();
+ easingFunc = checkArg(easingFunc, easingSmoothstep) as typeof easingFunc;
+
+ // @ts-ignore
+ color = useMode(modeJch)(color);
+ const lowMin = 0.05,
+ lowMax = 0.495,
+ highMin = 0.5,
+ highMax = 0.995;
+ const targetHueSteps = {
+ analogous: cb(3, 12, color),
+ triadic: cb(3, 120, color),
+ tetradic: cb(4, 90, color),
+ complementary: cb(2, 180, color)
+ };
+ // For each step return a random value between lowMin && lowMax multipied by highMin && highMax and 0.9 of the step
+ for (const scheme of Object.keys(targetHueSteps)) {
+ targetHueSteps[scheme].map(
+ (step: number) =>
+ random(step * lowMax, step * lowMin) +
+ random(step * highMax, step * highMin) / 2
+ );
+ }
+ // The map for steps to obtain the targeted palettes
+ const colors = targetHueSteps[schemeType].map((step: number) => ({
+ l: color['l'],
+ c: color['c'],
+ h: step * easingFunc(1 / targetHueSteps[schemeType].length),
+ mode: 'lch'
+ }));
+
+ return colors.map(toHex);
+ };
+}
+
+/**
+ *
+ * Takes an array of colors and finds the best matches for a set of predefined palettes. The function does not work on achromatic colors, you may use isAchromatic to filter grays from your collection before passing it to the function.
+ * @param colors The array of colors to create palettes from. Preferably use 5 or more colors for better results.
+ * @param schemeType (Optional) The palette type you want to return.
+ * @returns An array of colors if the scheme parameter is specified else it returns an object of all the palette types as keys and their values as an array of colors. If no colors are valid for the palette types it returns an empty array for the palette results.
+ * @example
+ *
+ * import { discoverPalettes } from 'huetiful-js'
+
+let sample = [
+ "#ffff00",
+ "#00ffdc",
+ "#00ff78",
+ "#00c000",
+ "#007e00",
+ "#164100",
+ "#720000",
+ "#600000",
+ "#4e0000",
+ "#3e0000",
+ "#310000",
+]
+
+console.log(discoverPalettes(sample, "tetradic"))
+// [ '#ffff00ff', '#00ffdcff', '#310000ff', '#720000ff' ]
+ */
+function discoverPalettes(
+ colors: ColorToken[],
+ schemeType?: 'analogous' | 'triadic' | 'tetradic' | 'complementary'
+): ColorToken[] | object {
+ const { keys } = Object;
+ const isColorEqual = (c1: ColorToken, c2: ColorToken): boolean => {
+ return c1['h'] === c2['h'] && c1['l'] === c2['l'] && c1['c'] === c2['c'];
+ };
+
+ const toLch = useMode(modeLch);
+ colors = colors.map((color) => toLch(toHex(color)));
+ const palettes = {};
+ const schemeKeys = ['analogous', 'triadic', 'tetradic', 'complementary'];
+ const targetPalettes = {};
+ for (const color of colors) {
+ schemeKeys.forEach((s) => (targetPalettes[s] = scheme(s)(color)));
+
+ for (const paletteType of keys(targetPalettes)) {
+ const palette = [];
+ let variance = 0;
+
+ for (const targetColor of targetPalettes[paletteType]) {
+ // filter out colors already in the palette
+ const availableColors = colors.filter(
+ (color1) => !palette.some((color2) => isColorEqual(color1, color2))
+ );
+
+ const match = nearest(
+ availableColors,
+ differenceEuclidean('lch')
+ )(targetColor)[0];
+
+ // @ts-ignore
+ variance += differenceEuclidean('lch')(targetColor, match);
+
+ palette.push(match);
+ }
+
+ if (!palettes[paletteType] || variance < palettes[paletteType].variance) {
+ palettes[paletteType] = palette.map(toHex);
+ }
+ }
+ }
+
+ if (typeof schemeType === 'string') {
+ return palettes[schemeType.toLowerCase()];
+ } else if (typeof schemeType === 'undefined') {
+ return palettes;
+ } else {
+ throw Error(
+ `${schemeType} is not a valid scheme. The schemes are triadic | tetradic | analogous | complementary`
+ );
+ }
+}
+
+/**
+ *
+ * Creates a scale of a spline based interpolation between an earthtone and a color.
+ * @param color The color to interpolate an earth tone with.
+ * @param options Optional overrides for customising interpolation and easing functions.
+ * @returns The array of colors resulting from the earthtone interpolation as hex codes.
+ * @example
+ *
+ * import { earthtone } from 'huetiful-js'
+
+
+console.log(earthtone("pink",{earthtones:'clay',iterations:5 }))
+// [ '#6a5c52ff', '#8d746aff', '#b38d86ff', '#d9a6a6ff', '#ffc0cbff' ]
+
+ */
+
+function earthtone(
+ color: ColorToken,
+ colorspace?: HueColorSpaces,
+ options?: EarthtoneOptions
+): ColorToken[] {
+ let { samples: iterations, earthtones } = options || {};
+
+ iterations = checkArg(iterations, 1) as number;
+
+ earthtones = checkArg(earthtones, 'dark') as typeof earthtones;
+ const tones = {
+ 'light-gray': '#e5e5e5',
+ silver: '#f5f5f5',
+ sand: '#c2b2a4',
+ tupe: '#a79e8a',
+ mahogany: '#958c7c',
+ 'brick-red': '#7d7065',
+ clay: '#6a5c52',
+ cocoa: '#584a3e',
+ 'dark-brown': '#473b31',
+ dark: '#352a21'
+ };
+
+ const base: ColorToken = tones[earthtones.toLowerCase()];
+
+ const f = interpolator([base, toHex(color)], colorspace);
+
+ return ((iterations === 1 && toHex(f(0.5))) ||
+ nativeSamples(iterations).map((t) => toHex(f(t)))) as ColorToken[];
+}
+
+/**
+ *
+ * Generates a palette of hue shifted colors (as a color becomes lighter, its hue shifts up and darker when its hue shifts down. ) from a single base color. Min and max lightness value determine how light or dark our colour will be at either extreme.
+ * @param color The color to use as the base of the hueshift. Colors are internally converted to LCH.
+ * @param options The optional overrides object to customize per channel options like interpolation methods and channel fixups.
+ *@returns An array of colors in hex. The length of the resultant array is the number of iterations multiplied by 2 plus the base color passed or (iterations*2)+1
+ * @example
+ * import { hueShift } from "huetiful-js";
+
+let hueShiftedPalette = hueShift("#3e0000");
+
+console.log(hueShiftedPalette);
+
+// [
+ '#ffffe1', '#ffdca5',
+ '#ca9a70', '#935c40',
+ '#5c2418', '#3e0000',
+ '#310000', '#34000f',
+ '#38001e', '#3b002c',
+ '#3b0c3a'
+]
+ */
+
+function hueShift(
+ color: ColorToken,
+ colorspace?: UniformColorSpaces,
+ options?: HueShiftOptions
+): ColorToken[] {
+ const lightnessMapper =
+ (n: number) =>
+ (start1: number, end1: number) =>
+ (start2: number, end2: number) =>
+ ((n - start1) / (end1 - start1)) * (end2 - start2) + start2;
+
+ color = ucsConverter(colorspace)(color as string);
+
+ let {
+ samples: iterations,
+ hueStep,
+ minLightness,
+ maxLightness,
+ easingFunc
+ } = options || {};
+ const [l, c] = [
+ matchLightnessChannel(colorspace).split('.')[1],
+ matchChromaChannel(colorspace).split('.')[1]
+ ];
+ // Pass default values in case the options object is overridden
+ easingFunc = checkArg(easingFunc, easingSmoothstep) as typeof easingFunc;
+ iterations = (checkArg(iterations, 6) as number) + 1;
+ hueStep = checkArg(hueStep, 5) as number;
+ (minLightness = checkArg(minLightness, 10) as number),
+ (maxLightness = checkArg(maxLightness, 90) as number);
+ // Pass in default values if any of the opts is undefined
+ const palette: ColorToken[] = [color];
+ // Maximum number of iterations possible.
+ //Each iteration add a darker shade to the start of the array and a lighter tint to the end.
+ for (let i = 1; i < iterations; i++) {
+ //adjustHue checks hue values are clamped.
+
+ // Here we use lightnessMapper to calculate our lightness values which takes a number that exists in range [0,1].
+
+ const [colorShiftDown, colorShiftUp] = [
+ {
+ [l]: lightnessMapper(i)(0.1, iterations)(color[l], minLightness),
+ [c]: color[c],
+ h: adjustHue(color['h'] - hueStep * (i * easingFunc(i))),
+ mode: colorspace
+ },
+ {
+ [l]: lightnessMapper(i)(0.15, iterations)(color[l], maxLightness),
+ [c]: color[c],
+ h: adjustHue(color['h'] + hueStep * (i * easingFunc(i))),
+ mode: colorspace
+ }
+ ];
+ palette.push(colorShiftUp);
+ palette.unshift(colorShiftDown);
+ }
+ return Array.from(new Set(palette)).map(toHex);
+}
+/**
+ *
+ * Returns a spline based interpolator function with customizable interpolation methods (passed in as 'kind') and optional channel specific overrides.
+ * @param colors The array of colors to interpolate. If a color has a falsy channel for example black has an undefined hue channel some interpolation methods may return NaN affecting the final result.
+ * @param colorspace The colorspace to perform the color space in. Prefer uniform color spaces for better results such as Lch or Jch.
+ * @param kind The type of the spline interpolation method. Default is basis.
+ * @param closed Optional parameter to return the 'closed' variant of the 'kind' of interpolation method which can be useful for cyclical color scales. Default is false
+ * @param options Optional channel specific overrides.
+ * @returns A hexadecimal representation of the resultant color.
+ */
+function interpolateSpline(
+ colors: ColorToken[],
+ colorspace?: HueColorSpaces,
+ samples?: number,
+ kind?: 'natural' | 'monotone' | 'basis',
+ closed = false,
+ options?: InterpolatorOptions
+): ColorToken[] {
+ let {
+ chromaInterpolator,
+ hueFixup,
+ hueInterpolator,
+ lightnessInterpolator,
+ easingFunc
+ } = checkArg(options, {}) as InterpolatorOptions;
+ // Set the internal defaults
+ easingFunc = checkArg(easingFunc, easingSmoothstep) as typeof easingFunc;
+ kind = checkArg(kind, 'basis') as typeof kind;
+
+ let func;
+ switch (kind) {
+ case 'basis':
+ func =
+ (closed && interpolatorSplineBasisClosed) || interpolatorSplineBasis;
+ break;
+ case 'monotone':
+ func =
+ (closed && interpolatorSplineMonotoneClosed) ||
+ interpolatorSplineMonotone;
+ break;
+ case 'natural':
+ func =
+ (closed && interpolatorSplineNaturalClosed) ||
+ interpolatorSplineNatural;
+ break;
+ }
+ // @ts-ignore
+ let f = interpolate([...colors, easingFunc], colorspace, {
+ h: {
+ //@ts-ignore
+ fixup: hueFixup,
+ use: checkArg(hueInterpolator, func)
+ },
+ [matchChromaChannel(colorspace as string)]: {
+ use: checkArg(chromaInterpolator, func)
+ },
+ [matchLightnessChannel(colorspace as string)]: {
+ use: checkArg(lightnessInterpolator, func)
+ }
+ });
+
+ // make sure samples is an absolute integer
+ samples =
+ (typeof samples === 'number' && samples >= 1 && samples) ||
+ Math.ceil(Math.abs(samples));
+
+ let result: string[];
+ if (samples > 1) {
+ result = nativeSamples(samples).map((s) => toHex(f(s)));
+ } else {
+ //@ts-ignore
+ result = result.push(toHex(f(0.5)));
+ }
+ return result;
+}
+
+function interpolator(
+ colors: ColorToken[],
+ colorspace?: HueColorSpaces,
+ options?: object
+) {
+ let {
+ chromaInterpolator,
+ hueFixup,
+ hueInterpolator,
+ lightnessInterpolator,
+ easingFunc
+ } = checkArg(options, {}) as InterpolatorOptions;
+ return interpolate(
+ [
+ ...(colors as Array),
+ checkArg(easingFunc, interpolator['easingFunc']) as typeof easingFunc
+ ],
+ checkArg(colorspace, 'jch') as typeof colorspace,
+ {
+ //@ts-ignore
+ h: {
+ fixup: hueFixup,
+
+ // @ts-ignore
+ use: checkArg(hueInterpolator, interpolator['hueInterpolator'])
+ },
+ [matchChromaChannel(colorspace)]: {
+ use: checkArg(chromaInterpolator, interpolator['chromaInterpolator'])
+ },
+ [matchLightnessChannel(colorspace)]: {
+ use: checkArg(
+ lightnessInterpolator,
+ interpolator['lightnessInterpolator']
+ )
+ }
+ }
+ );
+}
+
+/**
+ * pairedScheme
+ * Creates a scheme that consists of a base color that is incremented by a hueStep to get the final hue to pair with.The colors are interpolated via white or black.
+ * @param color The color to return a paired color scheme from.
+ * @param options The optional overrides object to customize per channel options like interpolation methods and channel fixups.
+ * @returns An array containing the paired scheme.
+ * @example
+ *
+ * import { pairedScheme } from 'huetiful-js'
+
+console.log(pairedScheme("green",{hueStep:6,iterations:4,tone:'dark'}))
+// [ '#008116ff', '#006945ff', '#184b4eff', '#007606ff' ]
+ */
+function pairedScheme(
+ color: ColorToken,
+ options?: PairedSchemeOptions
+): ColorToken[] | ColorToken {
+ // eslint-disable-next-line prefer-const
+ let { samples: iterations, via, hueStep, easingFunc } = options || {};
+
+ iterations = checkArg(iterations, 1) as number;
+ easingFunc = checkArg(easingFunc, easingSmoothstep) as typeof easingFunc;
+ via = checkArg(via, 'light') as typeof via;
+ hueStep = checkArg(hueStep, 5) as number;
+
+ const toLch = useMode(modeLch);
+ color = toLch(toHex(color));
+
+ // get the hue of the passed in color and add it to the step which will result in the final color to pair with
+ const derivedHue = setChannel('lch.h')(color, color['h'] + hueStep);
+
+ // Set the tones to color objects with hardcoded hue values and lightness channels clamped at extremes
+ const tones = {
+ dark: { l: 0, c: 0, h: 0, mode: 'lch65' },
+ light: { l: 100, c: 0, h: 0, mode: 'lch65' }
+ };
+
+ const scale = interpolate(
+ [color as unknown as string, tones[via as string], derivedHue, easingFunc],
+ 'lch',
+ checkArg(options, interpolator)
+ );
+
+ if (iterations <= 1) {
+ return toHex(scale(0.5));
+ } else {
+ // Declare the num of iterations in samples() which will be used as the t value
+ // Since the interpolation returns half duplicate values we double the sample value
+ // Guard the num param against negative values and floats
+ const smp = nativeSamples(iterations * 2);
+
+ //The array to capture the different iterations
+ const results: ColorToken[] = smp.map((t) => toHex(scale(easingFunc(t))));
+ // Return a slice of the array from the start to the half length of the array
+ return results.slice(0, results.length / 2);
+ }
+}
+
+/**
+ *
+ * Returns a random pastel variant of the passed in color.
+ * @param color The color to return a pastel variant of.
+ * @returns A random pastel color.
+ * @example
+ *
+ *
+import { pastel } from 'huetiful-js'
+
+console.log(pastel("green"))
+// #036103ff
+ */
+function pastel(color: ColorToken): ColorToken {
+ const samplePastelObj = [
+ {
+ color: '#fea3aa',
+ saturation: 0.35826771653543305,
+ value: 0.996078431372549
+ },
+ {
+ color: '#f8b88b',
+ saturation: 0.43951612903225806,
+ value: 0.9725490196078431
+ },
+ { color: '#faf884', saturation: 0.472, value: 0.9803921568627451 },
+ {
+ color: '#f2a2e8',
+ saturation: 0.3305785123966942,
+ value: 0.9490196078431372
+ },
+ {
+ color: '#b2cefe',
+ saturation: 0.2992125984251969,
+ value: 0.996078431372549
+ },
+ {
+ color: '#baed91',
+ saturation: 0.3881856540084388,
+ value: 0.9294117647058824
+ }
+ ];
+
+ const sampleSaturation = samplePastelObj.map((el) => el['saturation']);
+ const sampleValues = samplePastelObj.map((el) => el['value']);
+
+ const pastelSample = {
+ averageSaturation: averageNumber(sampleValues),
+ averageValue: averageNumber(sampleSaturation),
+ minSampleSaturation: min(sampleSaturation),
+ maxSampleSaturation: max(sampleSaturation),
+ minSampleValue: min(sampleValues),
+ maxSampleValue: max(sampleValues)
+ };
+
+ const toHsv = useMode(modeHsv);
+ color = toHsv(toHex(color));
+ // For now we're simply returning an hsv object with the s and v channel set to the averages
+ return toHex({
+ h: color['h'],
+ s: pastelSample['averageSaturation'],
+ v: random(pastelSample['minSampleValue'], pastelSample['maxSampleValue']),
+ mode: 'hsv'
+ });
+}
+
+export {
+ discoverPalettes,
+ hueShift,
+ pairedScheme,
+ pastel,
+ scheme,
+ interpolateSpline,
+ interpolator,
+ earthtone,
+ ucsConverter
+};
diff --git a/src/getters_and_setters/alpha.ts b/src/getters_and_setters/alpha.ts
deleted file mode 100644
index 49b4d3ac..00000000
--- a/src/getters_and_setters/alpha.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-//@ts-nocheck
-import { useMode, modeLch } from 'culori/fn';
-import { inRange } from '../fp/number/inRange.js';
-import { expressionParser } from '../fp/string/expressionParser.js';
-import { toHex } from '../converters/toHex.js';
-import type { Color } from '../paramTypes.js';
-
-/**
- * @function
- * @description Sets the opacity of a color. Also gets the alpha value of the color if the value param is omitted
- * @param color The color with the targeted opacity/alpha channel.
- * @param value The value to apply to the opacity channel. The value is between [0,1]
- * @returns color The resulting color. Returns an 8 character hex code.
- * @example
- *
- * // Getting the alpha
-console.log(alpha('#a1bd2f0d'))
-// 0.050980392156862744
-
-// Setting the alpha
-
-let myColor = alpha('b2c3f1', 0.5)
-
-console.log(myColor)
-
-// #b2c3f180
- */
-
-const alpha = (color: Color, value?: number | string): Color | number => {
- // We never perfom an operation on an undefined color. Defaults to pure black
- color = color || 'black';
-
- const channel = 'alpha';
- const lch = useMode(modeLch);
- const src: Color = lch(toHex(color));
- if (typeof value === 'undefined' || null) {
- return src[channel];
- } else if (typeof value === 'number') {
- if (inRange(value, 0, 1)) {
- src[channel] = value;
- } else {
- src[channel] = value / 100;
- }
- } else if (typeof value === 'string') {
- expressionParser(src, channel, value);
- }
- return formatHex8(src);
-};
-
-export { alpha };
diff --git a/src/getters_and_setters/darken.ts b/src/getters_and_setters/darken.ts
deleted file mode 100644
index 96c3169a..00000000
--- a/src/getters_and_setters/darken.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-//@ts-nocheck
-import { easingSmootherstep, modeLab, useMode } from 'culori/fn';
-import { toHex } from '../converters/toHex';
-import { expressionParser } from '../fp/string/expressionParser';
-import type { Color } from '../paramTypes';
-// ported froma chroma-js brighten
-const toLab = useMode(modeLab);
-/**
- * @function
- * @description Darkens the color by reducing the lightness channel. .
- * @param color The color to darken.
- * @param value The amount to darken with. Also supports expressions as strings e.g darken("#fc23a1","*0.5")
- * @returns color The darkened color.
- * @example
- *
- *
-
- */
-const darken = (color: Color, value: number | string): Color => {
- const Kn = 18;
- const channel = 'l';
-
- const src = toLab(toHex(color));
-
- if (typeof value === 'number') {
- src['l'] -= Kn * easingSmootherstep(value / 100);
- } else if (typeof value === 'string') {
- expressionParser(src, channel, value || 1);
- }
-
- return toHex(src);
-};
-
-/**
- *
- * @param color The color to brighten.
- * @param value The amount to brighten with. Also supports expressions as strings e.g darken("#fc23a1","*0.5")
- * @param mode The color space to compute the color in. Any color space with a lightness channel will do (including HWB)
- * @returns
- */
-const brighten = (color: Color, value: number | string): Color => {
- const src = toLab(toHex(color));
- const channel = 'l';
-
- if (typeof value == 'number') {
- value = Math.abs(value);
- src['l'] -= Kn * easingSmootherstep(value / 100);
- } else if (typeof value == 'string') {
- expressionParser(src, channel, value);
- }
-
- return toHex(src);
-};
-
-export { brighten, darken };
diff --git a/src/getters_and_setters/get.ts b/src/getters_and_setters/get.ts
deleted file mode 100644
index 9c8c08a7..00000000
--- a/src/getters_and_setters/get.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-//@ts-nocheck
-
-import { converter } from 'culori/fn';
-import 'culori/css';
-import { toHex } from '../converters/toHex.ts';
-import type { Color } from '../paramTypes';
-
-/**
- * @function
- * @description Gets the value specifified channel on the color.
- * @param mc The mode and channel to be retrieved. For example "rgb.b" will return the value of the blue channel in the RGB color space of that color.
- * @param color The color being queried.
- * @returns value The value of the queried channel.
- * @example
- *
- * import { getChannel } from 'huetiful-js'
-
-console.log(getChannel('rgb.g')('#a1bd2f'))
-// 0.7411764705882353
- * */
-const getChannel =
- (mc: string) =>
- (color: Color): number => {
- const [mode, channel] = mc.split('.');
- const src = converter(mode)(toHex(color));
-
- if (channel) {
- return src[channel];
- } else {
- throw Error(`unknown channel ${channel} in mode ${mode}`);
- }
- };
-
-export { getChannel };
diff --git a/src/getters_and_setters/index.ts b/src/getters_and_setters/index.ts
deleted file mode 100644
index 2b1caf28..00000000
--- a/src/getters_and_setters/index.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export { alpha } from './alpha';
-export { brighten, darken } from './darken';
-export { getChannel } from './get';
-export { setLuminance, getLuminance } from './luminance';
-export { setChannel } from './set';
diff --git a/src/getters_and_setters/luminance.ts b/src/getters_and_setters/luminance.ts
deleted file mode 100644
index 767428de..00000000
--- a/src/getters_and_setters/luminance.ts
+++ /dev/null
@@ -1,102 +0,0 @@
-//@ts-nocheck
-import { interpolate, wcagLuminance, useMode, modeRgb } from 'culori/fn';
-import type { Color } from '../paramTypes.js';
-import { toHex } from '../converters/toHex.js';
-/** @alias
- * Gets the luminance value of that color as defined by WCAG.
- * @param color The color to query.
- * @returns value The color's luminance value.
- * @example
- *
- * import { getLuminance } from 'huetiful-js'
-
-console.log(getLuminance('#a1bd2f'))
-// 0.4417749513730954
- */
-const getLuminance = (color: Color): number => wcagLuminance(hex(color));
-
-const { pow, abs } = Math;
-const toRgb = useMode(modeRgb);
-/**
- * @function
- * @description Sets the luminance by interpolating the color with black (to decrease luminance) or white (to increase the luminance).
- * @param color The color to set luminance
- * @param lum The amount of luminance to set. The value range is normalised between [0,1]
- * @returns The mutated color with the modified properties.
- * @example
- *
- * import { setLuminance, getLuminance } from 'huetiful-js'
-
-let myColor = setLuminance('#a1bd2f', 0.5)
-
-console.log(getLuminance(myColor))
-// 0.4999999136285792
- */
-const setLuminance = (color: Color, lum: number): Color => {
- const white = '#ffffff',
- black = '#000000';
-
- const EPS = 1e-7;
- let MAX_ITER = 20;
-
- if (lum !== undefined && typeof lum == 'number') {
- (lum == 0 && lum) || black || (lum == 1 && !lum) || white;
-
- // compute new color using...
-
- const cur_lum = wcagLuminance(color);
-
- color = toRgb(hex(color));
-
- const test = (low: Color, high: Color) => {
- //Must add the overrides object to change parameters like easings, fixups, and the mode to perform the computations in.
- const mid = interpolate([low, high])(0.5);
- const lm = wcagLuminance(mid);
- if (abs(lum - lm > EPS) || !MAX_ITER--) {
- // close enough
- return mid;
- }
-
- if (lm > lum) {
- return test(low, mid);
- } else {
- return test(mid, high);
- }
- };
-
- let rgb: Color;
- if (cur_lum > lum) {
- rgb = test(black, color);
- } else {
- rgb = test(color, white);
- }
- color = rgb;
- return color;
- }
- // spreading the array values (r,g,b)
-
- return rgb2luminance(color);
-};
-
-const rgb2luminance = (color: Color): number => {
- color = toRgb(toHex(color));
-
- // relative luminance
- // see http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
- return (
- 0.7152 * luminance_x(color['g']) +
- 0.2126 * luminance_x(color['r']) +
- 0.0722 * luminance_x(color['b'])
- );
-};
-
-const luminance_x = (x: number) => {
- x /= 255;
- if (x <= 0.03928) {
- return x / 12.92;
- } else {
- return pow((x + 0.055) / 1.055, 2.4);
- }
-};
-
-export { setLuminance, getLuminance };
diff --git a/src/getters_and_setters/set.ts b/src/getters_and_setters/set.ts
deleted file mode 100644
index 98196f47..00000000
--- a/src/getters_and_setters/set.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-//@ts-nocheck
-
-// ported from chroma-js Color.set
-
-import { converter } from 'culori/fn';
-import 'culori/css';
-import { toHex } from '../converters/toHex.js';
-import { expressionParser } from '../fp/string/expressionParser.js';
-import type { Color } from '../paramTypes.js';
-/**
- * @function
- *@description Sets the value for the specified channel in a color.
- * @param color Any recognizable color token.
- * @param mc The mode and channel to work with. For example 'rgb.b'.
- * @param value The value to set on the queried channel. Also supports expressions as strings e.g set('lch.c)("#fc23a1","*0.5")
- * @returns color The mutated color.
- *
- * @example
- *
- * import { setChannel } from 'huetiful-js'
-
-let myColor = setChannel('lch.h')('green',10)
-
-console.log(getChannel('lch.h')(myColor))
-// 10
- */
-
-const setChannel =
- (mc: string) =>
- (color: Color, value: number | string): Color => {
- const [mode, channel] = mc.split('.');
- const src: Color = converter(mode)(toHex(color));
-
- if (channel) {
- if (typeof value === 'number') {
- src[channel] = value;
- } else if (typeof value === 'string') {
- expressionParser(src, channel, value);
- } else {
- throw new Error(`unsupported value for setChannel`);
- }
-
- return src;
- } else {
- throw new Error(`unknown channel ${channel} in mode ${mode}`);
- }
- };
-
-export { setChannel };
diff --git a/src/helpers.ts b/src/helpers.ts
new file mode 100644
index 00000000..56760108
--- /dev/null
+++ b/src/helpers.ts
@@ -0,0 +1,668 @@
+/* eslint-disable no-ternary */
+/**
+ * @preserve
+ * @license
+ * helpers.ts - Helper functions for huetiful-js.
+ * Contains colors from TailwindCSS released under the MIT permissive licence.
+ * Contains parts of chroma.js released under the Apache-2.0 license.
+Copyright 2023 Dean Tarisai.
+This file is licensed to you under the Apache License, Version 2.0 (the 'License');
+you may not use this file except in compliance with the License. You may obtain a copy
+of the License at http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software distributed under
+the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+OF ANY KIND, either express or implied. See the License for the specific language
+governing permissions and limitations under the License.
+*/
+
+import { getChannel } from './utils';
+import type {
+ callback,
+ Factor,
+ ColorToken,
+ HueColorSpaces,
+ Order,
+ Options,
+ Colorspaces
+} from './types';
+import modeRanges from './color-maps/samples/modeRanges';
+
+import {
+ interpolatorSplineNatural,
+ fixupHueShorter,
+ interpolatorSplineBasisClosed,
+ interpolatorSplineMonotone,
+ easingSmoothstep
+} from 'culori/fn';
+import { ucsConverter } from './generators';
+
+/**
+ * @internal
+ * Returns the first truthy value.
+ * @param arg The value to check
+ * @param def The value to cast if arg is falsy
+ * @returns The first truthy value
+ */
+function checkArg(arg: unknown, def: unknown): unknown {
+ return arg || def;
+}
+
+// Global interpolator options defaults
+let {
+ chromaInterpolator,
+ easingFunc,
+ hueFixup,
+ hueInterpolator,
+ lightnessInterpolator
+}: Options = {};
+
+chromaInterpolator = interpolatorSplineNatural;
+hueFixup = fixupHueShorter;
+hueInterpolator = interpolatorSplineBasisClosed;
+easingFunc = easingSmoothstep;
+lightnessInterpolator = interpolatorSplineMonotone;
+
+const interpolatorConfig = {
+ easingFunc,
+ chromaInterpolator,
+ hueFixup,
+ hueInterpolator,
+ lightnessInterpolator
+};
+
+/**
+ * @internal
+ * Gets the clipped string of a passed in colorspace by removing non-channel characters.
+ * @param colorspace The colorspace to get the channel keys.
+ * @param index Optional index to return a single specified channel.
+ * @returns A string.
+ *
+ @example
+
+ console.log(getModeChannel("oklch"));
+// lch
+
+console.log(getModeChannel("okhsl", 2));
+// l
+
+ */
+function getModeChannel(colorspace: Colorspaces | string, index?: number) {
+ const result = colorspace.substring(colorspace.length - 3);
+
+ return (index && result.charAt(index)) || result;
+}
+
+/**
+ * @internal
+ * Takes an arithmetic operator followed by a value and passes the result of the expression to the specified channel. Currently supports addition,subtraction,division and multiplication symbols only.
+ * @param color The color.
+ * @param modeChannel The colorspace channel to set.
+ * @param expression The expression assignment as a string.
+ * @example
+ *
+ * console.log(lch('blue'));
+// { mode: 'lch',l: 29.568297153444703,c: 131.2014771995311,h: 301.36428148973533}
+
+console.log(expressionParser('blue', 'lch.l', '*0.3'));
+// { mode: 'lch',l: 8.87048914603341,c: 131.2014771995311,h: 301.36428148973533 }
+
+ */
+function expressionParser(
+ color: ColorToken,
+ modeChannel: string,
+ expression: string
+): number {
+ // regExp to match arithmetic operator and the value
+ const sign = /^(\*|\+|\-|\/)/.exec(expression)['0'],
+ value = /[0-9]*\.?[0-9]+/.exec(expression)['0'];
+ const [mode, channel] = modeChannel.split('.');
+
+ // @ts-ignore
+ color = ucsConverter(mode.toString().toLowerCase())(color);
+ const cb = (value: string) => parseFloat(value);
+
+ // Match an operator against the first truthy case and perform the relevant math operation
+ switch (sign) {
+ case '+':
+ color[channel] += +cb(value);
+ break;
+ case '-':
+ color[channel] -= +cb(value);
+ break;
+ case '*':
+ color[channel] *= +cb(value);
+ break;
+ case '/':
+ color[channel] /= +cb(value);
+ break;
+ // throw error alert
+ }
+
+ // @ts-ignore
+ return color;
+}
+
+/**
+ * @internal
+ * @function
+ * Matches the chroma/saturation channel of any compliant color space
+ * @param colorspace The color space to match saturation/chroma channel.
+ * @returns The mode channel string passed to getChannel()
+ * @example
+ *
+ * import { matchChromaChannel } from 'huetiful-js'
+ * console.log(matchChromaChannel("jch"));
+// jch.c
+
+console.log(matchChromaChannel("okhsl"));
+// okhsl.s
+ */
+function matchChromaChannel(colorspace: HueColorSpaces | string): string {
+ // Matches any string with c or s
+ colorspace = checkArg(colorspace, 'jch') as HueColorSpaces;
+ const reChroma = /(s|c)/i;
+ // @ts-ignore
+ const ch = reChroma.exec(colorspace)['0'];
+
+ // @ts-ignore
+ if (reChroma.test(colorspace)) {
+ return `${colorspace}.${ch}`;
+ } else {
+ throw Error(
+ `The color space ${colorspace} has no chroma/saturation channel.`
+ );
+ }
+}
+
+/**
+ * @internal
+ * @function
+ * Matches the lightness channel of any compliant color space
+ * @param colorspace The color space to match lightness channel.
+ * @returns The mode channel string passed to getChannel
+ *
+ * @example
+ *
+ * console.log(matchLightnessChannel("jch"));
+// jch.j
+
+console.log(matchLightnessChannel("okhsl"));
+// okhsl.l
+ */
+function matchLightnessChannel(colorspace: HueColorSpaces | string): string {
+ // Matches any string with c or s
+ colorspace = checkArg(colorspace, 'jch') as HueColorSpaces;
+ const reLightness = /(j|l)/i;
+ // @ts-ignore
+ const ch = reLightness.exec(colorspace)['0'];
+
+ // @ts-ignore
+ if (reLightness.test(colorspace)) {
+ // @ts-ignore
+ return `${colorspace}.${ch}`;
+ } else {
+ throw Error(`The color space ${colorspace} has no lightness channel.`);
+ }
+}
+
+function colorObj(factor: Factor, callback: callback) {
+ return (color: ColorToken) => {
+ // @ts-ignore
+ return { [factor]: callback(color), color: color };
+ };
+}
+
+/**
+ * @internal
+
+ *
+ * @private
+ * @param {Array|Object} collection The collection to inspect.
+ * @param {Function} predicate The function invoked per iteration.
+ * @param {number} factor The value to compare against
+ * @returns {*} Returns the found element or its key, else `undefined`.
+ */
+function customFindKey(collection: object, factor: number) {
+ // If the color is achromatic return the string gray
+ const propKeys = Object.keys(collection);
+
+ const result: string | undefined = propKeys
+ .filter((key) => {
+ const hueVals = customConcat(collection[key]);
+ // @ts-ignore
+ const minVal = min(...hueVals);
+ // @ts-ignore
+ const maxVal = max(...hueVals);
+ // Capture the min and max values and see if the passed in color is within that range
+ return inRange(factor, minVal, maxVal);
+ })
+ .toString();
+
+ return result;
+}
+
+function customConcat(hue: object): number[] {
+ const res = [];
+ const { keys } = Object;
+ if (typeof hue == 'object') {
+ const hueKeys = keys(hue);
+
+ //@ts-ignore
+ res.push(...hueKeys.map((key) => hue[key]));
+ }
+ // @ts-ignore
+ return res.flat(1);
+}
+
+/**
+ * @internal
+ *
+ * @param value The hue angle to normalize.
+ * @returns The normalized hue angle or passed in value if it was within [0,360]
+ *
+ * @example
+ *
+ * console.log(adjustHue(4));
+// 4
+
+console.log(adjustHue(444));
+// 84
+ */
+function adjustHue(value: number) {
+ return (value > 0 && (value += Math.ceil(-value / 360) * 360)) || value % 360;
+}
+
+/**
+ * @internal
+ * Returns the channel value difference between the passed in colors. They are both converted to the colorspace in the modeChannel parameter before values are computed.
+ * @param color The color to subtract values from/
+ * @param modeChannel The colorspace and channel string to perform the operation in.
+ * @returns The difference between the color channel(s)
+ * @example
+ *
+ *
+ */
+function channelDifference(color: ColorToken, modeChannel: string) {
+ /**
+ * @internal
+ * @param subtrahend The color to use as subtrahend
+ */
+ return (subtrahend: ColorToken) => {
+ const cb = (color: ColorToken) => getChannel(modeChannel)(color);
+ if (cb(color) < cb(subtrahend)) {
+ return cb(subtrahend) - cb(color);
+ } else {
+ return cb(color) - cb(subtrahend);
+ }
+ };
+}
+
+// Comparison operators
+function gt(x: number, y: number): boolean {
+ return x > y;
+}
+function lt(x: number, y: number): boolean {
+ return x < y;
+}
+function gte(x: number, y: number): boolean {
+ return x >= y;
+}
+function lte(x: number, y: number): boolean {
+ return x <= y;
+}
+function eq(x: number, y: number): boolean {
+ return x === y;
+}
+
+/**
+ * @internal
+ * @function
+ * Checks if a value is within the start and end range.
+ * @param number The number to check.
+ * @param start The minimum or starting value.
+ * @param end The maximum or starting value.
+ * @returns True if the number is in range else false.
+ */
+
+function inRange(number: number, start: number, end?: number): boolean {
+ /* Built-in method references for those with the same name as other `lodash` methods. */
+ Math.min;
+ return number >= Math.min(start, end) && number < Math.max(start, end);
+}
+
+/**
+ * @internal
+ * Checks if a number is an integer or float.
+ * @param num The number to query
+ * @returns True if the number is an integer else false if it is a float.
+ */
+function isInteger(num: number | string) {
+ const reInt = /^-?[0-9]+$/;
+ return reInt.test(num.toString());
+}
+
+/**
+ * @internal
+ * @function
+ * Normalizes passed in channel value to a range accepted by color spaces as defined in Culori.
+ * @param value The value to chec if its in the accepted range for the passed in mode channel
+ * @param modeChannel A string defining the mode and channel ranges to use for comparison
+ * @returns The normalized channel value or the passed in value if it was within range
+ */
+function normalize(value: number, modeChannel: string): number {
+ const [mode, channel]: string[] = modeChannel.split('.');
+ const [start, end]: number[] = modeRanges[mode][channel];
+ const range = inRange(value, start, end);
+
+ if (!range) {
+ if (inRange(value, 0, 1)) {
+ value = end * value;
+ } else if (inRange(value, 1, 150)) {
+ value = end * (value / 100);
+ } else {
+ throw Error(
+ `The value ${value} is out of range for channel ${channel} of colorspace ${mode} can only accept a value between [0,1] or [0,100] with 0.5 or 50 being half of the channel range.`
+ );
+ }
+ }
+ return value;
+}
+
+/**
+ * @internal
+ * @function
+ * Returns a random number between minimum and maximum bounds.
+ * @param min The lower bound.
+ * @param max The upper bound.
+ * @returns A number.
+ */
+function random(min: number, max: number): number {
+ if (min > max) {
+ const mn = min;
+ const mx = max;
+ max = mn;
+ min = mx;
+ } else {
+ return Math.random() * (max - min) + min;
+ }
+}
+
+const { ceil, floor } = Math;
+/**
+ * @internal
+ * @function
+ * Rounds up or down a number based on the float value.
+ * @param num The number to round up or down.
+ * @returns An integer
+ * @example
+ * console.log(floorCeil(1.45));
+// 1
+console.log(floorCeil(1.501));
+// 2
+
+ */
+
+function floorCeil(num: number): number {
+ if (!isInteger(num)) {
+ const strArr = num.toString().split('.');
+ const float = strArr[1];
+
+ //If the decimal value is .4 and below it will be rounded down else it will be rounded up.
+ const reFloorCeil = (float: string) => /^[0-4]$/.test(float.charAt(0));
+
+ if (reFloorCeil(float)) {
+ num = floor(num);
+ } else {
+ num = ceil(num);
+ }
+ }
+
+ return num;
+}
+
+/**
+ * @internal
+ * Helper function for native sorting method for arrays.
+ * @param factor The property to query.
+ * @param order Either ascending or descending.
+ * @returns A sorted array.
+ */
+function customSort(order: Order, factor?: Factor | string) {
+ // Special thanks to deechris27 on youtube
+ // a-b gives asc order & b-a gives desc order
+ factor = factor || 'factor';
+ return (a, b) => {
+ if (order === 'asc') {
+ return a[factor] - b[factor];
+ } else if (order === 'desc') {
+ return b[factor] - a[factor];
+ }
+ };
+}
+
+/*
+ * @function
+ * Creates a custom object with a factor to pass to the predicate function.
+ * @param factor The quality being queried.
+ * @param cb The callback function for computing the factor's start.
+ * @returns An array of objects.
+ */
+function colorObjArr(factor: Factor, callback) {
+ /**
+ * @internal
+ *
+ * @param colors The array or object of colors to iterate over. If an object is passed, its values are expected to be valid color tokens.
+ */
+ return (
+ collection: ColorToken[] | object | object
+ ): Array<{ factor: Factor; color: ColorToken }> => {
+ const cb = colorObj(factor, callback);
+ // @ts-ignore
+ return Object.keys(collection).map((color) => cb(colors[color]));
+ };
+}
+
+/**
+ * @internal
+ * Returns the passed in value
+ * @param value The value to return
+ * @returns
+ */
+function identity(value) {
+ return value;
+}
+function baseExtremum(array: number[], iteratee, comparator) {
+ var [index, length] = [-1, array.length];
+
+ while (++index < length) {
+ var [value, current] = [array[index], iteratee(value)];
+
+ if (
+ current != null &&
+ (computed === undefined
+ ? current === current
+ : comparator(current, computed))
+ ) {
+ var computed = current,
+ result = value;
+ }
+ }
+ return result;
+}
+/**
+ * @internal
+ * Gets the smallest value in an array
+ * @param array The array to retrieve minimum value
+ * @returns The smallest number in the array
+ * @example
+ * console.log(min([0, 3, 4]));
+// 0
+ *
+ */
+function min(array: number[]): number {
+ return (
+ (array && array.length && baseExtremum(array, identity, lt)) || undefined
+ );
+}
+/**
+ * @internal
+ * Gets the largest value in an array
+ * @param array The array to retrieve maximum value
+ * @returns The largest number in the array
+ * @example
+ * console.log(max([0, 3, 4]));
+// 4
+ */
+function max(array: number[]): number {
+ return (
+ (array && array.length && baseExtremum(array, identity, gt)) || undefined
+ );
+}
+
+/**
+ * @internal
+ * Gets the digits in the expression string
+ * @param s Thestring to match
+ * @returns The matched digits, if any, as a string.
+ */
+function matchDigits(s: string): string {
+ s = s.toString();
+ var reDigits = /[0-9]*\.?[0-9]+/;
+ return (reDigits.test(s) && reDigits.exec(s)['0']) || undefined;
+}
+
+/**
+ * @internal
+ * Matches the comparison symbols used in the expression string.
+ * @param s The string to match.
+ * @returns The matched comparator, if any, as a string.
+ */
+function matchComparator(s: string): string {
+ s = s.toString();
+ var reComparator = /^(>=|<=|<|>)/;
+
+ return (reComparator.test(s) && reComparator.exec(s)['0']) || undefined;
+}
+
+/**
+ * @internal
+ * Filters an array of color objects with a "factor" property whose value is determined by a predicate or getter via the cb param.
+ * @param factor The property to query
+ * @param callback The function to use for comparison.
+ * @returns An array of colors or color objects.
+ */
+function sortedArr(
+ factor: Factor,
+ callback: callback,
+ order: Order,
+ colorObj = false
+) {
+ return (collection: ColorToken[] | object) => {
+ const results: ColorToken[] | Array<{ factor: number; color: ColorToken }> =
+ colorObjArr(factor, callback)(collection);
+
+ // Assign the value of colorObj to results variable
+ // Sort the array using our customSort helper function
+ results.sort(customSort(order, factor));
+
+ // colorObj parameter is true return the array of color objects
+ // else just return the color's name value.
+ if (colorObj) {
+ return results;
+ } else {
+ return results.map((color) => color['color']);
+ }
+ };
+}
+
+/**
+ * @internal
+ * Filters an array according to the value of a color's queried factor
+ * @param factor The property to query and use as filtering criteria
+ * @param cb The function to use for comparison
+ * @returns The filtered array
+ */
+function filteredArr(factor: Factor, cb?: callback) {
+ return (
+ collection: ColorToken[] | object,
+ start: number | string,
+ end?: number
+ ): ColorToken[] => {
+ let result: ColorToken[];
+
+ if (typeof start === 'number') {
+ result = colorObjArr(
+ factor,
+ cb
+ )(collection)
+ .filter((color) => inRange(color[factor], start, end))
+ .map((color) => color['color']);
+
+ // If string split the the string to an array of signature [sign,value] with sign being the type of predicate returned to mapFilter.
+ } else if (typeof start === 'string') {
+ //The patterns to match
+
+ const val = matchDigits(start),
+ op = matchComparator(start);
+
+ const mapFilter = (
+ test: (x: number, y: number) => boolean
+ ): ColorToken[] => {
+ return colorObjArr(
+ factor,
+ cb
+ )(collection)
+ .filter((el) => test(el[factor], parseFloat(val)))
+ .map((el) => el['color']);
+ };
+ switch (op) {
+ case '<':
+ result = mapFilter(lt);
+
+ break;
+ case '>':
+ result = mapFilter(gt);
+ break;
+ case '<=':
+ result = mapFilter(lte);
+ break;
+ case '>=':
+ result = mapFilter(gte);
+ break;
+ }
+ }
+ return result;
+ };
+}
+
+export {
+ expressionParser,
+ matchLightnessChannel,
+ matchChromaChannel,
+ min,
+ max,
+ customSort,
+ colorObjArr,
+ sortedArr,
+ filteredArr,
+ customFindKey,
+ colorObj,
+ customConcat,
+ inRange,
+ random,
+ isInteger,
+ floorCeil,
+ adjustHue,
+ channelDifference,
+ lt,
+ gt,
+ gte,
+ lte,
+ eq,
+ normalize,
+ checkArg,
+ getModeChannel,
+ interpolatorConfig,
+ matchComparator,
+ matchDigits
+};
diff --git a/src/index.ts b/src/index.ts
index 1101299d..8eeb97a8 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,7 +1,7 @@
export * from './colors';
-export * from './getters_and_setters';
-export * from './palettes';
+export * from './utils';
+export * from './generators';
export * from './filterBy';
export * from './sortBy';
-export * from './fp';
export * from './converters';
+export * from './helpers';
diff --git a/src/palettes/adaptive.ts b/src/palettes/adaptive.ts
deleted file mode 100644
index 2b510e9c..00000000
--- a/src/palettes/adaptive.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-// @ts-nocheck
-
-// This module will make use of contrast ratio to create adaptive palettes
diff --git a/src/palettes/base.ts b/src/palettes/base.ts
deleted file mode 100644
index 8a974a7e..00000000
--- a/src/palettes/base.ts
+++ /dev/null
@@ -1,71 +0,0 @@
-//@ts-nocheck
-import { useMode, modeLch, easingSmoothstep, samples } from 'culori/fn';
-
-import type { Color } from '../paramTypes.ts';
-import { adjustHue } from '../fp/number/adjustHue.ts';
-import { random } from '../fp/number/random.ts';
-import { toHex } from '../converters/toHex.ts';
-// Globals
-const cb = (iterations: number, distance: number, color: Color) =>
- samples(iterations).map((val) =>
- adjustHue((color['h'] + distance) * (val * easingSmoothstep(val)))
- );
-
-/**
- * @function
- * @description Generates a randomised classic color scheme from a single base color.
- * @param scheme Any classic color scheme either "analogous"|"triadic"|"tetradic"|"complementary"|"splitComplementary".
- * @param hex Optional boolen to return lch color objects or hex codes in the result array. Default is false which returns LCH color objects.
- * @param easingFunc Optional parameter to pass in a custom easing function. The default is smoothstep
- * @returns An array of 8 character hex codes. Elements in the array depend on the number of sample colors in the targeted scheme.
- * @example
- *
- import { base } from 'huetiful-js'
-
-console.log(base("triadic")("#a1bd2f", true))
-// [ '#a1bd2fff', '#00caffff', '#ff78c9ff' ]
- */
-
-const base =
- (scheme: 'analogous' | 'triadic' | 'tetradic' | 'complementary') =>
- (color: Color, hex = false, easingFunc: (t: number) => number): Color[] => {
- scheme = scheme.toLowerCase();
- easingFunc = easingFunc || easingSmoothstep;
- // Converting the color to lch
- const lch = useMode(modeLch);
- color = lch(color);
- const lowMin = 0.05,
- lowMax = 0.495,
- highMin = 0.5,
- highMax = 0.995;
- const targetHueSteps = {
- analogous: cb(3, 12, color),
- triadic: cb(3, 120, color),
- tetradic: cb(4, 90, color),
- complementary: cb(2, 180, color)
- };
- // For each step return a random value between lowMin && lowMax multipied by highMin && highMax and 0.9 of the step
- for (const scheme of Object.keys(targetHueSteps)) {
- targetHueSteps[scheme].map(
- (step: number) =>
- random(step * lowMax, step * lowMin) +
- random(step * highMax, step * highMin) / 2
- );
- }
- // The map for steps to obtain the targeted palettes
-
- const colors = targetHueSteps[scheme].map((step: number) => ({
- l: color['l'],
- c: color['c'],
- h: step * easingFunc(1 / targetHueSteps[scheme].length),
- mode: 'lch'
- }));
-
- if (hex) {
- return colors.map(toHex);
- } else {
- return colors;
- }
- };
-
-export { base };
diff --git a/src/palettes/discoverPalettes.ts b/src/palettes/discoverPalettes.ts
deleted file mode 100644
index 6208f8e2..00000000
--- a/src/palettes/discoverPalettes.ts
+++ /dev/null
@@ -1,88 +0,0 @@
-// @ts-nocheck
-import { nearest, differenceEuclidean, useMode, modeLch } from 'culori/fn';
-import { Color } from '../paramTypes';
-import { base } from './base.ts';
-import { toHex } from '../converters/toHex.ts';
-
-const { keys } = Object;
-const isColorEqual = (c1: Color, c2: Color): boolean => {
- return c1['h'] === c2['h'] && c1['l'] === c2['l'] && c1['c'] === c2['c'];
-};
-
-/**
- * @function
- * @description Takes an array of colors and finds the best matches for a set of predefined palettes. The function does not work on achromatic colors, you may use isAchromatic to filter grays from your collection before passing it to the function.
- * @param colors The array of colors to create palettes from. Preferably use 5 or more colors for better results.
- * @param scheme (Optional) The palette type you want to return.
- * @returns An array of colors if the scheme parameter is specified else it returns an object of all the palette types as keys and their values as an array of colors. If no colors are valid for the palette types it returns an empty array for the palette results.
- * @example
- *
- * import { discoverPalettes } from 'huetiful-js'
-
-let sample = [
- "#ffff00",
- "#00ffdc",
- "#00ff78",
- "#00c000",
- "#007e00",
- "#164100",
- "#720000",
- "#600000",
- "#4e0000",
- "#3e0000",
- "#310000",
-]
-
-console.log(discoverPalettes(sample, "tetradic"))
-// [ '#ffff00ff', '#00ffdcff', '#310000ff', '#720000ff' ]
- */
-const discoverPalettes = (
- colors: Color[],
- scheme: 'analogous' | 'triadic' | 'tetradic' | 'complementary'
-): Color[] | object => {
- const toLch = useMode(modeLch);
- colors = colors.map((color) => toLch('lch')(toHex(color)));
- const palettes = {};
- const schemes = ['analogous', 'triadic', 'tetradic', 'complementary'];
- const targetPalettes = {};
- for (const color of colors) {
- schemes.forEach((s) => (targetPalettes[s] = base(s)(color, false)));
-
- for (const paletteType of keys(targetPalettes)) {
- const palette = [];
- let variance = 0;
-
- for (const targetColor of targetPalettes[paletteType]) {
- // filter out colors already in the palette
- const availableColors = colors.filter(
- (color1) => !palette.some((color2) => isColorEqual(color1, color2))
- );
-
- const match = nearest(
- availableColors,
- differenceEuclidean('lch')
- )(targetColor)[0];
-
- variance += differenceEuclidean('lch')(targetColor, match);
-
- palette.push(match);
- }
-
- if (!palettes[paletteType] || variance < palettes[paletteType].variance) {
- palettes[paletteType] = palette.map(formatHex8);
- }
- }
- }
-
- if (typeof scheme === 'string') {
- return palettes[scheme.toLowerCase()];
- } else if (typeof scheme === 'undefined') {
- return palettes;
- } else {
- throw Error(
- `${scheme} is not a valid scheme. The schemes are triadic | tetradic | analogous | complementary`
- );
- }
-};
-
-export { discoverPalettes };
diff --git a/src/palettes/earthtone.ts b/src/palettes/earthtone.ts
deleted file mode 100644
index 1ab83612..00000000
--- a/src/palettes/earthtone.ts
+++ /dev/null
@@ -1,85 +0,0 @@
-// Use eathtone colors as control points for the bezier interpolation
-// takes one color and returns the specified amount of samples
-// @ts-nocheck
-import {
- interpolate,
- samples,
- easingSmootherstep,
- interpolatorSplineNatural,
- fixupHueShorter,
- interpolatorSplineMonotone,
- interpolatorSplineBasisClosed
-} from 'culori/fn';
-import type { Color, Earthtones, EarthtoneOptions } from '../paramTypes.ts';
-import { toHex } from '../converters/toHex.ts';
-
-//Add an overrides object with interpolation function and
-
-/**
- * @function
- * @description Creates a scale of a spline based interpolation between an earthtone and a color.
- * @param color The color to interpolate an earth tone with.
- * @param earthtone The earthtone to interpolate with.
- * @param iterations The number of iterations to produce from the color and earthtone.
- * @param options Optional overrides for customising interpolation and easing functions.
- * @returns The array of colors resulting from the earthtone interpolation as hex codes.
- * @example
- *
- * import { earthtone } from 'huetiful-js'
-
-
-console.log(earthtone("pink", "clay", 5))
-// [ '#6a5c52ff', '#8d746aff', '#b38d86ff', '#d9a6a6ff', '#ffc0cbff' ]
-
- */
-
-const earthtone = (
- color: Color,
- earthtone?: Earthtones,
- iterations?: number,
- options?: EarthtoneOptions
-): Color[] => {
- options = {
- easingFunc: easingSmootherstep,
- hueInterpolator: interpolatorSplineBasisClosed,
- chromaInterpolator: interpolatorSplineNatural,
- hueFixup: fixupHueShorter,
- lightnessInterpolator: interpolatorSplineMonotone
- };
- iterations = iterations < 1 ? 1 : iterations;
- earthtone = earthtone.toLowerCase();
- const tones = {
- 'light-gray': '#e5e5e5',
- silver: '#f5f5f5',
- sand: '#c2b2a4',
- tupe: '#a79e8a',
- mahogany: '#958c7c',
- 'brick-red': '#7d7065',
- clay: '#6a5c52',
- cocoa: '#584a3e',
- 'dark-brown': '#473b31',
- dark: '#352a21'
- };
- const base: Color = tones[earthtone || 'dark'];
-
- const f = interpolate([base, toHex(color), options['easingFunc']], 'lch', {
- h: {
- fixup: options['hueFixup'],
- use: options['hueInterpolator']
- },
- c: {
- use: options['chromaInterpolator']
- },
- l: {
- use: options['lightnessInterpolator']
- }
- });
-
- if (iterations === 1) {
- return toHex(f(0.5));
- } else {
- return samples(iterations).map((t) => toHex(f(t)));
- }
-};
-
-export { earthtone };
diff --git a/src/palettes/getComplimentaryHue.ts b/src/palettes/getComplimentaryHue.ts
deleted file mode 100644
index 4deff96e..00000000
--- a/src/palettes/getComplimentaryHue.ts
+++ /dev/null
@@ -1,77 +0,0 @@
-// @ts-nocheck
-import hueTempMap from '../color-maps/samples/hueTemperature';
-import { getChannel } from '../getters_and_setters/get.ts';
-import { min, max } from '../fp/array/min_max.ts';
-import { customConcat } from '../fp/object/customConcat.ts';
-import { inRange } from '../fp/number/inRange.ts';
-import { adjustHue } from '../fp/number/adjustHue.ts';
-import { setChannel } from '../getters_and_setters/set.ts';
-import type { Color } from '../paramTypes';
-import { toHex } from '../converters/toHex.ts';
-
-const { keys } = Object;
-const hueKeys = keys(hueTempMap);
-
-/**
- * @function
- * @description Gets the complementary hue of the passed in color. The function is internally guarded against achromatic colors.
- * @param color The color to retrieve its complimentary hue.
- * @param colorObj Optional boolean whether to return an object with the result color hue family or just the result color. Default is false.
- * @returns An object with the hue family and complimentary color as keys.
- * @example
- *import { getComplimentaryHue } from "huetiful-js";
- *
- *
-console.log(getComplimentaryHue("pink", true))
-//// { hue: 'blue-green', color: '#97dfd7ff' }
-
-console.log(getComplimentaryHue("purple"))
-// #005700ff
- */
-const getComplimentaryHue = (
- color: Color,
- colorObj = false
-): { hue: string; color: Color } | Color => {
- const modeChannel = 'lch.h';
- // A complementary hue is 180 deg from the hue value of the passed in color
-
- const complementaryHue: number = adjustHue(
- getChannel(modeChannel)(color) + 180
- );
-
- // Find the hue family which the color belongs to
-
- // eslint-disable-next-line prefer-const
-
- const hueFamily = hueKeys
- .map((hue) => {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
-
- const hueVals = customConcat(hueTempMap[hue]);
- const minVal = min(...hueVals);
- const maxVal = max(...hueVals);
- const bool = customConcat(hueTempMap[hue]).some(() =>
- inRange(complementaryHue, minVal, maxVal)
- );
-
- if (bool) {
- return hue;
- }
- })
- .filter((val) => typeof val === 'string')
- .toString();
-
- let result: Color | { hue: string; color: Color };
- if (complementaryHue) {
- result = {
- hue: hueFamily,
- color: toHex(setChannel(modeChannel)(color, complementaryHue))
- };
- } else {
- result = { hue: 'gray', color: color };
- }
-
- return (colorObj && result) || result['color'];
-};
-
-export { getComplimentaryHue };
diff --git a/src/palettes/getHue.ts b/src/palettes/getHue.ts
deleted file mode 100644
index 69dba642..00000000
--- a/src/palettes/getHue.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-/* eslint-disable @typescript-eslint/ban-ts-comment */
-// Returns the hue range where a color is found. If the hue Channel is falsy we return gray ?
-// @ts-nocheck
-import hueTempMap from '../color-maps/samples/hueTemperature';
-import { useMode, modeLch } from 'culori/fn';
-import { inRange } from '../fp/number/inRange.ts';
-import { min, max } from '../fp/array/min_max.ts';
-import { customConcat } from '../fp/object/customConcat.ts';
-import { toHex } from '../converters/toHex.ts';
-import type { Color, Hue } from '../paramTypes';
-
-/**
- *@function
- @description Gets the hue family which a a color belongs to with the overtone included (if it has one.). For achromatic colors it returns the string "gray".
- * @param color The color to query its shade or hue family.
- * @returns The name of the hue family for example red or green.
- * @example
- *
- * import { getHue } from 'huetiful-js'
-
-
-console.log(getHue("#310000"))
-// red
- */
-const getHue = (color: Color): Hue => {
- // First convert the color to LCH
- const lch = useMode(modeLch);
- color = lch(toHex(color));
-
- //Capure the hue value
- const factor: number | undefined = color['h'];
-
- // First check if hue is falsy. If true return the string "gray"
- // The predicate-func
-
- // We then pick the truthy key by returning an object which returns true for the inRange predicate
-
- const hueKeys = Object.keys(hueTempMap);
- const hueFamily = hueKeys
- .map((hue) => {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
-
- const hueVals = customConcat(hueTempMap[hue]);
- const minVal = min(...hueVals);
- const maxVal = max(...hueVals);
- const bool = customConcat(hueTempMap[hue]).some(() =>
- inRange(factor, minVal, maxVal)
- );
-
- if (bool) {
- return hue;
- }
- })
- .filter((val) => typeof val === 'string')
- .toString();
-
- return hueFamily;
-};
-
-export { getHue };
diff --git a/src/palettes/hueShift.ts b/src/palettes/hueShift.ts
deleted file mode 100644
index def39c49..00000000
--- a/src/palettes/hueShift.ts
+++ /dev/null
@@ -1,122 +0,0 @@
-//@ts-nocheck
-// Original source from George Francis: Coloring with Code
-// Can we also lightnessMapper palette types to create hue shifted variants per each color in the palette ?
-
-import { easingSmootherstep, modeLch, samples, useMode } from 'culori/fn';
-import type { Color } from '../paramTypes.ts';
-import { adjustHue } from '../fp/number/adjustHue.ts';
-import { toHex } from '../converters/toHex.ts';
-
-const lightnessMapper =
- (n: number) =>
- (start1: number, end1: number) =>
- (start2: number, end2: number) =>
- ((n - start1) / (end1 - start1)) * (end2 - start2) + start2;
-
-/**
- * @function
- * @description Generates a palette of hue shifted colors (as a color becomes lighter, its hue shifts up and darker when its hue shifts down. ) from a single base color. Min and max lightness value determine how light or dark our colour will be at either extreme.
- * @param color The color to use as the base of the hueshift. Colors are internally converted to LCH.
- * @param minLightness Minimum lightness value (range 0-100).
- * @param maxLightness Maximum lightness value (range 0-100).
- * @param iterations The number of iterations to perform on the color. The length of the resultant array is the number of iterations multiplied by 2 plus the base color passed or (iterations*2)+1.
- * @param hueStep Controls how much the hue will shift at each iteration.
- * @param hex Optional boolen to return lch color objects or hex codes in the result array. Default is false which returns LCH color objects.
- * @returns An array of colors in either hex or as LCH color objects.
- * @example
- *
- * import { hueShift } from "huetiful-js";
-
-let hueShiftedPalette = hueShift("#3e0000",true);
-
-console.log(hueShiftedPalette);
-
-// [
- '#ffffe1', '#ffdca5',
- '#ca9a70', '#935c40',
- '#5c2418', '#3e0000',
- '#310000', '#34000f',
- '#38001e', '#3b002c',
- '#3b0c3a'
-]
- */
-
-const hueShift = (
- color: Color,
- hex = false,
- {
- minLightness,
- maxLightness,
- hueStep,
- iterations,
- easingFn
- }: {
- minLightness?: number;
- maxLightness?: number;
- hueStep?: number;
- iterations?: number;
- easingFn: (t: number) => number;
- }
-): Color[] => {
- minLightness = minLightness || 10;
- maxLightness = maxLightness || 90;
- hueStep = hueStep || 5;
- iterations = iterations || 6;
- easingFn = easingSmootherstep || easingFn;
- const toLch = useMode(modeLch);
-
- color = toLch(toHex(color));
-
- // Pass in default values if any of the opts is undefined
-
- const palette: Color[] = [color];
-
- // Maximum number of iterations possible.
- const MAX_SAFE_ITERATIONS = 360 / hueStep;
- //Each iteration add a darker shade to the start of the array and a lighter tint to the end.
-
- if (iterations <= MAX_SAFE_ITERATIONS) {
- samples(iterations).map((t) => {
- //adjustHue checks hue values are clamped.
- const hueDark = adjustHue(color['h'] - hueStep * t);
- const hueLight = adjustHue(color['h'] + hueStep * t);
-
- // Here we use lightnessMapper to calculate our lightness values which takes a number that exists in range [0,1].
- const lightnessDark = lightnessMapper(easingFn(t))(0.1, iterations)(
- color['l'],
- minLightness
- );
-
- const lightnessLight = lightnessMapper(easingFn(t))(0.05, iterations)(
- color['l'],
- maxLightness
- );
-
- palette.push({
- l: lightnessDark,
- c: color['c'],
- h: hueDark,
- mode: 'lch'
- });
-
- palette.unshift({
- l: lightnessLight,
- c: color['c'],
- h: hueLight,
- mode: 'lch'
- });
- });
- } else {
- throw Error(
- `The number of iterations exceeds the maximum number of iterations. The maximum iterations are determined by the size of the hueStep. To find the maximum iterations possible, use this formula: 360/hueStep`
- );
- }
-
- if (hex) {
- return palette.map(toHex);
- } else {
- return palette;
- }
-};
-
-export { hueShift };
diff --git a/src/palettes/index.ts b/src/palettes/index.ts
deleted file mode 100644
index 2fce808a..00000000
--- a/src/palettes/index.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-export { hueShift } from './hueShift';
-export { discoverPalettes } from './discoverPalettes';
-export { getComplimentaryHue } from './getComplimentaryHue';
-export { earthtone } from './earthtone';
-export { getHue } from './getHue';
-export { pairedScheme } from './paired';
-export { base } from './base';
-export { pastel } from './pastel';
diff --git a/src/palettes/paired.ts b/src/palettes/paired.ts
deleted file mode 100644
index d3f69a0d..00000000
--- a/src/palettes/paired.ts
+++ /dev/null
@@ -1,91 +0,0 @@
-// @ts-nocheck
-// From colorbrewr
-
-import { toHex } from '../converters/toHex.ts';
-import { setChannel } from '../getters_and_setters/set.ts';
-import { Color, EarthtoneOptions, Tone } from '../paramTypes.ts';
-import {
- interpolate,
- samples,
- easingSmootherstep,
- interpolatorSplineNatural,
- fixupHueShorter,
- interpolatorSplineMonotone,
- interpolatorSplineBasisClosed,
- useMode,
- modeLch
-} from 'culori/fn';
-
-/**
- * @function pairedScheme
- * @description Creates a scheme that consists of a base color that is incremented by a hueStep to get the final hue to pair with.The colors are interpolated via white or black.
- * @param color The color to return a paired color scheme from.
- * @param via The tone to interpolate through (either white or black). Default is white.
- * @param hueStep The value to increment the base color's hue channel with.
- * @param iterations The number of color samples to generate.
- * @param overrides The optional overrides object to customize per channel options like interpolation methods and channel fixups.
- * @returns An array containing the paired scheme.
- * @example
- *
- * import { pairedScheme } from 'huetiful-js'
-
-console.log(pairedScheme("green", 6, 5, "dark"))
-// [ '#008116ff', '#006945ff', '#184b4eff', '#007606ff' ]
- */
-const pairedScheme = (
- color: Color,
- hueStep: number,
- iterations: number,
- via: Tone,
- options: EarthtoneOptions
-): Color[] => {
- // eslint-disable-next-line prefer-const
- options = {
- easingFunc: defaultArg(easingSmootherstep),
- hueInterpolator: defaultArg(interpolatorSplineBasisClosed),
- chromaInterpolator: defaultArg(interpolatorSplineNatural),
- hueFixup: defaultArg(fixupHueShorter),
- lightnessInterpolator: defaultArg(interpolatorSplineMonotone)
- };
- const toLch = useMode(modeLch);
- color = toLch(toHex(color));
-
- // get the hue of the passed in color and add it to the step which will result in the final color to pair with
- const derivedHue = setChannel('lch.h')(color, color['h'] + hueStep || 12);
-
- // Set the tones to color objects with hardcoded hue values and lightness channels clamped at extremes
- const tones = {
- dark: '#263238',
- light: { l: 100, c: 0, h: 0, mode: 'lch' }
- };
-
- const scale = interpolate([color, tones[via || 'dark'], derivedHue], 'lch', {
- h: {
- fixup: options['hueFixup'],
- use: options['hueInterpolator']
- },
- c: {
- use: options['chromaInterpolator']
- },
- l: {
- use: options['lightnessInterpolator']
- }
- });
-
- const { abs, round } = Math;
-
- // Declare the num of iterations in samples() which will be used as the t value
- // Since the interpolation returns half duplicate values we double the sample value
- // Guard the num param against negative values and floats
- const smp = samples((round(abs(iterations)) || 4) * 2);
-
- //The array to capture the different iterations
- const results: Color[] = smp.map((t) =>
- formatHex8(scale(easingSmootherstep(t)))
- );
-
- // Return a slice of the array from the start to the half length of the array
- return results.slice(0, results.length / 2);
-};
-
-export { pairedScheme };
diff --git a/src/palettes/pastel.ts b/src/palettes/pastel.ts
deleted file mode 100644
index 344396a4..00000000
--- a/src/palettes/pastel.ts
+++ /dev/null
@@ -1,95 +0,0 @@
-//@ts-nocheck
-// Pastels.mjs. - This module creates pastel versions of a color. It will take an arr or single value , tweak it and then return the result. Optional overrides for min max values when iterating over an arr.
-import type { Color } from '../paramTypes.ts';
-import { averageNumber, modeHsv, useMode } from 'culori/fn';
-import { min, max } from '../fp/array/min_max.ts';
-import { random } from '../fp/number/random.ts';
-import { toHex } from '../converters/toHex.js';
-
-const samplePastelObj = [
- {
- color: '#fea3aa',
- saturation: 0.35826771653543305,
- value: 0.996078431372549
- },
- {
- color: '#f8b88b',
- saturation: 0.43951612903225806,
- value: 0.9725490196078431
- },
- { color: '#faf884', saturation: 0.472, value: 0.9803921568627451 },
- {
- color: '#f2a2e8',
- saturation: 0.3305785123966942,
- value: 0.9490196078431372
- },
- {
- color: '#b2cefe',
- saturation: 0.2992125984251969,
- value: 0.996078431372549
- },
- {
- color: '#baed91',
- saturation: 0.3881856540084388,
- value: 0.9294117647058824
- }
-];
-
-const sampleSaturation = samplePastelObj.map((el) => el['saturation']);
-const sampleValues = samplePastelObj.map((el) => el['value']);
-
-const pastelSample = {
- averageSaturation: averageNumber(sampleValues),
- averageValue: averageNumber(sampleSaturation),
- minSampleSaturation: min(sampleSaturation),
- maxSampleSaturation: max(sampleSaturation),
- minSampleValue: min(sampleValues),
- maxSampleValue: max(sampleValues)
-};
-
-//Normalize the s and v channels between low and max values for each
-
-/**
- * @function
- * @description Returns a random pastel variant of the passed in color.
- * @param color The color to return a pastel variant of.
- * @param hex Pass in true to return an 8-character hex code else it will return an HSV color object.
- * @returns A random pastel color.
- * @example
- *
- *
-import { pastel } from 'huetiful-js'
-
-console.log(pastel("green", true))
-// #036103ff
- */
-const pastel = (color: Color, hex = true): Color => {
- const toHsv = useMode(modeHsv);
- color = toHsv(toHex(color));
- // For now we're simply returning an hsv object with the s and v channel set to the averages
- if (hex) {
- return toHex({
- h: color['h'],
- s: pastelSample['averageSaturation'],
- v: pastelSample['averageValue'],
- mode: 'hsv'
- });
- } else {
- return {
- h: color['h'],
- s: random(
- pastelSample['minSampleSaturation'],
- pastelSample['maxSampleSaturation'],
- true
- ),
- v: random(
- pastelSample['minSampleValue'],
- pastelSample['maxSampleValue'],
- true
- ),
- mode: 'hsv'
- };
- }
-};
-
-export { pastel };
diff --git a/src/paramTypes.d.ts b/src/paramTypes.d.ts
deleted file mode 100644
index e6226f52..00000000
--- a/src/paramTypes.d.ts
+++ /dev/null
@@ -1,407 +0,0 @@
-export type EarthtoneOptions = {
- easingFunc?: (t: number) => number;
- hueInterpolator?: Interpolator;
- chromaInterpolator?: Interpolator;
- hueFixup?: (arr: number[]) => number[];
- lightnessInterpolator?: Interpolator;
-};
-export type Interpolator = (arr: number[]) => (t: number) => number;
-export type Tone = 'light' | 'dark';
-export type Hue =
- | 'red-purple'
- | 'red'
- | 'yellow-red'
- | 'yellow'
- | 'green-yellow'
- | 'green'
- | 'blue-green'
- | 'blue'
- | 'purple-blue'
- | 'purple';
-
-export type Earthtones =
- | 'light gray'
- | 'silver'
- | 'sand'
- | 'tupe'
- | 'mahogany'
- | 'brick red'
- | 'clay'
- | 'cocoa'
- | 'dark brown'
- | 'dark';
-export type DivergingScheme =
- | 'Spectral'
- | 'RdYlGn'
- | 'RdBu'
- | 'PiYG'
- | 'PRGn'
- | 'RdYlBu'
- | 'BrBG'
- | 'RdGy'
- | 'PuOr';
-export type QualitativeScheme =
- | 'Set2'
- | 'Accent'
- | 'Set1'
- | 'Set3'
- | 'Dark2'
- | 'Paired'
- | 'Pastel2'
- | 'Pastel1';
-
-export type SequentialScheme =
- | 'OrRd'
- | 'PuBu'
- | 'BuPu'
- | 'Oranges'
- | 'BuGn'
- | 'YlOrBr'
- | 'YlGn'
- | 'Reds'
- | 'RdPu'
- | 'Greens'
- | 'YlGnBu'
- | 'Purples'
- | 'GnBu'
- | 'Greys'
- | 'YlOrRd'
- | 'PuRd'
- | 'Blues'
- | 'PuBuGn'
- | 'Viridis';
-
-export type Color =
- | number
- | string
- | object
- | [string, number, number, number, number?];
-
-/**
- * @param
- * An array of baseColor tokens (in short, just an array of valid colors)
- */
-
-/**
- * @type
- * The property being queried. Used in utilities that perform operations on collections.
- */
-export type Factor =
- | 'luminance'
- | 'temp'
- | 'saturation'
- | 'contrast'
- | 'distance'
- | 'lightness'
- | 'hue';
-
-type Order = 'asc' | 'desc';
-
-type callback = (arg: Color, colorSpace?: HueColorSpaces) => number;
-
-type FactorMapper = (
- factor: Factor,
- callback: callback,
- order?: Order,
- colorObj = false
-) => (colors: Color[]) => Color[];
-
-export type ColorSpaces =
- | 'a98'
- | 'cubehelix'
- | 'dlab'
- | 'dlch'
- | 'hsi'
- | 'hsl'
- | 'hsv'
- | 'hwb'
- | 'jab'
- | 'jch'
- | 'lab'
- | 'lab65'
- | 'lch'
- | 'lch65'
- | 'lchuv'
- | 'lrgb'
- | 'luv'
- | 'okhsl'
- | 'okhsv'
- | 'oklab'
- | 'rgb';
-
-export type HueColorSpaces = 'hsl' | 'hsv' | 'hsi' | 'oklch' | 'lch' | 'hwb';
-
-export type ScaleValues =
- | '100'
- | '50'
- | '200'
- | '300'
- | '400'
- | '500'
- | '600'
- | '700'
- | '800'
- | '900';
-
-export type HueMap = {
- indigo: {
- '50': string;
- '100': string;
- '200': string;
- '300': string;
- '400': string;
- '500': string;
- '600': string;
- '700': string;
- '800': string;
- '900': string;
- };
- gray: {
- '50': string;
- '100': string;
- '200': string;
- '300': string;
- '400': string;
- '500': string;
- '600': string;
- '700': string;
- '800': string;
- '900': string;
- };
-
- zinc: {
- '50': string;
- '100': string;
- '200': string;
- '300': string;
- '400': string;
- '500': string;
- '600': string;
- '700': string;
- '800': string;
- '900': string;
- };
-
- neutral: {
- '50': string;
- '100': string;
- '200': string;
- '300': string;
- '400': string;
- '500': string;
- '600': string;
- '700': string;
- '800': string;
- '900': string;
- };
-
- stone: {
- '50': string;
- '100': string;
- '200': string;
- '300': string;
- '400': string;
- '500': string;
- '600': string;
- '700': string;
- '800': string;
- '900': string;
- };
-
- red: {
- '50': string;
- '100': string;
- '200': string;
- '300': string;
- '400': string;
- '500': string;
- '600': string;
- '700': string;
- '800': string;
- '900': string;
- };
-
- orange: {
- '50': string;
- '100': string;
- '200': string;
- '300': string;
- '400': string;
- '500': string;
- '600': string;
- '700': string;
- '800': string;
- '900': string;
- };
-
- amber: {
- '50': string;
- '100': string;
- '200': string;
- '300': string;
- '400': string;
- '500': string;
- '600': string;
- '700': string;
- '800': string;
- '900': string;
- };
-
- yellow: {
- '50': string;
- '100': string;
- '200': string;
- '300': string;
- '400': string;
- '500': string;
- '600': string;
- '700': string;
- '800': string;
- '900': string;
- };
-
- lime: {
- '50': string;
- '100': string;
- '200': string;
- '300': string;
- '400': string;
- '500': string;
- '600': string;
- '700': string;
- '800': string;
- '900': string;
- };
-
- green: {
- '50': string;
- '100': string;
- '200': string;
- '300': string;
- '400': string;
- '500': string;
- '600': string;
- '700': string;
- '800': string;
- '900': string;
- };
-
- emerald: {
- '50': string;
- '100': string;
- '200': string;
- '300': string;
- '400': string;
- '500': string;
- '600': string;
- '700': string;
- '800': string;
- '900': string;
- };
-
- teal: {
- '50': string;
- '100': string;
- '200': string;
- '300': string;
- '400': string;
- '500': string;
- '600': string;
- '700': string;
- '800': string;
- '900': string;
- };
-
- sky: {
- '50': string;
- '100': string;
- '200': string;
- '300': string;
- '400': string;
- '500': string;
- '600': string;
- '700': string;
- '800': string;
- '900': string;
- };
-
- blue: {
- '50': string;
- '100': string;
- '200': string;
- '300': string;
- '400': string;
- '500': string;
- '600': string;
- '700': string;
- '800': string;
- '900': string;
- };
-
- violet: {
- '50': string;
- '100': string;
- '200': string;
- '300': string;
- '400': string;
- '500': string;
- '600': string;
- '700': string;
- '800': string;
- '900': string;
- };
-
- purple: {
- '50': string;
- '100': string;
- '200': string;
- '300': string;
- '400': string;
- '500': string;
- '600': string;
- '700': string;
- '800': string;
- '900': string;
- };
-
- fuchsia: {
- '50': string;
- '100': string;
- '200': string;
- '300': string;
- '400': string;
- '500': string;
- '600': string;
- '700': string;
- '800': string;
- '900': string;
- };
-
- pink: {
- '50': string;
- '100': string;
- '200': string;
- '300': string;
- '400': string;
- '500': string;
- '600': string;
- '700': string;
- '800': string;
- '900': string;
- };
-
- rose: {
- '50': string;
- '100': string;
- '200': string;
- '300': string;
- '400': string;
- '500': string;
- '600': string;
- '700': string;
- '800': string;
- '900': string;
- };
-};
-
-type ColorTemp = 'warm' | 'cool' | 'daylight' | 'incadescent' | 'fluorescent';
diff --git a/src/sortBy.ts b/src/sortBy.ts
new file mode 100644
index 00000000..fb549fc7
--- /dev/null
+++ b/src/sortBy.ts
@@ -0,0 +1,377 @@
+/**
+ * @license
+ * sortBy.ts - Utilities for sorting collections of colors.
+Copyright 2023 Dean Tarisai.
+This file is licensed to you under the Apache License, Version 2.0 (the 'License');
+you may not use this file except in compliance with the License. You may obtain a copy
+of the License at http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software distributed under
+the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+OF ANY KIND, either express or implied. See the License for the specific language
+governing permissions and limitations under the License.
+*/
+
+import type {
+ Factor,
+ ColorToken,
+ Colorspaces,
+ ColorDistanceOptions,
+ HueColorSpaces,
+ callback,
+ Order
+} from './types';
+import {
+ checkArg,
+ sortedArr,
+ matchLightnessChannel,
+ matchChromaChannel
+} from './helpers';
+import { wcagContrast, differenceEuclidean } from 'culori/fn';
+import { getLuminance, getChannel } from './utils';
+
+function baseSortBy(
+ factor: Factor,
+ cb: callback,
+ order: Order,
+ collection: ColorToken[] | object
+) {
+ return sortedArr(factor, cb, order)(collection);
+}
+
+/**
+ *
+ * Sorts colors according to their saturation.
+ * @param colors The array of colors to sort
+ * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')
+ * @param mode The mode color space to compute the saturation value in. The default is jch .
+ * @returns An array of the sorted color values.
+ * @example
+ * import { sortBySaturation } from "huetiful-js";
+let sample = [
+ "#00ffdc",
+ "#00ff78",
+ "#00c000",
+ "#007e00",
+ "#164100",
+ "#ffff00",
+ "#310000",
+ "#3e0000",
+ "#4e0000",
+ "#600000",
+ "#720000",
+];
+
+let sorted = sortBySaturation(sample);
+console.log(sorted);
+
+// [
+ '#310000', '#3e0000',
+ '#164100', '#4e0000',
+ '#600000', '#720000',
+ '#00ffdc', '#007e00',
+ '#00ff78', '#00c000',
+ '#ffff00'
+]
+
+let sortedDescending = sortBySaturation(sample,'desc');
+console.log(sortedDescending)
+// [
+ '#ffff00', '#00c000',
+ '#00ff78', '#007e00',
+ '#00ffdc', '#720000',
+ '#600000', '#4e0000',
+ '#164100', '#3e0000',
+ '#310000'
+]
+
+ */
+
+function sortBySaturation(
+ collection: ColorToken[] | object,
+ order: 'asc' | 'desc',
+ mode?: HueColorSpaces
+): ColorToken[] {
+ return baseSortBy(
+ 'saturation',
+ getChannel(matchChromaChannel(mode)),
+ order,
+ collection
+ );
+}
+
+/**
+ *
+ * Sorts colors according to the relative brightness as defined by WCAG definition.
+ * @param colors The array of colors to sort
+ * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')
+ * @returns An array of the sorted color values.
+ * @example
+ * import { sortByLuminance } from "huetiful-js";
+let sample = [
+ "#00ffdc",
+ "#00ff78",
+ "#00c000",
+ "#007e00",
+ "#164100",
+ "#ffff00",
+ "#310000",
+ "#3e0000",
+ "#4e0000",
+ "#600000",
+ "#720000",
+];
+
+
+
+let sorted = sortByLuminance(sample)
+console.log(sorted)
+// [
+ '#310000', '#3e0000',
+ '#4e0000', '#600000',
+ '#720000', '#164100',
+ '#007e00', '#00c000',
+ '#00ff78', '#00ffdc',
+ '#ffff00'
+]
+
+let sortedDescending = sortByLuminance(sample, "desc");
+console.log(sortedDescending)
+// [
+ '#ffff00', '#00ffdc',
+ '#00ff78', '#00c000',
+ '#007e00', '#164100',
+ '#720000', '#600000',
+ '#4e0000', '#3e0000',
+ '#310000'
+]
+
+
+ */
+
+function sortByLuminance(
+ collection: ColorToken[] | object,
+ order: 'asc' | 'desc'
+): ColorToken[] {
+ return baseSortBy('luminance', getLuminance, order, collection);
+}
+
+/**
+ *
+ * Sorts colors according to their lightness.
+ * @param colors The array of colors to sort
+ * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')
+ * @param colorspace The mode colorspace to use for filtering color lightness. Defaut is lch65
+ * @returns An array of the sorted color values.
+ * @example
+ * import { sortByLightness } from "huetiful-js";
+
+let sample = [
+ "#00ffdc",
+ "#00ff78",
+ "#00c000",
+ "#007e00",
+ "#164100",
+ "#ffff00",
+ "#310000",
+ "#3e0000",
+ "#4e0000",
+ "#600000",
+ "#720000",
+]
+
+sortByLightness(sample)
+
+// [
+ '#310000', '#3e0000',
+ '#4e0000', '#600000',
+ '#720000', '#164100',
+ '#007e00', '#00c000',
+ '#00ff78', '#00ffdc',
+ '#ffff00'
+]
+
+
+sortByLightness(sample,'desc')
+
+// [
+ '#ffff00', '#00ffdc',
+ '#00ff78', '#00c000',
+ '#007e00', '#164100',
+ '#720000', '#600000',
+ '#4e0000', '#3e0000',
+ '#310000'
+]
+
+ */
+// For lightness use a different color space
+function sortByLightness(
+ collection: ColorToken[] | object,
+ order?: Order,
+ colorspace?: HueColorSpaces
+): ColorToken[] {
+ return baseSortBy(
+ 'lightness',
+ getChannel(matchLightnessChannel(colorspace)),
+ order,
+ collection
+ );
+}
+
+/**
+ *
+ * Sorts colors according to hue values. It works with any color space with a hue channel. Note that hue values between HSL and Lch do not align. Achromatic colors are not supported
+ * @param colors The array of colors to sort
+ * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')
+* @param colorspace The color space to compute the color distances in. All colors within the collection will be converted to mode. Also note that because differences in hue mapping certain color spaces such as HSL and LCH hue values do not align. Keep such quirks in mind to avoid weird results.
+* @returns An array of the sorted color values.
+ * @example
+ * let sample = [
+ "#00ffdc",
+ "#00ff78",
+ "#00c000",
+ "#007e00",
+ "#164100",
+ "#ffff00",
+ "#310000",
+ "#3e0000",
+ "#4e0000",
+ "#600000",
+ "#720000",
+];
+
+
+let sorted = sortByHue(sample);
+console.log(sorted)
+// [
+ '#310000', '#3e0000',
+ '#4e0000', '#600000',
+ '#720000', '#ffff00',
+ '#164100', '#00c000',
+ '#007e00', '#00ff78',
+ '#00ffdc'
+]
+
+let sortedDescending = sortByHue(sample,'desc');
+console.log(sortedDescending)
+// [
+ '#00ffdc', '#00ff78',
+ '#007e00', '#00c000',
+ '#164100', '#ffff00',
+ '#720000', '#600000',
+ '#4e0000', '#3e0000',
+ '#310000'
+]
+
+ */
+
+// Todo: Add the mode param so that users can select mode to work with. The default is lch
+function sortByHue(
+ collection: ColorToken[] | object,
+ order?: Order,
+ colorspace?: HueColorSpaces
+): ColorToken[] {
+ const reHue = /h/i.test(colorspace);
+ return (
+ reHue &&
+ baseSortBy(
+ 'lightness',
+ getChannel(`${checkArg(colorspace, 'jch')}.h`),
+ order,
+ collection
+ )
+ );
+}
+
+/**
+ *
+ * Sorts colors according to their contrast value as defined by WCAG. The contrast is tested against a comparison color (the 'against' param)
+ * @param colors The array of colors to sort
+ * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')
+ * @returns An array of the sorted color values.
+ * @example
+ *
+ * import { sortByContrast } from 'huetiful-js'
+
+let sample = ['purple', 'green', 'red', 'brown']
+console.log(sortByContrast(sample, 'yellow'))
+// [ 'red', 'green', 'brown', 'purple' ]
+
+console.log(sortByContrast(sample, 'yellow', 'desc'))
+// [ 'purple', 'brown', 'green', 'red' ]
+
+ */
+
+function sortByContrast(
+ collection: ColorToken[] | object,
+ against: ColorToken,
+ order?: Order
+): ColorToken[] {
+ // @ts-ignore
+ const cb = (against: ColorToken) => (color: ColorToken) =>
+ wcagContrast(color as string, against as string);
+ return baseSortBy('contrast', cb(against), order, collection);
+}
+
+/**
+ *
+ * Sorts colors according to their Euclidean distance. The distance factor is determined by the color space used (some color spaces are not symmetrical meaning that the distance between colorA and colorB is not equal to the distance between colorB and colorA ). The distance is computed from against a color which is used for comparison for all the colors in the array i.e it sorts the colors against the dist
+ * @param colors The array of colors to sort.
+ * @param against The color to compare the distance with. All the distances are calculated between this color and the ones in the colors array.
+ * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')
+ * @param weights The weighting values to pass to the Euclidean function. Default is [1,1,1,0].
+ * @param mode The color space to calculate the distance in . The default is the cylindrical variant of the CIELUV colorspace ('lchuv')
+ * @returns An array of the sorted color values.
+ * @example
+ * import { sortByDistance } from 'huetiful-js'
+
+let sample = ['purple', 'green', 'red', 'brown']
+console.log(
+ sortByDistance(sample, 'yellow', 'asc', {
+ mode: 'lch',
+ })
+)
+
+// [ 'brown', 'red', 'green', 'purple' ]
+
+let sample = ['purple', 'green', 'red', 'brown']
+console.log(
+ sortByDistance(sample, 'yellow', 'asc', {
+ mode: 'lch',
+ })
+)
+
+// [ 'green', 'brown', 'red', 'purple' ]
+ */
+
+function sortByDistance(
+ collection: ColorToken[] | object,
+ against: ColorToken,
+ order?: 'asc' | 'desc',
+ options?: ColorDistanceOptions
+): ColorToken[] {
+ var { mode, weights } = options || {};
+
+ const cb = (against: string, mode: Colorspaces) => (color: string) => {
+ // @ts-ignore
+ return differenceEuclidean(
+ checkArg(mode, 'lchuv') as typeof mode,
+ checkArg(weights, [1, 1, 1, 0]) as typeof weights
+ )(against, color);
+ };
+
+ return baseSortBy(
+ 'contrast',
+ cb(against as string, checkArg(mode, 'lchuv') as typeof mode),
+ order,
+ collection
+ );
+}
+
+export {
+ sortByContrast,
+ sortByDistance,
+ sortByLightness,
+ sortBySaturation,
+ sortByHue,
+ sortByLuminance
+};
diff --git a/src/sortBy/index.ts b/src/sortBy/index.ts
deleted file mode 100644
index 2a111189..00000000
--- a/src/sortBy/index.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export { sortByContrast } from './sortByContrast';
-export { sortByDistance } from './sortByDistance';
-export { sortByHue } from './sortByHue';
-export { sortByLightness } from './sortByLightness';
-export { sortByLuminance } from './sortByLuminance';
-export { sortBySaturation } from './sortBySaturation';
-export { sortByTemp } from './sortByTemp';
diff --git a/src/sortBy/sortByContrast.ts b/src/sortBy/sortByContrast.ts
deleted file mode 100644
index 9e9b0939..00000000
--- a/src/sortBy/sortByContrast.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-// @ts-nocheck
-import type { Factor, Color } from '../paramTypes.ts';
-import { sortedArr } from '../fp/array/sortedArr.ts';
-import { wcagContrast } from 'culori/fn';
-
-/**
- * @function
- * @description Sorts colors according to their contrast value as defined by WCAG. The contrast is tested against a comparison color (the 'against' param)
- * @param colors The array of colors to sort
- * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')
- * @returns An array of the sorted color values.
- * @example
- *
- * import { sortByContrast } from 'huetiful-js'
-
-let sample = ['purple', 'green', 'red', 'brown']
-console.log(sortByContrast(sample, 'yellow'))
-// [ 'red', 'green', 'brown', 'purple' ]
-
-console.log(sortByContrast(sample, 'yellow', 'desc'))
-// [ 'purple', 'brown', 'green', 'red' ]
-
- */
-
-const sortByContrast = (
- colors: Color[],
- against: Color,
- order: 'asc' | 'desc'
-): Color[] => {
- const factor: Factor = 'contrast';
- const cb = (against: Color) => (color: Color) => wcagContrast(color, against);
- //Sorting the color array of object by the 'temp' property in the specified order.
-
- return sortedArr(factor, cb(against), order)(colors);
-};
-
-export { sortByContrast };
diff --git a/src/sortBy/sortByDistance.ts b/src/sortBy/sortByDistance.ts
deleted file mode 100644
index 414caffd..00000000
--- a/src/sortBy/sortByDistance.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-// @ts-nocheck
-import type { Factor, Color, ColorSpaces } from '../paramTypes.ts';
-import { sortedArr } from '../fp/array/sortedArr.ts';
-import { differenceEuclidean } from 'culori/fn';
-
-/**
- * @function
- * @description Sorts colors according to their Euclidean distance. The distance factor is determined by the color space used (some color spaces are not symmetrical meaning that the distance between colorA and colorB is not equal to the distance between colorB and colorA ). The distance is compared against a color which is used for comparison for all the colors in the array.
- * @param colors The array of colors to sort.
- * @param against The color to compare the distance with. All the distances are calculated between this color and the ones in the colors array.
- * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')
- * @param weights The weighting values to pass to the Euclidean function. Default is [1,1,1,0].
- * @param mode The color space to calculate the distance in .
- * @returns An array of the sorted color values.
- * @example
- * import { sortByDistance } from 'huetiful-js'
-
-let sample = ['purple', 'green', 'red', 'brown']
-console.log(
- sortByDistance(sample, 'yellow', 'asc', {
- mode: 'lch',
- })
-)
-
-// [ 'brown', 'red', 'green', 'purple' ]
-
-let sample = ['purple', 'green', 'red', 'brown']
-console.log(
- sortByDistance(sample, 'yellow', 'asc', {
- mode: 'lch',
- })
-)
-
-// [ 'green', 'brown', 'red', 'purple' ]
- */
-
-const sortByDistance = (
- colors: Color[],
- against: Color,
- order?: 'asc' | 'desc',
- mode?: ColorSpaces,
- weights?: [number, number, number, number]
-): Color[] => {
- const factor: Factor = 'distance';
- const cb = (against: Color, mode: ColorSpaces) => (color: Color) =>
- differenceEuclidean(mode || 'lch', weights || [1, 1, 1, 0])(against, color);
- //Sorting the color array of object by the 'distance' property in the specified order.
-
- return sortedArr(factor, cb(against, mode), order)(colors);
-};
-
-export { sortByDistance };
diff --git a/src/sortBy/sortByHue.ts b/src/sortBy/sortByHue.ts
deleted file mode 100644
index ffee0b41..00000000
--- a/src/sortBy/sortByHue.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-// @ts-nocheck
-import { getChannel } from '../getters_and_setters/get.ts';
-import { sortedArr } from '../fp/array/sortedArr.ts';
-
-import type { Factor, Color } from '../paramTypes.ts';
-
-/**
- * @function
- * @description Sorts colors according to hue values. It works with any color space with a hue channel. Note that hue values between HSL and Lch do not align. Achromatic colors are not supported
- * @param colors The array of colors to sort
- * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')
-* @param mode The color space to compute the color distances in. All colors within the collection will be converted to mode. Also note that because differences in hue mapping certain color spaces such as HSL and LCH hue values do not align. Keep such quirks in mind to avoid weird results.
-* @returns An array of the sorted color values.
- * @example
- * let sample = [
- "#00ffdc",
- "#00ff78",
- "#00c000",
- "#007e00",
- "#164100",
- "#ffff00",
- "#310000",
- "#3e0000",
- "#4e0000",
- "#600000",
- "#720000",
-];
-
-
-let sorted = sortByHue(sample);
-console.log(sorted)
-// [
- '#310000', '#3e0000',
- '#4e0000', '#600000',
- '#720000', '#ffff00',
- '#164100', '#00c000',
- '#007e00', '#00ff78',
- '#00ffdc'
-]
-
-let sortedDescending = sortByHue(sample,'desc');
-console.log(sortedDescending)
-// [
- '#00ffdc', '#00ff78',
- '#007e00', '#00c000',
- '#164100', '#ffff00',
- '#720000', '#600000',
- '#4e0000', '#3e0000',
- '#310000'
-]
-
- */
-
-// Todo: Add the mode param so that users can select mode to work with. The default is
-const sortByHue = (colors: Color[], order: 'asc' | 'desc'): Color[] => {
- const factor: Factor = 'hue';
- const cb = getChannel('lch.h');
- //Sorting the color array of object by the 'temp' property in the specified order.
-
- return sortedArr(factor, cb, order)(colors);
-};
-
-export { sortByHue };
diff --git a/src/sortBy/sortByLightness.ts b/src/sortBy/sortByLightness.ts
deleted file mode 100644
index 5d009d37..00000000
--- a/src/sortBy/sortByLightness.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-// @ts-nocheck
-import type { Factor, Color } from '../paramTypes.ts';
-import { getChannel } from '../getters_and_setters/get.ts';
-import { sortedArr } from '../fp/array/sortedArr.ts';
-
-/**
- * @function
- * @description Sorts colors according to their lightness.
- * @param colors The array of colors to sort
- * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')
- * @returns An array of the sorted color values.
- * @example
- * import { sortByLightness } from "huetiful-js";
-
-let sample = [
- "#00ffdc",
- "#00ff78",
- "#00c000",
- "#007e00",
- "#164100",
- "#ffff00",
- "#310000",
- "#3e0000",
- "#4e0000",
- "#600000",
- "#720000",
-]
-
-sortByLightness(sample)
-
-// [
- '#310000', '#3e0000',
- '#4e0000', '#600000',
- '#720000', '#164100',
- '#007e00', '#00c000',
- '#00ff78', '#00ffdc',
- '#ffff00'
-]
-
-
-sortByLightness(sample,'desc')
-
-// [
- '#ffff00', '#00ffdc',
- '#00ff78', '#00c000',
- '#007e00', '#164100',
- '#720000', '#600000',
- '#4e0000', '#3e0000',
- '#310000'
-]
-
- */
-
-const sortByLightness = (colors: Color[], order: 'asc' | 'desc'): Color[] => {
- const factor: Factor = 'lightness';
- const cb = getChannel('lch.l');
- //Sorting the color array of object by the 'temp' property in the specified order.
-
- return sortedArr(factor, cb, order)(colors);
-};
-
-export { sortByLightness };
diff --git a/src/sortBy/sortByLuminance.ts b/src/sortBy/sortByLuminance.ts
deleted file mode 100644
index 1cfb4984..00000000
--- a/src/sortBy/sortByLuminance.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-// @ts-nocheck
-import type { Factor, Color } from '../paramTypes.ts';
-import { sortedArr } from '../fp/array/sortedArr.ts';
-import { getLuminance } from '../getters_and_setters/luminance.ts';
-
-/**
- * @function
- * @description Sorts colors according to the relative brightness as defined by WCAG definition.
- * @param colors The array of colors to sort
- * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')
- * @returns An array of the sorted color values.
- * @example
- * import { sortByLuminance } from "huetiful-js";
-let sample = [
- "#00ffdc",
- "#00ff78",
- "#00c000",
- "#007e00",
- "#164100",
- "#ffff00",
- "#310000",
- "#3e0000",
- "#4e0000",
- "#600000",
- "#720000",
-];
-
-
-
-let sorted = sortByLuminance(sample)
-console.log(sorted)
-// [
- '#310000', '#3e0000',
- '#4e0000', '#600000',
- '#720000', '#164100',
- '#007e00', '#00c000',
- '#00ff78', '#00ffdc',
- '#ffff00'
-]
-
-let sortedDescending = sortByLuminance(sample, "desc");
-console.log(sortedDescending)
-// [
- '#ffff00', '#00ffdc',
- '#00ff78', '#00c000',
- '#007e00', '#164100',
- '#720000', '#600000',
- '#4e0000', '#3e0000',
- '#310000'
-]
-
-
- */
-
-const sortByLuminance = (colors: Color[], order: 'asc' | 'desc'): Color[] => {
- const factor: Factor = 'luminance';
- const cb = getLuminance;
- //Sorting the color array of object by the 'temp' property in the specified order.
-
- return sortedArr(factor, cb, order)(colors);
-};
-
-export { sortByLuminance };
diff --git a/src/sortBy/sortBySaturation.ts b/src/sortBy/sortBySaturation.ts
deleted file mode 100644
index 03d07847..00000000
--- a/src/sortBy/sortBySaturation.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-// @ts-nocheck
-import type { Factor, Color } from '../paramTypes.ts';
-import { getChannel } from '../getters_and_setters/get.ts';
-import { sortedArr } from '../fp/array/sortedArr.ts';
-
-/**
- * @function
- * @description Sorts colors according to their saturation.
- * @param colors The array of colors to sort
- * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')
- * @returns An array of the sorted color values.
- * @example
- * import { sortBySaturation } from "huetiful-js";
-let sample = [
- "#00ffdc",
- "#00ff78",
- "#00c000",
- "#007e00",
- "#164100",
- "#ffff00",
- "#310000",
- "#3e0000",
- "#4e0000",
- "#600000",
- "#720000",
-];
-
-let sorted = sortBySaturation(sample);
-console.log(sorted);
-
-// [
- '#310000', '#3e0000',
- '#164100', '#4e0000',
- '#600000', '#720000',
- '#00ffdc', '#007e00',
- '#00ff78', '#00c000',
- '#ffff00'
-]
-
-let sortedDescending = sortBySaturation(sample,'desc');
-console.log(sortedDescending)
-// [
- '#ffff00', '#00c000',
- '#00ff78', '#007e00',
- '#00ffdc', '#720000',
- '#600000', '#4e0000',
- '#164100', '#3e0000',
- '#310000'
-]
-
- */
-
-const sortBySaturation = (colors: Color[], order: 'asc' | 'desc'): Color[] => {
- const factor: Factor = 'saturation';
- const cb = getChannel('lch.c');
- //Sorting the color array of object by the 'temp' property in the specified order.
-
- return sortedArr(factor, cb, order)(colors);
-};
-
-export { sortBySaturation };
diff --git a/src/sortBy/sortByTemp.ts b/src/sortBy/sortByTemp.ts
deleted file mode 100644
index 61cd7597..00000000
--- a/src/sortBy/sortByTemp.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-//@ts-nocheck
-import { getTemp } from '../converters/getTemp.ts';
-import type { Color, Factor } from '../paramTypes.ts';
-import { sortedArr } from '../fp/array/sortedArr.ts';
-
-/**
- * @function
- * @description Sorts colors according to temperature value in Kelvins according to the temperatu. Achromatic colors may return awkward results.
- * @param colors The array of colors to sort
- * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')
- * @returns An array of the sorted color values.
- * @see Based on Neil Bartlett's implementation https://github.com/neilbartlett/color-temperature
- * @example
- * import { sortByTemp } from 'huetiful-js'
-let sample = [
- '#00ffdc',
- '#00ff78',
- '#00c000',
- '#007e00',
- '#164100',
- '#ffff00',
- '#310000',
- '#3e0000',
- '#4e0000',
- '#600000',
- '#720000',
-]
-
-let sorted = sortByTemp(sample)
-console.log(sorted)
-
-let sortedDescending = sortByTemp(sample, 'desc')
-console.log(sortedDescending)
- */
-
-const sortByTemp = (colors: Color[], order: 'asc' | 'desc'): Color[] => {
- // Purifying the data.All colors are converted to the specified mode to ensure unbiased results.
- const factor: Factor = 'temp';
- const cb = getTemp;
- //Sorting the color array of object by the 'temp' property in the specified order.
-
- return sortedArr(factor, cb, order)(colors);
-};
-
-export { sortByTemp };
diff --git a/src/source.code-workspace b/src/source.code-workspace
new file mode 100644
index 00000000..022c3e5c
--- /dev/null
+++ b/src/source.code-workspace
@@ -0,0 +1,8 @@
+{
+ "folders": [
+ {
+ "path": "."
+ }
+ ],
+ "settings": {}
+}
diff --git a/src/types.d.ts b/src/types.d.ts
new file mode 100644
index 00000000..9c8e51a4
--- /dev/null
+++ b/src/types.d.ts
@@ -0,0 +1,293 @@
+/*
+ * @preserve
+ * @license
+ * types.d.ts - Type declarations for huetiful-js.
+ * Contains colors from TailwindCSS released under the MIT permissive licence.
+Copyright 2023 Dean Tarisai.
+This file is licensed to you under the Apache License, Version 2.0 (the 'License');
+you may not use this file except in compliance with the License. You may obtain a copy
+of the License at http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software distributed under
+the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+OF ANY KIND, either express or implied. See the License for the specific language
+governing permissions and limitations under the License.
+*/
+
+// Color token types
+export type ColorTuple = [string, number, number, number, number?];
+
+export type ColorObject = { mode: Colorspaces; alpha?: number };
+export type ColorOptions = {
+ alpha?: number;
+ lightness?: number;
+ temperature?: number;
+ colorspace?: HueColorSpaces;
+ luminance?: number;
+ saturation?: number;
+
+ lightMode?: ColorToken;
+ darkMode?: ColorToken;
+
+ contrast?: number;
+ colorSpace?: HueColorSpaces;
+};
+export type ColorDistanceOptions = {
+ weights?: [number, number, number, number];
+ mode?: Colorspaces;
+};
+
+/**
+ * @type
+ * @description This object returns the lightMode and darkMode optimized version of a color with support to add color vision deficiency simulation to the final color result.
+ */
+export type AdaptivePaletteOptions = {
+ backgroundColor?: { light?: ColorToken; dark?: ColorToken };
+ viewingConditions?: ViewingConditions;
+ colorBlind?: boolean;
+};
+
+export type InterpolatorOptions = Pick<
+ Options,
+ | 'easingFunc'
+ | 'hueInterpolator'
+ | 'chromaInterpolator'
+ | 'hueFixup'
+ | 'lightnessInterpolator'
+>;
+
+/**
+ * @type
+ * @description The override parameters for palette functions.
+ */
+type Options = {
+ /**
+ * The easing function to use.
+ * @param t Any value between 0 and 1
+ * @returns A number.
+ */
+ easingFunc?: (t: number) => number;
+
+ /**
+ *@param The interpolation method to use on the hue channel.
+ */
+ hueInterpolator?: Interpolator;
+
+ /**
+ *@param The interpolation method to use on the chroma channel.
+ */
+ chromaInterpolator?: Interpolator;
+
+ /**
+ *@param The type of hue fixup to apply to the hue channels during interpolation.
+ */
+ hueFixup?: (arr: number[]) => number[];
+
+ /**
+ *@param The interpolation method to use on the lightness channel.
+ */
+ lightnessInterpolator?: Interpolator;
+ /**
+ *@param The color to pass through during interpolation.
+ */
+ via?: Tone;
+
+ /**
+ *@param The amount of hue angles to increment each iteration with.
+ */
+ hueStep?: number;
+
+ /**
+ * * @param earthtone The earthtone to interpolate with.
+ */
+ earthtones?:
+ | 'light-gray'
+ | 'silver'
+ | 'sand'
+ | 'tupe'
+ | 'mahogany'
+ | 'brick-red'
+ | 'clay'
+ | 'cocoa'
+ | 'dark-brown'
+ | 'dark';
+
+ /**
+ *@param The amount of samples to return in the result collection.
+ */
+ samples?: number;
+
+ /**
+ * * @param minLightness Minimum lightness value (range 0-100).
+ */
+ minLightness?: number;
+ /**
+ * @param maxLightness Maximum lightness value (range 0-100).
+ */
+ maxLightness?: number;
+};
+
+export type PairedSchemeOptions = Omit<
+ Options,
+ 'earthtones' | 'maxLightness' | 'minLightness'
+>;
+export type EarthtoneOptions = Omit<
+ Options,
+ 'hueStep' | 'via' | 'maxLightness' | 'minLightness'
+>;
+
+export type HueShiftOptions = Omit &
+ InterpolatorOptions;
+export type Interpolator = (arr: number[]) => (t: number) => number;
+export type Tone = 'light' | 'dark';
+export type DeficiencyType = 'red' | 'blue' | 'green' | 'monochromacy';
+export type HueFamily =
+ | 'red-purple'
+ | 'red'
+ | 'yellow-red'
+ | 'yellow'
+ | 'green-yellow'
+ | 'green'
+ | 'blue-green'
+ | 'blue'
+ | 'purple-blue'
+ | 'purple';
+
+export type DivergingScheme =
+ | 'Spectral'
+ | 'RdYlGn'
+ | 'RdBu'
+ | 'PiYG'
+ | 'PRGn'
+ | 'RdYlBu'
+ | 'BrBG'
+ | 'RdGy'
+ | 'PuOr';
+export type QualitativeScheme =
+ | 'Set2'
+ | 'Accent'
+ | 'Set1'
+ | 'Set3'
+ | 'Dark2'
+ | 'Paired'
+ | 'Pastel2'
+ | 'Pastel1';
+
+export type SequentialScheme =
+ | 'OrRd'
+ | 'PuBu'
+ | 'BuPu'
+ | 'Oranges'
+ | 'BuGn'
+ | 'YlOrBr'
+ | 'YlGn'
+ | 'Reds'
+ | 'RdPu'
+ | 'Greens'
+ | 'YlGnBu'
+ | 'Purples'
+ | 'GnBu'
+ | 'Greys'
+ | 'YlOrRd'
+ | 'PuRd'
+ | 'Blues'
+ | 'PuBuGn'
+ | 'Viridis';
+
+/**
+ * @type
+ * @description Any recognizable color token.
+ */
+export type ColorToken = number | string | object | ColorTuple;
+
+/**
+ * @param
+ * An array of baseColor tokens (in short, just an array of valid colors)
+ */
+
+/**
+ * @type
+ * The property being queried. Used in utilities that perform operations on collections.
+ */
+export type Factor =
+ | 'luminance'
+ | 'temp'
+ | 'saturation'
+ | 'contrast'
+ | 'distance'
+ | 'lightness'
+ | 'hue';
+
+type Order = 'asc' | 'desc';
+
+type callback = unknown;
+
+type FactorMapper = (
+ factor: Factor,
+ callback: callback,
+ order?: Order,
+ colorObj?: boolean
+) => (colors: ColorToken[]) => ColorToken[];
+
+export type UniformColorSpaces =
+ | 'lch'
+ | 'jch'
+ | 'dlch'
+ | 'lch'
+ | 'lch65'
+ | 'lchuv'
+ | 'oklch';
+export type Colorspaces =
+ | 'a98'
+ | 'cubehelix'
+ | 'dlab'
+ | 'jab'
+ | 'lab'
+ | 'lab65'
+ | 'lrgb'
+ | 'luv'
+ | 'oklab'
+ | 'rgb'
+ | HueColorSpaces;
+
+export type HueColorSpaces =
+ | UniformColorSpaces
+ | 'hsl'
+ | 'hsv'
+ | 'hsi'
+ | 'hwb'
+ | 'okhsl'
+ | 'okhsv';
+
+export type ScaleValues =
+ | '50'
+ | '100'
+ | '200'
+ | '300'
+ | '400'
+ | '500'
+ | '600'
+ | '700'
+ | '800'
+ | '900';
+
+export type TailwindColorFamilies =
+ | 'indigo'
+ | 'gray'
+ | 'zinc'
+ | 'neutral'
+ | 'stone'
+ | 'red'
+ | 'orange'
+ | 'amber'
+ | 'yellow'
+ | 'lime'
+ | 'green'
+ | 'emerald'
+ | 'teal'
+ | 'sky'
+ | 'blue'
+ | 'violet'
+ | 'purple'
+ | 'fuchsia'
+ | 'pink'
+ | 'rose';
diff --git a/src/utils.ts b/src/utils.ts
new file mode 100644
index 00000000..fff09386
--- /dev/null
+++ b/src/utils.ts
@@ -0,0 +1,965 @@
+import hueTempMap from './color-maps/samples/hueTemperature.js';
+import {
+ adjustHue,
+ customConcat,
+ expressionParser,
+ floorCeil,
+ inRange,
+ lte,
+ matchLightnessChannel,
+ max,
+ min,
+ random
+} from './helpers.js';
+import { toHex } from './converters.js';
+import {
+ interpolate,
+ wcagLuminance,
+ modeRgb,
+ useMode,
+ modeLch,
+ converter,
+ wcagContrast,
+ easingSmootherstep,
+ modeLab,
+ nearest,
+ differenceHyab
+} from 'culori/fn';
+import 'culori/css';
+import {
+ ColorToken,
+ HueColorSpaces,
+ Factor,
+ Order,
+ callback,
+ HueFamily,
+ DeficiencyType
+} from './types.js';
+
+import { matchChromaChannel, sortedArr, checkArg } from './helpers.js';
+
+/**
+ *
+ Gets the hue family which a a color belongs to with the overtone included (if it has one.). For achromatic colors it returns the string "gray".
+ * @param color The color to query its shade or hue family.
+ * @returns The name of the hue family for example red or green.
+ * @example
+ *
+ * import { getHue } from 'huetiful-js'
+
+
+console.log(getHue("#310000"))
+// red
+ */
+function getHueFamily(color: ColorToken, mode?: HueColorSpaces): HueFamily {
+ //Capture the hue value
+
+ return Object.keys(hueTempMap)
+ .map((hue) => {
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+
+ // @ts-ignore
+ var [hueVals, minVal, maxVal] = [
+ customConcat(hueTempMap[hue]),
+ min(hueVals),
+ max(hueVals)
+ ];
+ const bool = customConcat(hueTempMap[hue]).some(() =>
+ inRange(getChannel(`${mode}.h`)(color), minVal, maxVal)
+ );
+
+ return bool && hue;
+ })
+ .filter((val) => typeof val === 'string')[0] as HueFamily;
+}
+
+function lightnessPredicate(colorspace) {
+ return getChannel(`${matchLightnessChannel(colorspace)}`);
+}
+
+function temperaturePredicate(factor: number, temp: 'warm' | 'cool'): boolean {
+ return Object.keys(hueTempMap).some((val) =>
+ inRange(
+ floorCeil(factor),
+ hueTempMap[val][temp][0],
+ hueTempMap[val][temp][1]
+ )
+ );
+}
+
+/**
+ *
+ * Checks if a color can be roughly classified as a cool color. Returns true if color is a cool color else false.
+ * @param color The color to check the temperature.
+ * @returns True or false.
+ * @example
+ *
+ * import { isCool } from 'huetiful-js'
+
+let sample = [
+ "#00ffdc",
+ "#00ff78",
+ "#00c000"
+];
+
+
+console.log(isCool(sample[2]));
+// false
+
+console.log(map(sample, isCool));
+
+// [ true, false, true]
+
+
+
+ */
+function isCool(color: ColorToken): boolean {
+ // First we need to get the hue value which we'll pass to the predicate
+
+ return temperaturePredicate(getChannel('lch.h')(color), 'cool');
+}
+
+/**
+ *
+ * Checks if a color can be roughly classified as a warm color. Returns true if color is a warm color else false.
+ * @param color The color to check the temperature.
+ * @returns True or false.
+ * @example import { isWarm } from 'huetiful-js'
+
+let sample = [
+ "#00ffdc",
+ "#00ff78",
+ "#00c000"
+];
+
+
+
+console.log(isWarm(sample[2]));
+//true
+
+console.log(map(sample, isWarm));
+
+
+// [ false, true, false]
+
+ */
+function isWarm(color: ColorToken): boolean {
+ return temperaturePredicate(getChannel('lch.h')(color), 'cool');
+}
+
+//// Global predicates used by utils such as getNearestHue
+function contrastPredicate(color) {
+ return (against) => getContrast(color, against);
+}
+
+function huePredicate(colorSpace: string) {
+ return (color: ColorToken) =>
+ getChannel(`${checkArg(colorSpace, 'jch')}.h`)(color);
+}
+function chromaPredicate(colorspace) {
+ return (color: ColorToken) =>
+ getChannel(matchChromaChannel(colorspace))(color);
+}
+
+// The baseFunc for getting specifified factor extremums
+function baseFunc(
+ factor: Factor,
+ collection: ColorToken[] | object,
+ cb: callback,
+ order?: Order,
+ colorObj?: boolean
+) {
+ const result: Array<{ factor: number; name: ColorToken }> | number =
+ sortedArr(
+ factor,
+ cb,
+ order as Order,
+ colorObj
+ )(collection).filter((el) => el[factor] !== undefined);
+
+ return (colorObj && result[0]) || result[0][factor];
+}
+
+/**
+ *
+ * Gets the smallest contrast value from the passed in colors compared against a sample color.
+ * @param colors The array of colors to query the color with the smallest contrast value.
+ * @param colorObj Optional boolean that makes the function return a custom object with factor (contrast) and name of the color as keys. Default is false.
+ * @param mode THe mode colorspace to retrieve the contrast value from.
+ * @returns The smallest contrast value in the colors passed in or a custom object.
+ * @example
+ *
+ * import { getNearestContrast } from 'huetiful-js'
+ *
+console.log(getNearestContrast(["b2c3f1", "#a1bd2f", "#f3bac1"], "green"));
+// 2.4061390502133424
+
+console.log(getNearestContrast(["b2c3f1", "#a1bd2f", "#f3bac1"], "green", true));
+// { contrast: 2.4061390502133424, name: '#a1bd2f' }
+ */
+function getNearestContrast(
+ collection: ColorToken[] | object,
+ against: ColorToken,
+ colorObj?: boolean
+) {
+ return baseFunc(
+ 'contrast',
+ colors,
+ contrastPredicate(against),
+ 'desc',
+ colorObj
+ );
+}
+
+/**
+ *
+ * Gets the largest contrast value from the passed in colors compared against a sample color.
+ * @param colors The array of colors to query the color with the largest contrast value.
+ * @param colorObj Optional boolean that makes the function return a custom object with factor (contrast) and name of the color as keys. Default is false.
+ * @param mode THe mode colorspace to retrieve the contrast value from.
+ * @returns The largest contrast value in the colors passed in or a custom object.
+ * @example
+ *
+import { getFarthestContrast } from 'huetiful-js'
+
+console.log(getFarthestContrast(["b2c3f1", "#a1bd2f", "#f3bac1"], "green"));
+// 3.08355493246362
+
+console.log(getFarthestContrast(["b2c3f1", "#a1bd2f", "#f3bac1"], "green", true));
+// { contrast: 3.08355493246362, name: '#f3bac1' }
+
+ */
+
+function getFarthestContrast(
+ collection: ColorToken[] | object,
+ against: ColorToken,
+ colorObj?: boolean
+): number | { factor: number; name: ColorToken } {
+ return baseFunc(
+ 'contrast',
+ colors,
+ contrastPredicate(against),
+ 'desc',
+ colorObj
+ );
+}
+
+/**
+ *
+ * Gets the smallest chroma/saturation value from the passed in colors.
+ * @param colors The array of colors to query the color with the smallest chroma/saturation value.
+ * @param colorspace The mode color space to perform the computation in.
+ * @param colorObj Optional boolean that makes the function return a custom object with factor (saturation) and name of the color as keys. Default is false.
+ * @returns The smallest chroma/saturation value in the colors passed in or a custom object.
+ * @example
+ *
+ * import { getNearestChroma } from 'huetiful-js'
+
+let sample = ['b2c3f1', '#a1bd2f', '#f3bac1']
+
+console.log(getNearestChroma(sample, 'lch'))
+// 22.45669293295522
+ */
+function getNearestChroma(
+ collection: ColorToken[] | object,
+ colorspace?: HueColorSpaces,
+ colorObj = false
+): number | { factor: number; color: ColorToken } {
+ return baseFunc(
+ 'saturation',
+ colors,
+ chromaPredicate(colorspace),
+ 'asc',
+ colorObj
+ );
+}
+
+/**
+ *
+ * Gets the largest saturation value from the passed in colors.
+ * @param colors The array of colors to query the color with the largest saturation value.
+ * @param colorSpace The mode color space to perform the computation in.
+ * @param colorObj Optional boolean that makes the function return a custom object with factor (saturation) and name of the color as keys. Default is false.
+ * @returns The largest saturation value in the colors passed in or a custom object.
+ * @example
+ *
+ * import { getFarthestChroma } from 'huetiful-js'
+
+let sample = ['b2c3f1', '#a1bd2f', '#f3bac1']
+
+console.log(getFarthestChroma(sample, 'lch'))
+// 67.22120855010492
+ */
+function getFarthestChroma(
+ collection: ColorToken[] | object,
+ colorObj = false
+): number | { factor: number; color: ColorToken } {
+ return baseFunc('saturation', colors, chromaPredicate, 'desc', colorObj);
+}
+
+/**
+ *
+ * Gets the smallest hue value from the passed in colors.
+ * @param colors The array of colors to query the color with the smallest hue value.
+ * @param colorspace The mode color space to perform the computation in.
+ * @param colorObj Optional boolean that makes the function return a custom object with factor (hue) and name of the color as keys. Default is false.
+ * @returns The smallest hue value in the colors passed in or a custom object.
+ * @example
+ *
+ * import { getNearestHue } from 'huetiful-js'
+
+let sample = ['b2c3f1', '#a1bd2f', '#f3bac1']
+
+console.log(getNearestHue(sample, 'lch'))
+// 12.462831644544274
+ */
+function getNearestHue(
+ collection: ColorToken[] | object,
+ colorspace?: HueColorSpaces | string,
+ colorObj = false
+): number | { factor: number; color: ColorToken } {
+ return baseFunc('hue', colors, huePredicate(colorspace), 'asc', colorObj);
+}
+
+/**
+ *
+ * Gets the largest hue value from the passed in colors.
+ * @param colors The array of colors to query the color with the largest hue value.
+ * @param colorspace The mode color space to perform the computation in.
+ * @param colorObj Optional boolean that makes the function return a custom object with factor (hue) and name of the color as keys. Default is false.
+ * @returns The largest hue value in the colors passed in or a custom object.
+ * @example
+ *
+ * import { getFarthestHue } from 'huetiful-js'
+let sample = ['b2c3f1', '#a1bd2f', '#f3bac1']
+
+console.log(getFarthestHue(sample, 'lch'))
+// 273.54920266436477
+ */
+function getFarthestHue(
+ collection: ColorToken[] | object,
+ colorspace?: HueColorSpaces,
+ colorObj = false
+): number | { factor: number; color: ColorToken } {
+ return baseFunc('hue', colors, huePredicate(colorspace), 'desc', colorObj);
+}
+
+/**
+ *
+ * Gets the complementary hue of the passed in color. The function is internally guarded against achromatic colors.
+ * @param color The color to retrieve its complimentary hue.
+ * @param colorObj Optional boolean whether to return an object with the result color hue family or just the result color. Default is false.
+ * @returns An object with the hue family and complimentary color as keys.
+ * @example
+ *import { getComplimentaryHue } from "huetiful-js";
+ *
+ *
+console.log(getComplimentaryHue("pink", true))
+//// { hue: 'blue-green', color: '#97dfd7ff' }
+
+console.log(getComplimentaryHue("purple"))
+// #005700ff
+ */
+function getComplimentaryHue(
+ color: ColorToken,
+ colorspace?: HueColorSpaces,
+ colorObj = false
+): { hue: string; color: ColorToken } | ColorToken {
+ const modeChannel = `${colorspace}.h`;
+
+ const complementaryHue: number = adjustHue(
+ getChannel(modeChannel)(color) + 180 * random(0.965, 1)
+ );
+
+ const result: ColorToken | { hue: string; color: ColorToken } =
+ (complementaryHue && {
+ hue: getHueFamily(complementaryHue),
+ color: toHex(setChannel(modeChannel)(color, complementaryHue))
+ }) || { hue: 'gray', color: color };
+
+ return (colorObj && result) || result['color'];
+}
+
+/**
+ *
+ * Sets the value for the specified channel in a color.
+ * @param color Any recognizable color token.
+ * @param mc The mode and channel to work with. For example 'rgb.b'.
+ * @param value The value to set on the queried channel. Also supports expressions as strings e.g set('lch.c)("#fc23a1","*0.5")
+ * @returns color The mutated color.
+ *
+ * @example
+ *
+ * import { setChannel } from 'huetiful-js'
+
+let myColor = setChannel('lch.h')('green',10)
+
+console.log(getChannel('lch.h')(myColor))
+// 10
+ */
+
+function setChannel(mc: string) {
+ return (color: ColorToken, value: number | string): ColorToken => {
+ const [mode, channel] = mc.split('.');
+ // @ts-ignore
+ const src: ColorToken = converter(mode)(toHex(color));
+
+ if (channel) {
+ if (typeof value === 'number') {
+ src[channel] = value;
+ } else if (typeof value === 'string') {
+ expressionParser(src, channel, value);
+ } else {
+ throw new Error(`unsupported value for setChannel`);
+ }
+
+ return src;
+ } else {
+ throw new Error(`unknown channel ${channel} in mode ${mode}`);
+ }
+ };
+}
+
+/**
+ *
+ * Gets the value specifified channel on the color.
+ * @param mc The mode and channel to be retrieved. For example "rgb.b" will return the value of the blue channel in the RGB color space of that color.
+ * @param color The color being queried.
+ * @returns value The value of the queried channel.
+ * @example
+ *
+ * import { getChannel } from 'huetiful-js'
+
+console.log(getChannel('rgb.g')('#a1bd2f'))
+// 0.7411764705882353
+ * */
+function getChannel(mc: string) {
+ return (color: ColorToken): number => {
+ const [mode, channel] = mc.split('.');
+ // @ts-ignore
+ const src = converter(mode)(toHex(color));
+
+ if (channel) {
+ return src[channel];
+ } else {
+ throw Error(`unknown channel ${channel} in mode ${mode}`);
+ }
+ };
+}
+
+/** @alias
+ * Gets the luminance value of that color as defined by WCAG.
+ * @param color The color to query.
+ * @returns value The color's luminance value.
+ * @example
+ *
+ * import { getLuminance } from 'huetiful-js'
+
+console.log(getLuminance('#a1bd2f'))
+// 0.4417749513730954
+ */
+function getLuminance(color: ColorToken): number {
+ return wcagLuminance(toHex(color));
+}
+
+const { pow, abs } = Math;
+const toRgb = useMode(modeRgb);
+/**
+ *
+ * Sets the luminance by interpolating the color with black (to decrease luminance) or white (to increase the luminance).
+ * @param color The color to set luminance
+ * @param lum The amount of luminance to set. The value range is normalised between [0,1]
+ * @returns The mutated color with the modified properties.
+ * @example
+ *
+ * import { setLuminance, getLuminance } from 'huetiful-js'
+
+let myColor = setLuminance('#a1bd2f', 0.5)
+
+console.log(getLuminance(myColor))
+// 0.4999999136285792
+ */
+function setLuminance(color: ColorToken, lum: number): ColorToken {
+ const white = '#ffffff',
+ black = '#000000';
+
+ const EPS = 1e-7;
+ let MAX_ITER = 20;
+
+ if (lum !== undefined && typeof lum == 'number') {
+ (lum == 0 && lum) || black || (lum == 1 && !lum) || white;
+
+ // compute new color using...
+ // @ts-ignore
+ const cur_lum = wcagLuminance(color);
+
+ color = toRgb(toHex(color));
+
+ const test = (low: ColorToken, high: ColorToken) => {
+ //Must add the overrides object to change parameters like easings, fixups, and the mode to perform the computations in.
+ // @ts-ignore
+ const mid = interpolate([low, high])(0.5);
+ const lm = getLuminance(color);
+ // @ts-ignore
+ if (abs(lum - lm > EPS) || !MAX_ITER--) {
+ // close enough
+ return mid;
+ }
+
+ if (lm > lum) {
+ return test(low, mid);
+ } else {
+ return test(mid, high);
+ }
+ };
+
+ let rgb: ColorToken;
+ if (cur_lum > lum) {
+ rgb = test(black, color);
+ } else {
+ rgb = test(color, white);
+ }
+ color = rgb;
+ return color;
+ }
+ // spreading the array values (r,g,b)
+ return rgb2luminance(color);
+}
+
+function rgb2luminance(color: ColorToken): number {
+ color = toRgb(toHex(color));
+
+ // relative luminance
+ // see http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
+ return (
+ 0.7152 * luminance_x(color['g']) +
+ 0.2126 * luminance_x(color['r']) +
+ 0.0722 * luminance_x(color['b'])
+ );
+}
+
+function luminance_x(x: number) {
+ x /= 255;
+ return (lte(x, 0.03928) && x / 12.92) || pow((x + 0.055) / 1.055, 2.4);
+}
+
+/**
+ *
+ * Sets the opacity of a color. Also gets the alpha value of the color if the value param is omitted
+ * @param color The color with the targeted opacity/alpha channel.
+ * @param value The value to apply to the opacity channel. The value is between [0,1]
+ * @returns color The resulting color. Returns an 8 character hex code.
+ * @example
+ *
+ * // Getting the alpha
+console.log(alpha('#a1bd2f0d'))
+// 0.050980392156862744
+
+// Setting the alpha
+
+let myColor = alpha('b2c3f1', 0.5)
+
+console.log(myColor)
+
+// #b2c3f180
+ */
+
+function alpha(color: ColorToken, value?: number | string): number {
+ // We never perfom an operation on an undefined color. Defaults to pure black
+ color = color || 'black';
+
+ const channel = 'alpha';
+ const lch = useMode(modeLch);
+ var src: ColorToken = lch(toHex(color));
+ if (typeof value === 'undefined' || null) {
+ return src[channel];
+ } else if (typeof value === 'number') {
+ if (inRange(value, 0, 1)) {
+ src[channel] = value;
+ } else {
+ src[channel] = value / 100;
+ }
+ } else if (typeof value === 'string') {
+ expressionParser(src, channel, value);
+ }
+ // @ts-ignore
+ return toHex(src);
+}
+
+/**
+ *
+ * Gets the contrast between the passed in colors.
+ * @param color
+ * @param against
+ * @returns The relative luminance of the lightest color.
+ * @example
+ *
+ * import { getContrast } from 'huetiful-js'
+ *
+ * console.log(getContrast("black", "white"));
+ * // 21
+ */
+function getContrast(color: ColorToken, against: ColorToken): number {
+ return wcagContrast(toHex(color), toHex(against));
+}
+
+/**
+ *
+ * Returns the hue which is biasing the passed in color
+ * @param color The color to query its overtone.
+ * @returns The name of the overtone hue. If an achromatic color is passed in it return the string gray otherwise if the color has no bias it returns false.
+ * @example
+ *
+ * import { overtone } from "huetiful-js";
+ *
+console.log(overtone("fefefe"))
+// 'gray'
+
+console.log(overtone("cyan"))
+// 'green'
+
+console.log(overtone("blue"))
+// false
+ */
+function overtone(color: ColorToken): string | boolean {
+ var hue = getHueFamily(color);
+
+ // We check if the color can be found in the defined ranges
+ return (
+ (isAchromatic(color) && 'gray') ||
+ (/-/.test(hue) && hue.split('-')[1]) ||
+ false
+ );
+}
+
+/**
+ *
+ * Gets the smallest lightness value from the passed in colors.
+ * @param colors The array of colors to query the color with the smallest lightness value.
+ * @param colorObj Optional boolean that makes the function return a custom object with factor (lightness) and name of the color as keys. Default is false.
+ * @param mode THe mode colorspace to retrieve the lightness value from.
+ * @returns The smallest lightness value in the colors passed in or a custom object.
+ * @example
+ *
+ * import { getNearestLightness } from 'huetiful-js'
+
+let sample = ["b2c3f1", "#a1bd2f", "#f3bac1"]
+
+console.log(getNearestLightness(sample, true))
+
+// { lightness: 72.61647882089876, name: '#a1bd2f' }
+
+ */
+function getNearestLightness(
+ collection: ColorToken[] | object,
+ colorspace?: HueColorSpaces,
+ colorObj = false
+): number | { factor: number; color: ColorToken } {
+ // @ts-ignore
+
+ return baseFunc(
+ 'lightness',
+ colors,
+ lightnessPredicate(colorspace),
+ 'asc',
+ colorObj
+ );
+}
+
+/**
+ *
+ * Gets the largest lightness value from the passed in colors.
+ * @param colors The array of colors to query the color with the largest lightness value.
+ * @param colorObj Optional boolean that makes the function return a custom object with factor (lightness) and name of the color as keys. Default is false.
+ * @param colorspace THe mode colorspace to retrieve the lightness value from.
+ * @returns The largest lightness value in the colors passed in or a custom object.
+ * @example
+ *
+ * import { getFarthestLightness } from 'huetiful-js'
+
+let sample = ["b2c3f1", "#a1bd2f", "#f3bac1"]
+
+console.log(getFarthestLightness(sample, true))
+
+// { lightness: 80.94668903360088, name: '#f3bac1' }
+
+ */
+function getFarthestLightness(
+ collection: ColorToken[] | object,
+ colorspace?: HueColorSpaces,
+ colorObj = false
+): number | { factor: number; color: ColorToken } {
+ // @ts-ignore
+ return baseFunc(
+ 'lightness',
+ colors,
+ lightnessPredicate(colorspace),
+ 'asc',
+ colorObj
+ );
+}
+
+const toLab = useMode(modeLab);
+/**
+ *
+ * Darkens the color by reducing the lightness channel. .
+ * @param color The color to darken.
+ * @param value The amount to darken with. Also supports expressions as strings e.g darken("#fc23a1","*0.5")
+ * @returns color The darkened color.
+ * @example
+ *
+ *
+
+ */
+function darken(color: ColorToken, value: number | string): ColorToken {
+ const Kn = 18;
+ const channel = 'l';
+
+ const src = toLab(toHex(color));
+
+ if (typeof value === 'number') {
+ src['l'] -= Kn * easingSmootherstep(value / 100);
+ } else if (typeof value === 'string') {
+ // @ts-ignore
+ expressionParser(src, channel, value || 1);
+ }
+
+ return toHex(src);
+}
+
+/**
+ *
+ * @param color The color to brighten.
+ * @param value The amount to brighten with. Also supports expressions as strings e.g darken("#fc23a1","*0.5")
+ * @param mode The color space to compute the color in. Any color space with a lightness channel will do (including HWB)
+ * @returns
+ */
+function brighten(
+ color: ColorToken,
+ value: number | string,
+ colorspace
+): ColorToken {
+ const src = toLab(toHex(color));
+ const ch = matchLightnessChannel(colorspace).split('.')[1];
+ let result = src;
+ if (typeof value == 'number') {
+ result[ch] -= 18 * easingSmootherstep(Math.abs(value) / 100);
+ } else if (typeof value == 'string') {
+ //@ts-ignore
+ result = expressionParser(src, ch, value);
+ }
+
+ return toHex(result);
+}
+
+/**
+ *
+ * Checks if a color is achromatic(without hue or simply grayscale).
+ * @param color The color to test if it is achromatic or not.
+ * @returns boolean Returns true if the color is achromatic else false
+ * @example
+ *
+ * import { isAchromatic } from "huetiful-js";
+import { formatHex8, interpolate, samples } from "culori"
+
+
+isAchromatic('pink')
+// false
+
+let sample = [
+ "#164100",
+ "#ffff00",
+ "#310000",
+ 'pink'
+];
+
+console.log(map(sample, isAchromatic));
+
+// [false, false, false,false]
+
+isAchromatic('gray')
+// Returns true
+
+
+console.log(map(sample, isAchromatic));
+
+
+// we create an interpolation using black and white
+let f = interpolate(["black", "white"]);
+
+//We then create 12 evenly spaced samples and pass them to f as the `t` param required by an interpolating function.
+// Lastly we convert the color to hex for brevity for this example (otherwise color objects work fine too.)
+let grays = map(samples(12), (c) => formatHex8(f(c)));
+console.log(map(grays, isAchromatic));
+
+//
+ [ false, true, true,
+ true, true, true,
+ true, true, true,
+ true, true, false
+]
+
+ */
+function isAchromatic(color: ColorToken, mode?: HueColorSpaces): boolean {
+ // If a color has no lightness then it has no hue so its technically achromatic
+ const props = {
+ lightness: getChannel(`${matchLightnessChannel(mode as string)}`),
+ chroma: getChannel(`${matchChromaChannel(mode as string)}`)(color)
+ };
+
+ // Check if the saturation channel is zero or falsy for color spaces with saturation/chroma channel
+ // @ts-ignore
+ return (props['chroma'] && props['lightness'] !== false) || 0 || NaN;
+}
+
+import {
+ filterDeficiencyDeuter,
+ filterDeficiencyProt,
+ filterDeficiencyTrit,
+ filterGrayscale
+} from 'culori/fn';
+import { colors } from './colors.js';
+
+// This module is focused on creating color blind safe palettes that adhere to the minimum contrast requirements
+
+// How can I achieve this ?
+// 1. First I pass the color(s) to a color vision deficiency simulation function
+// 2. Check if the color has the minimum required contrast as compared to a dark/light mode surface which can optionally be overriden
+// 3. Check the min luminance contrast ratio between the color and background.
+// 4. Find out which channels do I need to tweak in order to fix up the colors.
+// 5. Maybe provide an optional adaptive boolean which returns dark/light mode variant colors of the color blind safe palettes.
+
+// Add reference to articles
+// Read more about the minimum accepted values for palette accessibility
+
+/**
+ *
+ * Returns the color as a simulation of the passed in type of color vision deficiency with the deficiency filter's intensity determined by the severity value.
+ * @param deficiencyType The type of color vision deficiency. To avoid writing the long types, the expected parameters are simply the colors that are hard to perceive for the type of color blindness. For example those with 'tritanopia' are unable to perceive 'blue' light. Default is 'red' when the defeciency parameter is undefined or any falsy value.
+ * @see For a deep dive on color vision deficiency go to
+ * @param color The color to return its deficiency simulated variant.
+ * @param severity The intensity of the filter. The exepected value is between [0,1]. For example 0.5
+ * @returns The color as its simulated variant as a hexadecimal string.
+ * @example
+ *
+ * import { colorDeficiency, toHex } from 'huetiful-js'
+
+// Here we are simulating color blindness of tritanomaly or we can't see 'blue'.
+// We are passing in our color as an array of channel values in the mode "rgb". The severity is set to 0.1
+let tritanomaly = colorDeficiency('blue')
+console.log(tritanomaly(['rgb', 230, 100, 50, 0.5], 0.1))
+// #dd663680
+
+// Here we are simulating color blindness of tritanomaly or we can't see 'red'. The severity is not explicitly set so it defaults to 1
+let protanopia = colorDeficiency('red')
+console.log(protanopia({ h: 20, w: 50, b: 30, mode: 'hwb' }))
+// #9f9f9f
+ */
+function colorDeficiency(deficiencyType?: DeficiencyType) {
+ const baseColorDeficiency = (
+ def: 'red' | 'blue' | 'green' | 'monochromacy',
+ col: ColorToken,
+ sev: number
+ ) => {
+ let result: ColorToken;
+ col = toHex(col);
+ switch (def) {
+ case 'blue': // @ts-ignore
+ result = filterDeficiencyTrit(sev)(col);
+ break;
+ case 'red': // @ts-ignore
+ result = filterDeficiencyProt(sev)(col);
+ break;
+ case 'green': // @ts-ignore
+ result = filterDeficiencyDeuter(sev)(col);
+ break;
+ case 'monochromacy':
+ result = filterGrayscale(sev, 'lch')(col);
+ break;
+ }
+
+ return toHex(result);
+ };
+
+ return (color: ColorToken, severity = 1) => {
+ // Store the keys of deficiency types
+ const deficiencies: string[] = ['red', 'blue', 'green', 'monochromacy'];
+ // Cast 'red' as the default parameter
+ deficiencyType = checkArg(deficiencyType, 'red') as DeficiencyType;
+
+ if (
+ typeof deficiencyType === 'string' &&
+ deficiencies.some((el) => el === deficiencyType)
+ ) {
+ return baseColorDeficiency(deficiencyType, color, severity);
+ } else {
+ throw Error(
+ `Unknown color vision deficiency ${deficiencyType}. The options are the strings 'red' | 'blue' | 'green' | 'monochromacy'`
+ );
+ }
+ };
+}
+
+/**
+ *
+ * @param collection The collection of colors to search for nearest colors
+ * @param color The color to use for distance comparison
+ * @param num The number of colors to return, if the value is above the colors in the available sample, the entire collection is returned with colors ordered in ascending order using the differenceHyab metric.
+ * @returns An array of colors.
+ * @example
+ *
+ * let cols = colors('all', '500')
+ *
+console.log(getNearestColor(cols, 'blue', 3));
+ // [ '#a855f7', '#8b5cf6', '#d946ef' ]
+ */
+function getNearestColor(
+ collection: ColorToken[] | 'tailwind',
+ color: ColorToken,
+ num = 1
+): ColorToken | ColorToken[] {
+ const cb = (collection, color) => {
+ //
+ return nearest(
+ collection as ColorToken[],
+ differenceHyab(),
+ (color) => color as string
+ )(color as string, num);
+ };
+ let result: ColorToken;
+
+ if (collection === 'tailwind') {
+ result = cb(colors('all'), color);
+ } else {
+ result = cb(collection, color);
+ }
+
+ return result;
+}
+
+export {
+ getNearestColor,
+ colorDeficiency,
+ brighten,
+ isAchromatic,
+ alpha,
+ darken,
+ getFarthestLightness,
+ getNearestLightness,
+ overtone,
+ getFarthestChroma,
+ getNearestChroma,
+ setChannel,
+ setLuminance,
+ getChannel,
+ getLuminance,
+ getContrast,
+ getFarthestContrast,
+ getNearestContrast,
+ isCool,
+ isWarm,
+ getHueFamily,
+ getComplimentaryHue,
+ getFarthestHue,
+ getNearestHue
+};
diff --git a/tailwind.config.js b/tailwind.config.js
new file mode 100644
index 00000000..332d51e7
--- /dev/null
+++ b/tailwind.config.js
@@ -0,0 +1,1074 @@
+/** @type {import('tailwindcss').Config} */
+export default {
+ content: ['./templates/*.html'],
+ presets: [],
+ darkMode: 'media', // or 'class'
+ theme: {
+ accentColor: ({ theme }) => ({
+ ...theme('colors'),
+ auto: 'auto'
+ }),
+ animation: {
+ none: 'none',
+ spin: 'spin 1s linear infinite',
+ ping: 'ping 1s cubic-bezier(0, 0, 0.2, 1) infinite',
+ pulse: 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite',
+ bounce: 'bounce 1s infinite'
+ },
+ aria: {
+ busy: 'busy="true"',
+ checked: 'checked="true"',
+ disabled: 'disabled="true"',
+ expanded: 'expanded="true"',
+ hidden: 'hidden="true"',
+ pressed: 'pressed="true"',
+ readonly: 'readonly="true"',
+ required: 'required="true"',
+ selected: 'selected="true"'
+ },
+ aspectRatio: {
+ auto: 'auto',
+ square: '1 / 1',
+ video: '16 / 9'
+ },
+ backdropBlur: ({ theme }) => theme('blur'),
+ backdropBrightness: ({ theme }) => theme('brightness'),
+ backdropContrast: ({ theme }) => theme('contrast'),
+ backdropGrayscale: ({ theme }) => theme('grayscale'),
+ backdropHueRotate: ({ theme }) => theme('hueRotate'),
+ backdropInvert: ({ theme }) => theme('invert'),
+ backdropOpacity: ({ theme }) => theme('opacity'),
+ backdropSaturate: ({ theme }) => theme('saturate'),
+ backdropSepia: ({ theme }) => theme('sepia'),
+ backgroundColor: ({ theme }) => theme('colors'),
+ backgroundImage: {
+ none: 'none',
+ 'gradient-to-t': 'linear-gradient(to top, var(--tw-gradient-stops))',
+ 'gradient-to-tr':
+ 'linear-gradient(to top right, var(--tw-gradient-stops))',
+ 'gradient-to-r': 'linear-gradient(to right, var(--tw-gradient-stops))',
+ 'gradient-to-br':
+ 'linear-gradient(to bottom right, var(--tw-gradient-stops))',
+ 'gradient-to-b': 'linear-gradient(to bottom, var(--tw-gradient-stops))',
+ 'gradient-to-bl':
+ 'linear-gradient(to bottom left, var(--tw-gradient-stops))',
+ 'gradient-to-l': 'linear-gradient(to left, var(--tw-gradient-stops))',
+ 'gradient-to-tl': 'linear-gradient(to top left, var(--tw-gradient-stops))'
+ },
+ backgroundOpacity: ({ theme }) => theme('opacity'),
+ backgroundPosition: {
+ bottom: 'bottom',
+ center: 'center',
+ left: 'left',
+ 'left-bottom': 'left bottom',
+ 'left-top': 'left top',
+ right: 'right',
+ 'right-bottom': 'right bottom',
+ 'right-top': 'right top',
+ top: 'top'
+ },
+ backgroundSize: {
+ auto: 'auto',
+ cover: 'cover',
+ contain: 'contain'
+ },
+ blur: {
+ 0: '0',
+ none: '0',
+ sm: '4px',
+ DEFAULT: '8px',
+ md: '12px',
+ lg: '16px',
+ xl: '24px',
+ '2xl': '40px',
+ '3xl': '64px'
+ },
+ borderColor: ({ theme }) => ({
+ ...theme('colors'),
+ DEFAULT: theme('colors.gray.200', 'currentColor')
+ }),
+ borderOpacity: ({ theme }) => theme('opacity'),
+ borderRadius: {
+ none: '0px',
+ sm: '0.125rem',
+ DEFAULT: '0.25rem',
+ md: '0.375rem',
+ lg: '0.5rem',
+ xl: '0.75rem',
+ '2xl': '1rem',
+ '3xl': '1.5rem',
+ full: '9999px'
+ },
+ borderSpacing: ({ theme }) => ({
+ ...theme('spacing')
+ }),
+ borderWidth: {
+ DEFAULT: '1px',
+ 0: '0px',
+ 2: '2px',
+ 4: '4px',
+ 8: '8px'
+ },
+ boxShadow: {
+ sm: '0 1px 2px 0 rgb(0 0 0 / 0.05)',
+ DEFAULT: '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
+ md: '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
+ lg: '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)',
+ xl: '0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)',
+ '2xl': '0 25px 50px -12px rgb(0 0 0 / 0.25)',
+ inner: 'inset 0 2px 4px 0 rgb(0 0 0 / 0.05)',
+ none: 'none'
+ },
+ boxShadowColor: ({ theme }) => theme('colors'),
+ brightness: {
+ 0: '0',
+ 50: '.5',
+ 75: '.75',
+ 90: '.9',
+ 95: '.95',
+ 100: '1',
+ 105: '1.05',
+ 110: '1.1',
+ 125: '1.25',
+ 150: '1.5',
+ 200: '2'
+ },
+ caretColor: ({ theme }) => theme('colors'),
+ colors: ({ colors }) => ({
+ inherit: colors.inherit,
+ current: colors.current,
+ transparent: colors.transparent,
+ black: colors.black,
+ white: colors.white,
+ slate: colors.slate,
+ gray: colors.gray,
+ zinc: colors.zinc,
+ neutral: colors.neutral,
+ stone: colors.stone,
+ red: colors.red,
+ orange: colors.orange,
+ amber: colors.amber,
+ yellow: colors.yellow,
+ lime: colors.lime,
+ green: colors.green,
+ emerald: colors.emerald,
+ teal: colors.teal,
+ cyan: colors.cyan,
+ sky: colors.sky,
+ blue: colors.blue,
+ indigo: colors.indigo,
+ violet: colors.violet,
+ purple: colors.purple,
+ fuchsia: colors.fuchsia,
+ pink: colors.pink,
+ rose: colors.rose
+ }),
+ columns: {
+ auto: 'auto',
+ 1: '1',
+ 2: '2',
+ 3: '3',
+ 4: '4',
+ 5: '5',
+ 6: '6',
+ 7: '7',
+ 8: '8',
+ 9: '9',
+ 10: '10',
+ 11: '11',
+ 12: '12',
+ '3xs': '16rem',
+ '2xs': '18rem',
+ xs: '20rem',
+ sm: '24rem',
+ md: '28rem',
+ lg: '32rem',
+ xl: '36rem',
+ '2xl': '42rem',
+ '3xl': '48rem',
+ '4xl': '56rem',
+ '5xl': '64rem',
+ '6xl': '72rem',
+ '7xl': '80rem'
+ },
+ container: {},
+ content: {
+ none: 'none'
+ },
+ contrast: {
+ 0: '0',
+ 50: '.5',
+ 75: '.75',
+ 100: '1',
+ 125: '1.25',
+ 150: '1.5',
+ 200: '2'
+ },
+ cursor: {
+ auto: 'auto',
+ default: 'default',
+ pointer: 'pointer',
+ wait: 'wait',
+ text: 'text',
+ move: 'move',
+ help: 'help',
+ 'not-allowed': 'not-allowed',
+ none: 'none',
+ 'context-menu': 'context-menu',
+ progress: 'progress',
+ cell: 'cell',
+ crosshair: 'crosshair',
+ 'vertical-text': 'vertical-text',
+ alias: 'alias',
+ copy: 'copy',
+ 'no-drop': 'no-drop',
+ grab: 'grab',
+ grabbing: 'grabbing',
+ 'all-scroll': 'all-scroll',
+ 'col-resize': 'col-resize',
+ 'row-resize': 'row-resize',
+ 'n-resize': 'n-resize',
+ 'e-resize': 'e-resize',
+ 's-resize': 's-resize',
+ 'w-resize': 'w-resize',
+ 'ne-resize': 'ne-resize',
+ 'nw-resize': 'nw-resize',
+ 'se-resize': 'se-resize',
+ 'sw-resize': 'sw-resize',
+ 'ew-resize': 'ew-resize',
+ 'ns-resize': 'ns-resize',
+ 'nesw-resize': 'nesw-resize',
+ 'nwse-resize': 'nwse-resize',
+ 'zoom-in': 'zoom-in',
+ 'zoom-out': 'zoom-out'
+ },
+ divideColor: ({ theme }) => theme('borderColor'),
+ divideOpacity: ({ theme }) => theme('borderOpacity'),
+ divideWidth: ({ theme }) => theme('borderWidth'),
+ dropShadow: {
+ sm: '0 1px 1px rgb(0 0 0 / 0.05)',
+ DEFAULT: ['0 1px 2px rgb(0 0 0 / 0.1)', '0 1px 1px rgb(0 0 0 / 0.06)'],
+ md: ['0 4px 3px rgb(0 0 0 / 0.07)', '0 2px 2px rgb(0 0 0 / 0.06)'],
+ lg: ['0 10px 8px rgb(0 0 0 / 0.04)', '0 4px 3px rgb(0 0 0 / 0.1)'],
+ xl: ['0 20px 13px rgb(0 0 0 / 0.03)', '0 8px 5px rgb(0 0 0 / 0.08)'],
+ '2xl': '0 25px 25px rgb(0 0 0 / 0.15)',
+ none: '0 0 #0000'
+ },
+ fill: ({ theme }) => ({
+ none: 'none',
+ ...theme('colors')
+ }),
+ flex: {
+ 1: '1 1 0%',
+ auto: '1 1 auto',
+ initial: '0 1 auto',
+ none: 'none'
+ },
+ flexBasis: ({ theme }) => ({
+ auto: 'auto',
+ ...theme('spacing'),
+ '1/2': '50%',
+ '1/3': '33.333333%',
+ '2/3': '66.666667%',
+ '1/4': '25%',
+ '2/4': '50%',
+ '3/4': '75%',
+ '1/5': '20%',
+ '2/5': '40%',
+ '3/5': '60%',
+ '4/5': '80%',
+ '1/6': '16.666667%',
+ '2/6': '33.333333%',
+ '3/6': '50%',
+ '4/6': '66.666667%',
+ '5/6': '83.333333%',
+ '1/12': '8.333333%',
+ '2/12': '16.666667%',
+ '3/12': '25%',
+ '4/12': '33.333333%',
+ '5/12': '41.666667%',
+ '6/12': '50%',
+ '7/12': '58.333333%',
+ '8/12': '66.666667%',
+ '9/12': '75%',
+ '10/12': '83.333333%',
+ '11/12': '91.666667%',
+ full: '100%'
+ }),
+ flexGrow: {
+ 0: '0',
+ DEFAULT: '1'
+ },
+ flexShrink: {
+ 0: '0',
+ DEFAULT: '1'
+ },
+ fontFamily: {
+ sans: [
+ 'ui-sans-serif',
+ 'system-ui',
+ 'sans-serif',
+ '"Apple Color Emoji"',
+ '"Segoe UI Emoji"',
+ '"Segoe UI Symbol"',
+ '"Noto Color Emoji"'
+ ],
+ serif: [
+ 'ui-serif',
+ 'Georgia',
+ 'Cambria',
+ '"Times New Roman"',
+ 'Times',
+ 'serif'
+ ],
+ mono: [
+ 'ui-monospace',
+ 'SFMono-Regular',
+ 'Menlo',
+ 'Monaco',
+ 'Consolas',
+ '"Liberation Mono"',
+ '"Courier New"',
+ 'monospace'
+ ]
+ },
+ fontSize: {
+ xs: ['0.75rem', { lineHeight: '1rem' }],
+ sm: ['0.875rem', { lineHeight: '1.25rem' }],
+ base: ['1rem', { lineHeight: '1.5rem' }],
+ lg: ['1.125rem', { lineHeight: '1.75rem' }],
+ xl: ['1.25rem', { lineHeight: '1.75rem' }],
+ '2xl': ['1.5rem', { lineHeight: '2rem' }],
+ '3xl': ['1.875rem', { lineHeight: '2.25rem' }],
+ '4xl': ['2.25rem', { lineHeight: '2.5rem' }],
+ '5xl': ['3rem', { lineHeight: '1' }],
+ '6xl': ['3.75rem', { lineHeight: '1' }],
+ '7xl': ['4.5rem', { lineHeight: '1' }],
+ '8xl': ['6rem', { lineHeight: '1' }],
+ '9xl': ['8rem', { lineHeight: '1' }]
+ },
+ fontWeight: {
+ thin: '100',
+ extralight: '200',
+ light: '300',
+ normal: '400',
+ medium: '500',
+ semibold: '600',
+ bold: '700',
+ extrabold: '800',
+ black: '900'
+ },
+ gap: ({ theme }) => theme('spacing'),
+ gradientColorStops: ({ theme }) => theme('colors'),
+ gradientColorStopPositions: {
+ '0%': '0%',
+ '5%': '5%',
+ '10%': '10%',
+ '15%': '15%',
+ '20%': '20%',
+ '25%': '25%',
+ '30%': '30%',
+ '35%': '35%',
+ '40%': '40%',
+ '45%': '45%',
+ '50%': '50%',
+ '55%': '55%',
+ '60%': '60%',
+ '65%': '65%',
+ '70%': '70%',
+ '75%': '75%',
+ '80%': '80%',
+ '85%': '85%',
+ '90%': '90%',
+ '95%': '95%',
+ '100%': '100%'
+ },
+ grayscale: {
+ 0: '0',
+ DEFAULT: '100%'
+ },
+ gridAutoColumns: {
+ auto: 'auto',
+ min: 'min-content',
+ max: 'max-content',
+ fr: 'minmax(0, 1fr)'
+ },
+ gridAutoRows: {
+ auto: 'auto',
+ min: 'min-content',
+ max: 'max-content',
+ fr: 'minmax(0, 1fr)'
+ },
+ gridColumn: {
+ auto: 'auto',
+ 'span-1': 'span 1 / span 1',
+ 'span-2': 'span 2 / span 2',
+ 'span-3': 'span 3 / span 3',
+ 'span-4': 'span 4 / span 4',
+ 'span-5': 'span 5 / span 5',
+ 'span-6': 'span 6 / span 6',
+ 'span-7': 'span 7 / span 7',
+ 'span-8': 'span 8 / span 8',
+ 'span-9': 'span 9 / span 9',
+ 'span-10': 'span 10 / span 10',
+ 'span-11': 'span 11 / span 11',
+ 'span-12': 'span 12 / span 12',
+ 'span-full': '1 / -1'
+ },
+ gridColumnEnd: {
+ auto: 'auto',
+ 1: '1',
+ 2: '2',
+ 3: '3',
+ 4: '4',
+ 5: '5',
+ 6: '6',
+ 7: '7',
+ 8: '8',
+ 9: '9',
+ 10: '10',
+ 11: '11',
+ 12: '12',
+ 13: '13'
+ },
+ gridColumnStart: {
+ auto: 'auto',
+ 1: '1',
+ 2: '2',
+ 3: '3',
+ 4: '4',
+ 5: '5',
+ 6: '6',
+ 7: '7',
+ 8: '8',
+ 9: '9',
+ 10: '10',
+ 11: '11',
+ 12: '12',
+ 13: '13'
+ },
+ gridRow: {
+ auto: 'auto',
+ 'span-1': 'span 1 / span 1',
+ 'span-2': 'span 2 / span 2',
+ 'span-3': 'span 3 / span 3',
+ 'span-4': 'span 4 / span 4',
+ 'span-5': 'span 5 / span 5',
+ 'span-6': 'span 6 / span 6',
+ 'span-7': 'span 7 / span 7',
+ 'span-8': 'span 8 / span 8',
+ 'span-9': 'span 9 / span 9',
+ 'span-10': 'span 10 / span 10',
+ 'span-11': 'span 11 / span 11',
+ 'span-12': 'span 12 / span 12',
+ 'span-full': '1 / -1'
+ },
+ gridRowEnd: {
+ auto: 'auto',
+ 1: '1',
+ 2: '2',
+ 3: '3',
+ 4: '4',
+ 5: '5',
+ 6: '6',
+ 7: '7',
+ 8: '8',
+ 9: '9',
+ 10: '10',
+ 11: '11',
+ 12: '12',
+ 13: '13'
+ },
+ gridRowStart: {
+ auto: 'auto',
+ 1: '1',
+ 2: '2',
+ 3: '3',
+ 4: '4',
+ 5: '5',
+ 6: '6',
+ 7: '7',
+ 8: '8',
+ 9: '9',
+ 10: '10',
+ 11: '11',
+ 12: '12',
+ 13: '13'
+ },
+ gridTemplateColumns: {
+ none: 'none',
+ subgrid: 'subgrid',
+ 1: 'repeat(1, minmax(0, 1fr))',
+ 2: 'repeat(2, minmax(0, 1fr))',
+ 3: 'repeat(3, minmax(0, 1fr))',
+ 4: 'repeat(4, minmax(0, 1fr))',
+ 5: 'repeat(5, minmax(0, 1fr))',
+ 6: 'repeat(6, minmax(0, 1fr))',
+ 7: 'repeat(7, minmax(0, 1fr))',
+ 8: 'repeat(8, minmax(0, 1fr))',
+ 9: 'repeat(9, minmax(0, 1fr))',
+ 10: 'repeat(10, minmax(0, 1fr))',
+ 11: 'repeat(11, minmax(0, 1fr))',
+ 12: 'repeat(12, minmax(0, 1fr))'
+ },
+ gridTemplateRows: {
+ none: 'none',
+ subgrid: 'subgrid',
+ 1: 'repeat(1, minmax(0, 1fr))',
+ 2: 'repeat(2, minmax(0, 1fr))',
+ 3: 'repeat(3, minmax(0, 1fr))',
+ 4: 'repeat(4, minmax(0, 1fr))',
+ 5: 'repeat(5, minmax(0, 1fr))',
+ 6: 'repeat(6, minmax(0, 1fr))',
+ 7: 'repeat(7, minmax(0, 1fr))',
+ 8: 'repeat(8, minmax(0, 1fr))',
+ 9: 'repeat(9, minmax(0, 1fr))',
+ 10: 'repeat(10, minmax(0, 1fr))',
+ 11: 'repeat(11, minmax(0, 1fr))',
+ 12: 'repeat(12, minmax(0, 1fr))'
+ },
+ height: ({ theme }) => ({
+ auto: 'auto',
+ ...theme('spacing'),
+ '1/2': '50%',
+ '1/3': '33.333333%',
+ '2/3': '66.666667%',
+ '1/4': '25%',
+ '2/4': '50%',
+ '3/4': '75%',
+ '1/5': '20%',
+ '2/5': '40%',
+ '3/5': '60%',
+ '4/5': '80%',
+ '1/6': '16.666667%',
+ '2/6': '33.333333%',
+ '3/6': '50%',
+ '4/6': '66.666667%',
+ '5/6': '83.333333%',
+ full: '100%',
+ screen: '100vh',
+ svh: '100svh',
+ lvh: '100lvh',
+ dvh: '100dvh',
+ min: 'min-content',
+ max: 'max-content',
+ fit: 'fit-content'
+ }),
+ hueRotate: {
+ 0: '0deg',
+ 15: '15deg',
+ 30: '30deg',
+ 60: '60deg',
+ 90: '90deg',
+ 180: '180deg'
+ },
+ inset: ({ theme }) => ({
+ auto: 'auto',
+ ...theme('spacing'),
+ '1/2': '50%',
+ '1/3': '33.333333%',
+ '2/3': '66.666667%',
+ '1/4': '25%',
+ '2/4': '50%',
+ '3/4': '75%',
+ full: '100%'
+ }),
+ invert: {
+ 0: '0',
+ DEFAULT: '100%'
+ },
+ keyframes: {
+ spin: {
+ to: {
+ transform: 'rotate(360deg)'
+ }
+ },
+ ping: {
+ '75%, 100%': {
+ transform: 'scale(2)',
+ opacity: '0'
+ }
+ },
+ pulse: {
+ '50%': {
+ opacity: '.5'
+ }
+ },
+ bounce: {
+ '0%, 100%': {
+ transform: 'translateY(-25%)',
+ animationTimingFunction: 'cubic-bezier(0.8,0,1,1)'
+ },
+ '50%': {
+ transform: 'none',
+ animationTimingFunction: 'cubic-bezier(0,0,0.2,1)'
+ }
+ }
+ },
+ letterSpacing: {
+ tighter: '-0.05em',
+ tight: '-0.025em',
+ normal: '0em',
+ wide: '0.025em',
+ wider: '0.05em',
+ widest: '0.1em'
+ },
+ lineHeight: {
+ none: '1',
+ tight: '1.25',
+ snug: '1.375',
+ normal: '1.5',
+ relaxed: '1.625',
+ loose: '2',
+ 3: '.75rem',
+ 4: '1rem',
+ 5: '1.25rem',
+ 6: '1.5rem',
+ 7: '1.75rem',
+ 8: '2rem',
+ 9: '2.25rem',
+ 10: '2.5rem'
+ },
+ listStyleType: {
+ none: 'none',
+ disc: 'disc',
+ decimal: 'decimal'
+ },
+ listStyleImage: {
+ none: 'none'
+ },
+ margin: ({ theme }) => ({
+ auto: 'auto',
+ ...theme('spacing')
+ }),
+ lineClamp: {
+ 1: '1',
+ 2: '2',
+ 3: '3',
+ 4: '4',
+ 5: '5',
+ 6: '6'
+ },
+ maxHeight: ({ theme }) => ({
+ ...theme('spacing'),
+ none: 'none',
+ full: '100%',
+ screen: '100vh',
+ svh: '100svh',
+ lvh: '100lvh',
+ dvh: '100dvh',
+ min: 'min-content',
+ max: 'max-content',
+ fit: 'fit-content'
+ }),
+ maxWidth: ({ theme, breakpoints }) => ({
+ ...theme('spacing'),
+ none: 'none',
+ xs: '20rem',
+ sm: '24rem',
+ md: '28rem',
+ lg: '32rem',
+ xl: '36rem',
+ '2xl': '42rem',
+ '3xl': '48rem',
+ '4xl': '56rem',
+ '5xl': '64rem',
+ '6xl': '72rem',
+ '7xl': '80rem',
+ full: '100%',
+ min: 'min-content',
+ max: 'max-content',
+ fit: 'fit-content',
+ prose: '65ch',
+ ...breakpoints(theme('screens'))
+ }),
+ minHeight: ({ theme }) => ({
+ ...theme('spacing'),
+ full: '100%',
+ screen: '100vh',
+ svh: '100svh',
+ lvh: '100lvh',
+ dvh: '100dvh',
+ min: 'min-content',
+ max: 'max-content',
+ fit: 'fit-content'
+ }),
+ minWidth: ({ theme }) => ({
+ ...theme('spacing'),
+ full: '100%',
+ min: 'min-content',
+ max: 'max-content',
+ fit: 'fit-content'
+ }),
+ objectPosition: {
+ bottom: 'bottom',
+ center: 'center',
+ left: 'left',
+ 'left-bottom': 'left bottom',
+ 'left-top': 'left top',
+ right: 'right',
+ 'right-bottom': 'right bottom',
+ 'right-top': 'right top',
+ top: 'top'
+ },
+ opacity: {
+ 0: '0',
+ 5: '0.05',
+ 10: '0.1',
+ 15: '0.15',
+ 20: '0.2',
+ 25: '0.25',
+ 30: '0.3',
+ 35: '0.35',
+ 40: '0.4',
+ 45: '0.45',
+ 50: '0.5',
+ 55: '0.55',
+ 60: '0.6',
+ 65: '0.65',
+ 70: '0.7',
+ 75: '0.75',
+ 80: '0.8',
+ 85: '0.85',
+ 90: '0.9',
+ 95: '0.95',
+ 100: '1'
+ },
+ order: {
+ first: '-9999',
+ last: '9999',
+ none: '0',
+ 1: '1',
+ 2: '2',
+ 3: '3',
+ 4: '4',
+ 5: '5',
+ 6: '6',
+ 7: '7',
+ 8: '8',
+ 9: '9',
+ 10: '10',
+ 11: '11',
+ 12: '12'
+ },
+ outlineColor: ({ theme }) => theme('colors'),
+ outlineOffset: {
+ 0: '0px',
+ 1: '1px',
+ 2: '2px',
+ 4: '4px',
+ 8: '8px'
+ },
+ outlineWidth: {
+ 0: '0px',
+ 1: '1px',
+ 2: '2px',
+ 4: '4px',
+ 8: '8px'
+ },
+ padding: ({ theme }) => theme('spacing'),
+ placeholderColor: ({ theme }) => theme('colors'),
+ placeholderOpacity: ({ theme }) => theme('opacity'),
+ ringColor: ({ theme }) => ({
+ DEFAULT: theme('colors.blue.500', '#3b82f6'),
+ ...theme('colors')
+ }),
+ ringOffsetColor: ({ theme }) => theme('colors'),
+ ringOffsetWidth: {
+ 0: '0px',
+ 1: '1px',
+ 2: '2px',
+ 4: '4px',
+ 8: '8px'
+ },
+ ringOpacity: ({ theme }) => ({
+ DEFAULT: '0.5',
+ ...theme('opacity')
+ }),
+ ringWidth: {
+ DEFAULT: '3px',
+ 0: '0px',
+ 1: '1px',
+ 2: '2px',
+ 4: '4px',
+ 8: '8px'
+ },
+ rotate: {
+ 0: '0deg',
+ 1: '1deg',
+ 2: '2deg',
+ 3: '3deg',
+ 6: '6deg',
+ 12: '12deg',
+ 45: '45deg',
+ 90: '90deg',
+ 180: '180deg'
+ },
+ saturate: {
+ 0: '0',
+ 50: '.5',
+ 100: '1',
+ 150: '1.5',
+ 200: '2'
+ },
+ scale: {
+ 0: '0',
+ 50: '.5',
+ 75: '.75',
+ 90: '.9',
+ 95: '.95',
+ 100: '1',
+ 105: '1.05',
+ 110: '1.1',
+ 125: '1.25',
+ 150: '1.5'
+ },
+ screens: {
+ sm: '640px',
+ md: '768px',
+ lg: '1024px',
+ xl: '1280px',
+ '2xl': '1536px'
+ },
+ scrollMargin: ({ theme }) => ({
+ ...theme('spacing')
+ }),
+ scrollPadding: ({ theme }) => theme('spacing'),
+ sepia: {
+ 0: '0',
+ DEFAULT: '100%'
+ },
+ skew: {
+ 0: '0deg',
+ 1: '1deg',
+ 2: '2deg',
+ 3: '3deg',
+ 6: '6deg',
+ 12: '12deg'
+ },
+ space: ({ theme }) => ({
+ ...theme('spacing')
+ }),
+ spacing: {
+ px: '1px',
+ 0: '0px',
+ 0.5: '0.125rem',
+ 1: '0.25rem',
+ 1.5: '0.375rem',
+ 2: '0.5rem',
+ 2.5: '0.625rem',
+ 3: '0.75rem',
+ 3.5: '0.875rem',
+ 4: '1rem',
+ 5: '1.25rem',
+ 6: '1.5rem',
+ 7: '1.75rem',
+ 8: '2rem',
+ 9: '2.25rem',
+ 10: '2.5rem',
+ 11: '2.75rem',
+ 12: '3rem',
+ 14: '3.5rem',
+ 16: '4rem',
+ 20: '5rem',
+ 24: '6rem',
+ 28: '7rem',
+ 32: '8rem',
+ 36: '9rem',
+ 40: '10rem',
+ 44: '11rem',
+ 48: '12rem',
+ 52: '13rem',
+ 56: '14rem',
+ 60: '15rem',
+ 64: '16rem',
+ 72: '18rem',
+ 80: '20rem',
+ 96: '24rem'
+ },
+ stroke: ({ theme }) => ({
+ none: 'none',
+ ...theme('colors')
+ }),
+ strokeWidth: {
+ 0: '0',
+ 1: '1',
+ 2: '2'
+ },
+ supports: {},
+ data: {},
+ textColor: ({ theme }) => theme('colors'),
+ textDecorationColor: ({ theme }) => theme('colors'),
+ textDecorationThickness: {
+ auto: 'auto',
+ 'from-font': 'from-font',
+ 0: '0px',
+ 1: '1px',
+ 2: '2px',
+ 4: '4px',
+ 8: '8px'
+ },
+ textIndent: ({ theme }) => ({
+ ...theme('spacing')
+ }),
+ textOpacity: ({ theme }) => theme('opacity'),
+ textUnderlineOffset: {
+ auto: 'auto',
+ 0: '0px',
+ 1: '1px',
+ 2: '2px',
+ 4: '4px',
+ 8: '8px'
+ },
+ transformOrigin: {
+ center: 'center',
+ top: 'top',
+ 'top-right': 'top right',
+ right: 'right',
+ 'bottom-right': 'bottom right',
+ bottom: 'bottom',
+ 'bottom-left': 'bottom left',
+ left: 'left',
+ 'top-left': 'top left'
+ },
+ transitionDelay: {
+ 0: '0s',
+ 75: '75ms',
+ 100: '100ms',
+ 150: '150ms',
+ 200: '200ms',
+ 300: '300ms',
+ 500: '500ms',
+ 700: '700ms',
+ 1000: '1000ms'
+ },
+ transitionDuration: {
+ DEFAULT: '150ms',
+ 0: '0s',
+ 75: '75ms',
+ 100: '100ms',
+ 150: '150ms',
+ 200: '200ms',
+ 300: '300ms',
+ 500: '500ms',
+ 700: '700ms',
+ 1000: '1000ms'
+ },
+ transitionProperty: {
+ none: 'none',
+ all: 'all',
+ DEFAULT:
+ 'color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter',
+ colors:
+ 'color, background-color, border-color, text-decoration-color, fill, stroke',
+ opacity: 'opacity',
+ shadow: 'box-shadow',
+ transform: 'transform'
+ },
+ transitionTimingFunction: {
+ DEFAULT: 'cubic-bezier(0.4, 0, 0.2, 1)',
+ linear: 'linear',
+ in: 'cubic-bezier(0.4, 0, 1, 1)',
+ out: 'cubic-bezier(0, 0, 0.2, 1)',
+ 'in-out': 'cubic-bezier(0.4, 0, 0.2, 1)'
+ },
+ translate: ({ theme }) => ({
+ ...theme('spacing'),
+ '1/2': '50%',
+ '1/3': '33.333333%',
+ '2/3': '66.666667%',
+ '1/4': '25%',
+ '2/4': '50%',
+ '3/4': '75%',
+ full: '100%'
+ }),
+ size: ({ theme }) => ({
+ auto: 'auto',
+ ...theme('spacing'),
+ '1/2': '50%',
+ '1/3': '33.333333%',
+ '2/3': '66.666667%',
+ '1/4': '25%',
+ '2/4': '50%',
+ '3/4': '75%',
+ '1/5': '20%',
+ '2/5': '40%',
+ '3/5': '60%',
+ '4/5': '80%',
+ '1/6': '16.666667%',
+ '2/6': '33.333333%',
+ '3/6': '50%',
+ '4/6': '66.666667%',
+ '5/6': '83.333333%',
+ '1/12': '8.333333%',
+ '2/12': '16.666667%',
+ '3/12': '25%',
+ '4/12': '33.333333%',
+ '5/12': '41.666667%',
+ '6/12': '50%',
+ '7/12': '58.333333%',
+ '8/12': '66.666667%',
+ '9/12': '75%',
+ '10/12': '83.333333%',
+ '11/12': '91.666667%',
+ full: '100%',
+ min: 'min-content',
+ max: 'max-content',
+ fit: 'fit-content'
+ }),
+ width: ({ theme }) => ({
+ auto: 'auto',
+ ...theme('spacing'),
+ '1/2': '50%',
+ '1/3': '33.333333%',
+ '2/3': '66.666667%',
+ '1/4': '25%',
+ '2/4': '50%',
+ '3/4': '75%',
+ '1/5': '20%',
+ '2/5': '40%',
+ '3/5': '60%',
+ '4/5': '80%',
+ '1/6': '16.666667%',
+ '2/6': '33.333333%',
+ '3/6': '50%',
+ '4/6': '66.666667%',
+ '5/6': '83.333333%',
+ '1/12': '8.333333%',
+ '2/12': '16.666667%',
+ '3/12': '25%',
+ '4/12': '33.333333%',
+ '5/12': '41.666667%',
+ '6/12': '50%',
+ '7/12': '58.333333%',
+ '8/12': '66.666667%',
+ '9/12': '75%',
+ '10/12': '83.333333%',
+ '11/12': '91.666667%',
+ full: '100%',
+ screen: '100vw',
+ svw: '100svw',
+ lvw: '100lvw',
+ dvw: '100dvw',
+ min: 'min-content',
+ max: 'max-content',
+ fit: 'fit-content'
+ }),
+ willChange: {
+ auto: 'auto',
+ scroll: 'scroll-position',
+ contents: 'contents',
+ transform: 'transform'
+ },
+ zIndex: {
+ auto: 'auto',
+ 0: '0',
+ 10: '10',
+ 20: '20',
+ 30: '30',
+ 40: '40',
+ 50: '50'
+ }
+ },
+ plugins: []
+};
diff --git a/test/colors.spec.js b/test/colors.spec.js
new file mode 100644
index 00000000..72397866
--- /dev/null
+++ b/test/colors.spec.js
@@ -0,0 +1,14 @@
+import * as sortBy from "../lib/huetiful.esm.mjs";
+
+/**
+ * @license
+ * colors.ts - Test suite for huetiful-js colors module.
+Copyright 2023 Dean Tarisai.
+This file is licensed to you under the Apache License, Version 2.0 (the 'License');
+you may not use this file except in compliance with the License. You may obtain a copy
+of the License at http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software distributed under
+the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+OF ANY KIND, either express or implied. See the License for the specific language
+governing permissions and limitations under the License.
+*/
diff --git a/test/converters.spec.js b/test/converters.spec.js
new file mode 100644
index 00000000..3ed1bf36
--- /dev/null
+++ b/test/converters.spec.js
@@ -0,0 +1,14 @@
+import * as sortBy from "../lib/huetiful.esm.mjs";
+
+/**
+ * @license
+ * converters.ts - Test suite for huetiful-js converters module.
+Copyright 2023 Dean Tarisai.
+This file is licensed to you under the Apache License, Version 2.0 (the 'License');
+you may not use this file except in compliance with the License. You may obtain a copy
+of the License at http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software distributed under
+the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+OF ANY KIND, either express or implied. See the License for the specific language
+governing permissions and limitations under the License.
+*/
diff --git a/test/filterBy.spec.js b/test/filterBy.spec.js
new file mode 100644
index 00000000..ef0bbb06
--- /dev/null
+++ b/test/filterBy.spec.js
@@ -0,0 +1,14 @@
+import * as sortBy from "../lib/huetiful.esm.mjs";
+
+/**
+ * @license
+ * filterBy.ts - Test suite for huetiful-js color filtering module.
+Copyright 2023 Dean Tarisai.
+This file is licensed to you under the Apache License, Version 2.0 (the 'License');
+you may not use this file except in compliance with the License. You may obtain a copy
+of the License at http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software distributed under
+the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+OF ANY KIND, either express or implied. See the License for the specific language
+governing permissions and limitations under the License.
+*/
diff --git a/test/generators.spec.js b/test/generators.spec.js
new file mode 100644
index 00000000..383a85c9
--- /dev/null
+++ b/test/generators.spec.js
@@ -0,0 +1,14 @@
+import * as sortBy from "../lib/huetiful.esm.mjs";
+
+/**
+ * @license
+ * sortBy.ts - Test suite for huetiful-js palette generator module.
+Copyright 2023 Dean Tarisai.
+This file is licensed to you under the Apache License, Version 2.0 (the 'License');
+you may not use this file except in compliance with the License. You may obtain a copy
+of the License at http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software distributed under
+the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+OF ANY KIND, either express or implied. See the License for the specific language
+governing permissions and limitations under the License.
+*/
diff --git a/test/sortBy.spec.js b/test/sortBy.spec.js
new file mode 100644
index 00000000..24e76a48
--- /dev/null
+++ b/test/sortBy.spec.js
@@ -0,0 +1,14 @@
+import * as sortBy from "../lib/huetiful.esm.mjs";
+
+/**
+ * @license
+ * sortBy.ts - Test suite for huetiful-js color sorting module.
+Copyright 2023 Dean Tarisai.
+This file is licensed to you under the Apache License, Version 2.0 (the 'License');
+you may not use this file except in compliance with the License. You may obtain a copy
+of the License at http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software distributed under
+the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+OF ANY KIND, either express or implied. See the License for the specific language
+governing permissions and limitations under the License.
+*/
diff --git a/test/utils.spec.js b/test/utils.spec.js
new file mode 100644
index 00000000..7bc950ca
--- /dev/null
+++ b/test/utils.spec.js
@@ -0,0 +1,25 @@
+import * as utils from "../source/lib/huetiful.esm.mjs";
+
+/**
+ * @license
+ * utils.ts - Test suite for huetiful-js utils module.
+Copyright 2023 Dean Tarisai.
+This file is licensed to you under the Apache License, Version 2.0 (the 'License');
+you may not use this file except in compliance with the License. You may obtain a copy
+of the License at http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software distributed under
+the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+OF ANY KIND, either express or implied. See the License for the specific language
+governing permissions and limitations under the License.
+*/
+
+it(`should get the passed in color's hue family`, function () {
+ const colors = [
+ ["rgb", 60, 79, 165],
+ "ffce33",
+ { j: 0.1105, c: 0.18, h: 163, mode: "jch" },
+ 679000,
+ ];
+ const hues = colors.map((el) => utils.getHueFamily(el));
+ expect(hues).toBe(colors);
+});
diff --git a/tsconfig.json b/tsconfig.json
index 5d5fb6f5..0b9f75db 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,17 +1,20 @@
{
"include": [
- "src/index.ts"
- ],
+ "./src/index.ts"
+ ],"exclude":["**/*/node_modules"],
"compilerOptions": {
+ "lib":["es2015"],
+ "target":"es2015",
"allowImportingTsExtensions": true,
"skipLibCheck": true,
// "allowJs": true,
- "checkJs": true,
+ // "checkJs": true,
"declaration": true,
// "declarationMap": true,
"emitDeclarationOnly": true,
"isolatedModules": true,
"forceConsistentCasingInFileNames": true,
"declarationDir": "./types",
+ // "rootDir":"./src"
}
}
\ No newline at end of file
Returns the color as a simulation of the passed in type of color vision deficiency with the deficiency filter's intensity determined by the severity value.
+