├── .babelrc ├── .eslintrc.json ├── .github ├── CONDUCT.md └── CONTRIBUTING.md ├── .gitignore ├── .prettierrc.js ├── .travis.yml ├── LICENSE ├── README.md ├── commitlint.config.js ├── jest.config.js ├── logo.svg ├── package.json ├── rollup.config.ts ├── scripts └── minify.js ├── src ├── cmykToRgb.ts ├── contrastInfo.ts ├── data.ts ├── expandHexShorthand.ts ├── formatCmyk.ts ├── formatHex.ts ├── formatHsl.ts ├── formatHsv.ts ├── formatRgb.ts ├── hexFromName.ts ├── hexToName.ts ├── hexToRgb.ts ├── hslToRgb.ts ├── hsvToRgb.ts ├── index.ts ├── isDark.ts ├── isHexShorthand.ts ├── isValidCmyk.ts ├── isValidHex.ts ├── isValidHsl.ts ├── isValidHsv.ts ├── isValidRgb.ts ├── mix.ts ├── parseCmyk.ts ├── parseHex.ts ├── parseHsl.ts ├── parseHsv.ts ├── parseRgb.ts ├── randomCmyk.ts ├── randomHex.ts ├── randomHsl.ts ├── randomHsv.ts ├── randomRgb.ts ├── relativeLuminance.ts ├── rgbToCmyk.ts ├── rgbToHex.ts ├── rgbToHsl.ts ├── rgbToHsv.ts ├── toCmyk.ts ├── toHex.ts ├── toHsl.ts ├── toHsv.ts ├── toRgb.ts ├── types.ts ├── utils.ts └── whichModel.ts ├── test ├── cmykToRgb.ts ├── contrastInfo.ts ├── expandHexShorthand.ts ├── formatCmyk.ts ├── formatHex.ts ├── formatHsl.ts ├── formatHsv.ts ├── formatRgb.ts ├── hexFromName.ts ├── hexToName.ts ├── hexToRgb.ts ├── hslToRgb.ts ├── hsvToRgb.ts ├── isDark.ts ├── isHexShorthand.ts ├── isValidCmyk.ts ├── isValidHex.ts ├── isValidHsl.ts ├── isValidHsv.ts ├── isValidRgb.ts ├── mix.ts ├── parseCmyk.ts ├── parseHex.ts ├── parseHsl.ts ├── parseHsv.ts ├── parseRgb.ts ├── randomCmyk.ts ├── randomHex.ts ├── randomHsl.ts ├── randomHsv.ts ├── randomRgb.ts ├── rgbToCmyk.ts ├── rgbToHex.ts ├── rgbToHsl.ts ├── rgbToHsv.ts ├── toCmyk.ts ├── toHex.ts ├── toHsl.ts ├── toHsv.ts ├── toRgb.ts └── whichModel.ts ├── tsconfig.json └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["@babel/preset-env", { "modules": false }] 4 | ], 5 | "env": { 6 | "test": { 7 | "presets": ["@babel/preset-env"] 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "parserOptions": { 4 | "project": "./tsconfig.json" 5 | }, 6 | "env": { 7 | "browser": true, 8 | "commonjs": true, 9 | "es6": true, 10 | "jest/globals": true 11 | }, 12 | "extends": [ 13 | "plugin:jest/recommended", 14 | "plugin:@typescript-eslint/recommended", 15 | "plugin:prettier/recommended", 16 | "prettier/@typescript-eslint" 17 | ], 18 | "plugins": ["jest", "prettier", "@typescript-eslint"], 19 | "rules": { 20 | "@typescript-eslint/camelcase": "off", 21 | "@typescript-eslint/no-explicit-any": "off", 22 | "@typescript-eslint/explicit-function-return-type": "off", 23 | "@typescript-eslint/no-use-before-define": "off" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.github/CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at logaretm1@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ### Getting Started 2 | 3 | #### Clone the repo 4 | 5 | `git clone https://github.com/baianat/color-fns.git` 6 | 7 | #### Install dependencies 8 | 9 | ``` 10 | npm install 11 | ``` 12 | 13 | > Make sure you are using npm v5.x 14 | 15 | #### Hack away! 16 | 17 | ### Code Style 18 | 19 | We are using ESlint to enfore the style "Standard" with small modifications, for example we like our semicolons. Make sure to run `npm run lint` to make sure your code follows the style guide. 20 | 21 | ### Testing 22 | 23 | All functions must have a corresponding test file in the `tests` folder of the same name. Tests should have **100%** coverage at all times. We are using `jest` for testing. 24 | 25 | #### To run the tests: 26 | 27 | ```bash 28 | npm run test 29 | 30 | #or 31 | 32 | npm t 33 | ``` 34 | 35 | ### Pull Requests 36 | 37 | You can contribute anything you think is useful to the project, but it is recommended to discuss your ideas by either creating an issue or prefixing your PR with `[WIP]` and a checklist. The maintainers will be more than happy to respond to your contribution. 38 | 39 | Please read the [Code of conduct](CONDUCT.md) to avoid any miscommunications. 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .vscode 4 | coverage 5 | test.html 6 | dist 7 | .rpt2_cache 8 | compiled 9 | .nyc_output 10 | .DS_Store 11 | .idea 12 | .awcache 13 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | // prettier.config.js or .prettierrc.js 2 | module.exports = { 3 | trailingComma: 'none', 4 | printWidth: 120, 5 | tabWidth: 2, 6 | semi: true, 7 | singleQuote: true, 8 | bracketSpacing: true, 9 | arrowParens: 'avoid', 10 | endOfLine: 'lf' 11 | }; 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '9' 4 | install: 5 | - npm install -g codecov 6 | - npm install 7 | after_success: 8 | - npm run test:coverage 9 | - bash <(curl -s https://codecov.io/bash) 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Baianat 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 |

6 | 7 | [![Build Status](https://travis-ci.org/baianat/color-fns.svg?branch=master)](https://travis-ci.org/baianat/color-fns) 8 | [![codecov](https://codecov.io/gh/baianat/color-fns/branch/master/graph/badge.svg)](https://codecov.io/gh/baianat/color-fns) 9 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/a4d7bc3726514688b7186cce0852ebc4)](https://www.codacy.com/app/logaretm1/color-fns?utm_source=github.com&utm_medium=referral&utm_content=baianat/color-fns&utm_campaign=Badge_Grade) 10 | 11 |

12 |
13 | 14 | Modern and Modular JavaScript color utility library. [inspired by date-fns](https://date-fns.org/). 15 | 16 | ## Features 17 | 18 | - Written in TypeScript. 😎 19 | - Lightweight. 💸 20 | - Modular and Tree-shakable.🌳 21 | - Multi-Color Model Support. 🎨 22 | - CSS compatible output. 💨 23 | 24 | ## Installation 25 | 26 | ```bash 27 | # yarn 28 | yarn add color-fns 29 | 30 | # npm 31 | npm i color-fns 32 | ``` 33 | 34 | OR 35 | 36 | ```html 37 | 38 | ``` 39 | 40 | ### Usage 41 | 42 | ```js 43 | // ES2015 (ES6) 44 | import { toRgb } from 'color-fns'; 45 | 46 | console.log(toRgb('#fff')); 47 | 48 | 49 | // CommonJS 50 | const { toRgb } = require('color-fns'); 51 | console.log(toRgb('#fff')); 52 | 53 | // UMD (Script Tag) 54 | console.log(ColorFns.toRgb('#fff')); 55 | ``` 56 | 57 | ### Available Functions 58 | 59 | #### Parsing 60 | 61 | - parseCmyk 62 | - parseHex 63 | - parseHsl 64 | - parseHsv 65 | - parseRgb 66 | 67 | #### Conversion 68 | 69 | - cmykToRgb 70 | - hexToRgb 71 | - hslToRgb 72 | - hsvToRgb 73 | - rgbToCmyk 74 | - rgbToHex 75 | - rgbToHsl 76 | - rgbToHsv 77 | - toCmyk 78 | - toHex 79 | - toHsl 80 | - toHsv 81 | - toRgb 82 | 83 | #### Operations 84 | 85 | - mix 86 | 87 | #### Format and Display 88 | 89 | - expandHexShorthand 90 | - formatCmyk 91 | - formatHex 92 | - formatHsl 93 | - formatHsv 94 | - formatRgb 95 | 96 | #### Validation 97 | 98 | - isValidCmyk 99 | - isValidHex 100 | - isValidHsl 101 | - isValidHsv 102 | - isValidRgb 103 | 104 | #### Querying 105 | 106 | - whichModel 107 | - constrastInfo 108 | - isDark 109 | - hexFromName 110 | - hexToName 111 | 112 | #### Calculations 113 | 114 | - relativeLuminance 115 | 116 | ### Contribution 117 | 118 | Contributions are welcomed, however make sure you read the [contribution guide](.github/CONTRIBUTING.md) and the [code of conduct](.github/CONDUCT.md) before making any pull requests. 119 | 120 | ### License 121 | 122 | MIT 123 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | rules: { 3 | 'body-leading-blank': [1, 'always'], 4 | 'footer-leading-blank': [1, 'always'], 5 | 'header-max-length': [2, 'always', 72], 6 | 'scope-case': [2, 'always', 'lower-case'], 7 | 'subject-case': [2, 'never', ['sentence-case', 'start-case', 'pascal-case', 'upper-case']], 8 | 'subject-empty': [2, 'never'], 9 | 'subject-full-stop': [2, 'never', '.'], 10 | 'type-case': [2, 'always', 'lower-case'], 11 | 'type-empty': [2, 'never'], 12 | 'type-enum': [ 13 | 2, 14 | 'always', 15 | ['build', 'chore', 'ci', 'docs', 'feat', 'fix', 'perf', 'refactor', 'revert', 'style', 'test'] 16 | ] 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | collectCoverageFrom: ['src/**/*.ts', '!src/index.ts', '!src/utils.ts'], 3 | moduleFileExtensions: ['ts', 'js'], 4 | moduleNameMapper: { 5 | '^@/(.*)$': '/src/$1' 6 | }, 7 | testMatch: ['**/test/**/*.ts'], 8 | transform: { 9 | '\\.(ts)$': 'ts-jest', 10 | '^.+\\.jsx?$': 'babel-jest' 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 78 | 79 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "color-fns", 3 | "version": "0.1.1", 4 | "description": "Modern JavaScript color utility library.", 5 | "main": "dist/color-fns.js", 6 | "module": "dist/color-fns.esm.js", 7 | "unpkg": "dist/color-fns.js", 8 | "typings": "dist/types/index.d.ts", 9 | "repository": "https://github.com/baianat/color-fns", 10 | "license": "MIT", 11 | "keywords": [ 12 | "color", 13 | "utility", 14 | "functions" 15 | ], 16 | "scripts": { 17 | "test": "jest", 18 | "test:coverage": "jest --coverage", 19 | "test:watch": "jest --watch", 20 | "lint": "eslint . '**/*.{js,jsx,ts,tsx}' --fix", 21 | "build": "tsc --module commonjs && rollup -c rollup.config.ts", 22 | "watch": "rollup -c rollup.config.ts -w", 23 | "types": "tsc --declaration --declarationDir ./types --emitDeclarationOnly ./src/index.ts" 24 | }, 25 | "maintainers": [ 26 | { 27 | "name": "Abdelrahman3D", 28 | "email": "abdelrahman3d@gmail.com" 29 | } 30 | ], 31 | "files": [ 32 | "dist/**/*.js", 33 | "dist/types/**/*.d.ts" 34 | ], 35 | "devDependencies": { 36 | "@babel/core": "^7.4.5", 37 | "@babel/preset-env": "^7.4.5", 38 | "@commitlint/cli": "^8.0.0", 39 | "@types/jest": "^24.0.15", 40 | "@typescript-eslint/eslint-plugin": "^1.10.2", 41 | "@typescript-eslint/parser": "^1.10.2", 42 | "chalk": "^2.4.2", 43 | "eslint": "^5.16.0", 44 | "eslint-config-prettier": "^5.0.0", 45 | "eslint-plugin-jest": "^22.7.0", 46 | "eslint-plugin-prettier": "^3.1.0", 47 | "filesize": "^4.1.2", 48 | "gzip-size": "^5.1.1", 49 | "husky": "^2.4.1", 50 | "jest": "24.8.0", 51 | "lint-staged": "^8.2.1", 52 | "mkdirp": "^0.5.1", 53 | "prettier": "^1.18.2", 54 | "rollup": "^1.16.1", 55 | "rollup-plugin-babel": "^4.3.2", 56 | "rollup-plugin-commonjs": "^10.0.0", 57 | "rollup-plugin-json": "^4.0.0", 58 | "rollup-plugin-node-resolve": "^5.0.3", 59 | "rollup-plugin-replace": "^2.2.0", 60 | "rollup-plugin-sourcemaps": "^0.4.2", 61 | "rollup-plugin-typescript2": "^0.21.2", 62 | "ts-jest": "^24.0.2", 63 | "tslint-config-prettier": "^1.18.0", 64 | "typescript": "^3.5.2", 65 | "uglify-js": "^3.6.0" 66 | }, 67 | "husky": { 68 | "hooks": { 69 | "pre-commit": "lint-staged", 70 | "commit-msg": "commitlint --edit -E HUSKY_GIT_PARAMS" 71 | } 72 | }, 73 | "lint-staged": { 74 | "{src,test}/**/*.ts": [ 75 | "eslint --fix", 76 | "git add", 77 | "jest --maxWorkers=1 --bail --findRelatedTests" 78 | ] 79 | }, 80 | "eslintIgnore": [ 81 | "locale", 82 | "dist" 83 | ] 84 | } 85 | -------------------------------------------------------------------------------- /rollup.config.ts: -------------------------------------------------------------------------------- 1 | import json from 'rollup-plugin-json'; 2 | import resolve from 'rollup-plugin-node-resolve'; 3 | import sourceMaps from 'rollup-plugin-sourcemaps'; 4 | import typescript from 'rollup-plugin-typescript2'; 5 | import commonjs from 'rollup-plugin-commonjs'; 6 | import replace from 'rollup-plugin-replace'; 7 | 8 | // eslint-disable-next-line 9 | const pkg = require('./package.json'); 10 | 11 | export default { 12 | input: `src/index.ts`, 13 | output: [ 14 | { file: pkg.main, name: 'ColorFns', format: 'umd', sourcemap: true }, 15 | { file: pkg.module, format: 'es', sourcemap: true } 16 | ], 17 | // Indicate here external modules you don't wanna include in your bundle (i.e.: 'lodash') 18 | external: [], 19 | watch: { 20 | include: 'src/**' 21 | }, 22 | plugins: [ 23 | json(), 24 | replace({ 25 | __VERSION__: pkg.version 26 | }), 27 | typescript({ useTsconfigDeclarationDir: true }), 28 | commonjs(), 29 | resolve(), 30 | sourceMaps() 31 | ] 32 | }; 33 | -------------------------------------------------------------------------------- /scripts/minify.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baianat/color-fns/c1db680ae4f10d9498a8855e581d103786dc8962/scripts/minify.js -------------------------------------------------------------------------------- /src/cmykToRgb.ts: -------------------------------------------------------------------------------- 1 | import { parseCmyk } from './parseCmyk'; 2 | import { CmykColor, RgbColor } from './types'; 3 | import { normalizeDecNum } from './utils'; 4 | 5 | export function cmykToRgb(cmyk: CmykColor | string | null): RgbColor | null { 6 | const parsed = typeof cmyk === 'string' ? parseCmyk(cmyk) : cmyk; 7 | if (!parsed) { 8 | return null; 9 | } 10 | 11 | // Convert the CMYK values to the range 0-1 12 | const [cyan, magenta, yellow, key, alpha] = [ 13 | parsed.cyan / 100, 14 | parsed.magenta / 100, 15 | parsed.yellow / 100, 16 | parsed.key / 100, 17 | parsed.alpha 18 | ]; 19 | 20 | // Calculate the rgb values 21 | const red = normalizeDecNum(255 * (1 - cyan) * (1 - key)); 22 | const green = normalizeDecNum(255 * (1 - magenta) * (1 - key)); 23 | const blue = normalizeDecNum(255 * (1 - yellow) * (1 - key)); 24 | 25 | return { 26 | alpha: typeof alpha === 'undefined' ? 1 : alpha, 27 | blue, 28 | green, 29 | red 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /src/contrastInfo.ts: -------------------------------------------------------------------------------- 1 | import { relativeLuminance } from './relativeLuminance'; 2 | import { RgbColor } from './types'; 3 | 4 | interface WCAGInfo { 5 | ratio: number; 6 | isAA: boolean; 7 | isAALarge: boolean; 8 | isAAA: boolean; 9 | isAAALarge: boolean; 10 | isUIAA: boolean; 11 | } 12 | 13 | export function contrastInfo(c1: RgbColor, c2: RgbColor): WCAGInfo { 14 | const L1 = relativeLuminance(c1); 15 | const L2 = relativeLuminance(c2); 16 | 17 | const ratio = (Math.max(L1, L2) + 0.05) / (Math.min(L1, L2) + 0.05); 18 | 19 | return { 20 | ratio: parseFloat(ratio.toFixed(2)), 21 | isAA: ratio >= 4.5, 22 | isAALarge: ratio >= 3, 23 | isAAA: ratio >= 7, 24 | isAAALarge: ratio >= 4.5, 25 | isUIAA: ratio >= 3 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /src/data.ts: -------------------------------------------------------------------------------- 1 | import { ColorNameLookup } from './types'; 2 | 3 | export const CSS_COLORS: ColorNameLookup = { 4 | aliceblue: '#f0f8ff', 5 | antiquewhite: '#faebd7', 6 | aqua: '#00ffff', 7 | aquamarine: '#7fffd4', 8 | azure: '#f0ffff', 9 | beige: '#f5f5dc', 10 | bisque: '#ffe4c4', 11 | black: '#000000', 12 | blanchedalmond: '#ffebcd', 13 | blue: '#0000ff', 14 | blueviolet: '#8a2be2', 15 | brown: '#a52a2a', 16 | burlywood: '#deb887', 17 | cadetblue: '#5f9ea0', 18 | chartreuse: '#7fff00', 19 | chocolate: '#d2691e', 20 | coral: '#ff7f50', 21 | cornflowerblue: '#6495ed', 22 | cornsilk: '#fff8dc', 23 | crimson: '#dc143c', 24 | cyan: '#00ffff', 25 | darkblue: '#00008b', 26 | darkcyan: '#008b8b', 27 | darkgoldenrod: '#b8860b', 28 | darkgray: '#a9a9a9', 29 | darkgreen: '#006400', 30 | darkgrey: '#a9a9a9', 31 | darkkhaki: '#bdb76b', 32 | darkmagenta: '#8b008b', 33 | darkolivegreen: '#556b2f', 34 | darkorange: '#ff8c00', 35 | darkorchid: '#9932cc', 36 | darkred: '#8b0000', 37 | darksalmon: '#e9967a', 38 | darkseagreen: '#8fbc8f', 39 | darkslateblue: '#483d8b', 40 | darkslategray: '#2f4f4f', 41 | darkslategrey: '#2f4f4f', 42 | darkturquoise: '#00ced1', 43 | darkviolet: '#9400d3', 44 | deeppink: '#ff1493', 45 | deepskyblue: '#00bfff', 46 | dimgray: '#696969', 47 | dimgrey: '#696969', 48 | dodgerblue: '#1e90ff', 49 | firebrick: '#b22222', 50 | floralwhite: '#fffaf0', 51 | forestgreen: '#228b22', 52 | fuchsia: '#ff00ff', 53 | gainsboro: '#dcdcdc', 54 | ghostwhite: '#f8f8ff', 55 | goldenrod: '#daa520', 56 | gold: '#ffd700', 57 | gray: '#808080', 58 | green: '#008000', 59 | greenyellow: '#adff2f', 60 | grey: '#808080', 61 | honeydew: '#f0fff0', 62 | hotpink: '#ff69b4', 63 | indianred: '#cd5c5c', 64 | indigo: '#4b0082', 65 | ivory: '#fffff0', 66 | khaki: '#f0e68c', 67 | lavenderblush: '#fff0f5', 68 | lavender: '#e6e6fa', 69 | lawngreen: '#7cfc00', 70 | lemonchiffon: '#fffacd', 71 | lightblue: '#add8e6', 72 | lightcoral: '#f08080', 73 | lightcyan: '#e0ffff', 74 | lightgoldenrodyellow: '#fafad2', 75 | lightgray: '#d3d3d3', 76 | lightgreen: '#90ee90', 77 | lightgrey: '#d3d3d3', 78 | lightpink: '#ffb6c1', 79 | lightsalmon: '#ffa07a', 80 | lightseagreen: '#20b2aa', 81 | lightskyblue: '#87cefa', 82 | lightslategray: '#778899', 83 | lightslategrey: '#778899', 84 | lightsteelblue: '#b0c4de', 85 | lightyellow: '#ffffe0', 86 | lime: '#00ff00', 87 | limegreen: '#32cd32', 88 | linen: '#faf0e6', 89 | magenta: '#ff00ff', 90 | maroon: '#800000', 91 | mediumaquamarine: '#66cdaa', 92 | mediumblue: '#0000cd', 93 | mediumorchid: '#ba55d3', 94 | mediumpurple: '#9370db', 95 | mediumseagreen: '#3cb371', 96 | mediumslateblue: '#7b68ee', 97 | mediumspringgreen: '#00fa9a', 98 | mediumturquoise: '#48d1cc', 99 | mediumvioletred: '#c71585', 100 | midnightblue: '#191970', 101 | mintcream: '#f5fffa', 102 | mistyrose: '#ffe4e1', 103 | moccasin: '#ffe4b5', 104 | navajowhite: '#ffdead', 105 | navy: '#000080', 106 | oldlace: '#fdf5e6', 107 | olive: '#808000', 108 | olivedrab: '#6b8e23', 109 | orange: '#ffa500', 110 | orangered: '#ff4500', 111 | orchid: '#da70d6', 112 | palegoldenrod: '#eee8aa', 113 | palegreen: '#98fb98', 114 | paleturquoise: '#afeeee', 115 | palevioletred: '#db7093', 116 | papayawhip: '#ffefd5', 117 | peachpuff: '#ffdab9', 118 | peru: '#cd853f', 119 | pink: '#ffc0cb', 120 | plum: '#dda0dd', 121 | powderblue: '#b0e0e6', 122 | purple: '#800080', 123 | rebeccapurple: '#663399', 124 | red: '#ff0000', 125 | rosybrown: '#bc8f8f', 126 | royalblue: '#4169e1', 127 | saddlebrown: '#8b4513', 128 | salmon: '#fa8072', 129 | sandybrown: '#f4a460', 130 | seagreen: '#2e8b57', 131 | seashell: '#fff5ee', 132 | sienna: '#a0522d', 133 | silver: '#c0c0c0', 134 | skyblue: '#87ceeb', 135 | slateblue: '#6a5acd', 136 | slategray: '#708090', 137 | slategrey: '#708090', 138 | snow: '#fffafa', 139 | springgreen: '#00ff7f', 140 | steelblue: '#4682b4', 141 | tan: '#d2b48c', 142 | teal: '#008080', 143 | thistle: '#d8bfd8', 144 | tomato: '#ff6347', 145 | turquoise: '#40e0d0', 146 | violet: '#ee82ee', 147 | wheat: '#f5deb3', 148 | white: '#ffffff', 149 | whitesmoke: '#f5f5f5', 150 | yellow: '#ffff00', 151 | yellowgreen: '#9acd32' 152 | }; 153 | -------------------------------------------------------------------------------- /src/expandHexShorthand.ts: -------------------------------------------------------------------------------- 1 | export function expandHexShorthand(hex: string): string { 2 | const regex = /^#([a-f\d])([a-f\d])([a-f\d])([a-f\d])*$/i; 3 | if ((hex.length === 5 || hex.length === 4) && regex.test(hex)) { 4 | hex = hex.replace(regex, (m, r, g, b, a) => { 5 | return `#${r}${r}${g}${g}${b}${b}${a ? `${a}${a}` : ''}`; 6 | }); 7 | } 8 | 9 | return hex; 10 | } 11 | -------------------------------------------------------------------------------- /src/formatCmyk.ts: -------------------------------------------------------------------------------- 1 | import { CmykColor } from './types'; 2 | import { isBetween } from './utils'; 3 | 4 | export function formatCmyk(value: CmykColor | null): string { 5 | if (!value) { 6 | return 'Invalid Color'; 7 | } 8 | 9 | if (typeof value.alpha !== 'undefined' && isBetween(0, 0.999)(value.alpha)) { 10 | return `cmyka(${value.cyan},${value.magenta},${value.yellow},${value.key},${value.alpha})`; 11 | } 12 | 13 | return `cmyk(${value.cyan},${value.magenta},${value.yellow},${value.key})`; 14 | } 15 | -------------------------------------------------------------------------------- /src/formatHex.ts: -------------------------------------------------------------------------------- 1 | import { HexColor } from './types'; 2 | import { hexNumToDec, isBetween } from './utils'; 3 | 4 | export function formatHex(value: HexColor | null): string { 5 | if (!value) { 6 | return 'Invalid Color'; 7 | } 8 | 9 | if (value.alpha !== undefined && isBetween(0, 0.999)(hexNumToDec(value.alpha) / 255)) { 10 | return `#${value.red}${value.green}${value.blue}${value.alpha}`; 11 | } 12 | 13 | return `#${value.red}${value.green}${value.blue}`; 14 | } 15 | -------------------------------------------------------------------------------- /src/formatHsl.ts: -------------------------------------------------------------------------------- 1 | import { HslColor } from './types'; 2 | import { isBetween } from './utils'; 3 | 4 | export function formatHsl(value: HslColor | null): string { 5 | if (!value) { 6 | return 'Invalid Color'; 7 | } 8 | 9 | if (value.alpha !== undefined && isBetween(0, 0.999)(value.alpha)) { 10 | return `hsla(${value.hue},${value.sat}%,${value.lum}%,${value.alpha})`; 11 | } 12 | 13 | return `hsl(${value.hue},${value.sat}%,${value.lum}%)`; 14 | } 15 | -------------------------------------------------------------------------------- /src/formatHsv.ts: -------------------------------------------------------------------------------- 1 | import { HsvColor } from './types'; 2 | import { isBetween } from './utils'; 3 | 4 | export function formatHsv(value: HsvColor | null): string { 5 | if (!value) { 6 | return 'Invalid Color'; 7 | } 8 | 9 | if (value.alpha !== undefined && isBetween(0, 0.999)(value.alpha)) { 10 | return `hsva(${value.hue},${value.sat}%,${value.val}%,${value.alpha})`; 11 | } 12 | 13 | return `hsv(${value.hue},${value.sat}%,${value.val}%)`; 14 | } 15 | -------------------------------------------------------------------------------- /src/formatRgb.ts: -------------------------------------------------------------------------------- 1 | import { RgbColor } from './types'; 2 | import { isBetween } from './utils'; 3 | 4 | export function formatRgb(value: RgbColor | null): string { 5 | if (!value) { 6 | return 'Invalid Color'; 7 | } 8 | 9 | if (typeof value.alpha !== 'undefined' && isBetween(0, 0.999)(value.alpha)) { 10 | return `rgba(${value.red},${value.green},${value.blue},${value.alpha})`; 11 | } 12 | 13 | return `rgb(${value.red},${value.green},${value.blue})`; 14 | } 15 | -------------------------------------------------------------------------------- /src/hexFromName.ts: -------------------------------------------------------------------------------- 1 | import { CSS_COLORS } from './data'; 2 | import { parseHex } from './parseHex'; 3 | import { ColorNameLookup, HexColor } from './types'; 4 | 5 | export function hexFromName(name: string, extendedColors?: ColorNameLookup): HexColor | null { 6 | if (name in CSS_COLORS) { 7 | return parseHex(CSS_COLORS[name]); 8 | } 9 | 10 | if (extendedColors && name in extendedColors) { 11 | return parseHex(extendedColors[name]); 12 | } 13 | 14 | return null; 15 | } 16 | -------------------------------------------------------------------------------- /src/hexToName.ts: -------------------------------------------------------------------------------- 1 | import { hexToRgb } from './hexToRgb'; 2 | import { ColorNameLookup, HexColor, RgbColor } from './types'; 3 | 4 | function dictionaryToArrayObj(obj: ColorNameLookup) { 5 | const keys = Object.keys(obj); 6 | const length = keys.length; 7 | const arr: { name: string; value: RgbColor }[] = []; 8 | for (let i = 0; i < length; i++) { 9 | const rgb = hexToRgb(obj[keys[i]]); 10 | if (!rgb) { 11 | continue; 12 | } 13 | 14 | arr.push({ 15 | name: keys[i], 16 | value: rgb 17 | }); 18 | } 19 | 20 | return arr; 21 | } 22 | 23 | /** 24 | * Finds the nearest name for a given color in a color dictionary. 25 | * The results are compensated for human color perception. 26 | * https://en.wikipedia.org/wiki/Color_difference 27 | * 28 | */ 29 | export function hexToName(hex: HexColor | string, colors: ColorNameLookup): string | null { 30 | const queriedColor = hexToRgb(hex); 31 | if (!queriedColor) { 32 | return null; 33 | } 34 | 35 | let minDistance = Infinity; 36 | let nearestColor!: string; 37 | const colorsArr = dictionaryToArrayObj(colors); 38 | const length = colorsArr.length; 39 | 40 | for (let i = 0; i < length; i++) { 41 | const rgb = colorsArr[i].value; 42 | const distance = 43 | 2 * (rgb.red - queriedColor.red) ** 2 + 44 | 4 * (rgb.green - queriedColor.green) ** 2 + 45 | 3 * (rgb.blue - queriedColor.blue) ** 2; 46 | 47 | if (distance < minDistance) { 48 | nearestColor = colorsArr[i].name; 49 | minDistance = distance; 50 | } 51 | } 52 | 53 | return nearestColor; 54 | } 55 | -------------------------------------------------------------------------------- /src/hexToRgb.ts: -------------------------------------------------------------------------------- 1 | import { parseHex } from './parseHex'; 2 | import { HexColor, RgbColor } from './types'; 3 | import { hexNumToDec } from './utils'; 4 | 5 | export function hexToRgb(hex: HexColor | string | null): RgbColor | null { 6 | const normalizedValue = typeof hex === 'string' ? parseHex(hex) : hex; 7 | if (!normalizedValue) { 8 | return null; 9 | } 10 | 11 | let alpha = 1; 12 | if (typeof normalizedValue.alpha !== 'undefined') { 13 | alpha = Number((hexNumToDec(normalizedValue.alpha) / 255).toFixed(2)); 14 | } 15 | 16 | return { 17 | alpha, 18 | blue: hexNumToDec(normalizedValue.blue), 19 | green: hexNumToDec(normalizedValue.green), 20 | red: hexNumToDec(normalizedValue.red) 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /src/hslToRgb.ts: -------------------------------------------------------------------------------- 1 | import { parseHsl } from './parseHsl'; 2 | import { HslColor, RgbColor } from './types'; 3 | import { normalizeDecNum } from './utils'; 4 | 5 | export function hslToRgb(hsl: HslColor | string | null): RgbColor | null { 6 | const value = typeof hsl === 'string' ? parseHsl(hsl) : hsl; 7 | if (!value) { 8 | return null; 9 | } 10 | 11 | const [hue, sat, lgh, alpha] = [value.hue / 360, value.sat / 100, value.lum / 100, value.alpha]; 12 | let [red, green, blue] = [0, 0, 0]; 13 | 14 | if (sat === 0) { 15 | red = green = blue = normalizeDecNum(lgh * 255); 16 | } 17 | 18 | if (sat !== 0) { 19 | const temp1 = lgh >= 50 ? lgh + sat - lgh * sat : lgh * (1 + sat); 20 | const temp2 = 2 * lgh - temp1; 21 | 22 | const testHue = (test: number): number => { 23 | if (test < 0) { 24 | test += 1; 25 | } 26 | if (test > 1) { 27 | test -= 1; 28 | } 29 | 30 | if (test < 1 / 6) { 31 | return temp2 + (temp1 - temp2) * 6 * test; 32 | } 33 | if (test < 1 / 2) { 34 | return temp1; 35 | } 36 | if (test < 2 / 3) { 37 | return temp2 + (temp1 - temp2) * (2 / 3 - test) * 6; 38 | } 39 | 40 | return temp2; 41 | }; 42 | 43 | red = normalizeDecNum(255 * testHue(hue + 1 / 3)); 44 | green = normalizeDecNum(255 * testHue(hue)); 45 | blue = normalizeDecNum(255 * testHue(hue - 1 / 3)); 46 | } 47 | 48 | return { 49 | alpha, 50 | blue, 51 | green, 52 | red 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /src/hsvToRgb.ts: -------------------------------------------------------------------------------- 1 | import { parseHsv } from './parseHsv'; 2 | import { HsvColor, RgbColor } from './types'; 3 | import { normalizeDecNum } from './utils'; 4 | 5 | export function hsvToRgb(hsv: HsvColor | string | null): RgbColor | null { 6 | const value = typeof hsv === 'string' ? parseHsv(hsv) : hsv; 7 | if (!value) { 8 | return null; 9 | } 10 | 11 | const [hue, sat, val, alpha] = [value.hue / 360, value.sat / 100, value.val / 100, value.alpha]; 12 | let [red, green, blue] = [0, 0, 0]; 13 | 14 | if (sat === 0) { 15 | red = green = blue = normalizeDecNum(val * 255); 16 | } 17 | 18 | if (sat !== 0) { 19 | const c = val * sat; 20 | const x = c * (1 - Math.abs(((hue * 6) % 2) - 1)); 21 | const m = val - c; 22 | 23 | const testHue = (test: number): [number, number, number] => { 24 | if (test < 1 / 6) { 25 | return [c, x, 0]; 26 | } 27 | if (test < 1 / 3) { 28 | return [x, c, 0]; 29 | } 30 | if (test < 1 / 2) { 31 | return [0, c, x]; 32 | } 33 | if (test < 2 / 3) { 34 | return [0, x, c]; 35 | } 36 | if (test < 5 / 6) { 37 | return [x, 0, c]; 38 | } 39 | 40 | return [c, 0, x]; 41 | }; 42 | 43 | const [r, g, b] = testHue(hue); 44 | red = normalizeDecNum(255 * (r + m)); 45 | green = normalizeDecNum(255 * (g + m)); 46 | blue = normalizeDecNum(255 * (b + m)); 47 | } 48 | 49 | return { 50 | alpha, 51 | blue, 52 | green, 53 | red 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { cmykToRgb } from './cmykToRgb'; 2 | export { expandHexShorthand } from './expandHexShorthand'; 3 | export { whichModel } from './whichModel'; 4 | export { hexToRgb } from './hexToRgb'; 5 | export { hslToRgb } from './hslToRgb'; 6 | export { hsvToRgb } from './hsvToRgb'; 7 | export { isHexShorthand } from './isHexShorthand'; 8 | export { mix } from './mix'; 9 | export { parseCmyk } from './parseCmyk'; 10 | export { parseHex } from './parseHex'; 11 | export { parseHsl } from './parseHsl'; 12 | export { parseHsv } from './parseHsv'; 13 | export { parseRgb } from './parseRgb'; 14 | export { rgbToCmyk } from './rgbToCmyk'; 15 | export { rgbToHex } from './rgbToHex'; 16 | export { rgbToHsl } from './rgbToHsl'; 17 | export { rgbToHsv } from './rgbToHsv'; 18 | export { toCmyk } from './toCmyk'; 19 | export { toHex } from './toHex'; 20 | export { toHsl } from './toHsl'; 21 | export { toHsv } from './toHsv'; 22 | export { toRgb } from './toRgb'; 23 | export { randomCmyk } from './randomCmyk'; 24 | export { randomHsl } from './randomHsl'; 25 | export { randomHsv } from './randomHsv'; 26 | export { randomRgb } from './randomRgb'; 27 | export { randomHex } from './randomHex'; 28 | export { isValidRgb } from './isValidRgb'; 29 | export { isValidHex } from './isValidHex'; 30 | export { isValidHsl } from './isValidHsl'; 31 | export { isValidHsv } from './isValidHsv'; 32 | export { isValidCmyk } from './isValidCmyk'; 33 | export { formatRgb } from './formatRgb'; 34 | export { formatHex } from './formatHex'; 35 | export { formatHsl } from './formatHsl'; 36 | export { formatHsv } from './formatHsv'; 37 | export { formatCmyk } from './formatCmyk'; 38 | export { contrastInfo } from './contrastInfo'; 39 | export { relativeLuminance } from './relativeLuminance'; 40 | export { isDark } from './isDark'; 41 | export { hexFromName } from './hexFromName'; 42 | export { hexToName } from './hexToName'; 43 | export { CSS_COLORS } from './data'; 44 | 45 | // Simple composition util 46 | export const compose = (...fns: Function[]) => (x: any) => fns.reduceRight((y, f) => f(y), x); 47 | 48 | export const version = '__VERSION__'; 49 | -------------------------------------------------------------------------------- /src/isDark.ts: -------------------------------------------------------------------------------- 1 | import { relativeLuminance } from './relativeLuminance'; 2 | import { RgbColor } from './types'; 3 | 4 | export function isDark(value: RgbColor): boolean { 5 | const L = relativeLuminance(value); 6 | 7 | return L < 0.179; 8 | } 9 | -------------------------------------------------------------------------------- /src/isHexShorthand.ts: -------------------------------------------------------------------------------- 1 | export function isHexShorthand(hex: string): boolean { 2 | const regex = /^#([a-f\d])([a-f\d])([a-f\d])([a-f\d])*$/i; 3 | if ((hex.length === 5 || hex.length === 4) && regex.test(hex)) { 4 | return true; 5 | } 6 | 7 | return false; 8 | } 9 | -------------------------------------------------------------------------------- /src/isValidCmyk.ts: -------------------------------------------------------------------------------- 1 | import { parseCmyk } from './parseCmyk'; 2 | import { CmykColor } from './types'; 3 | import { isBetween } from './utils'; 4 | 5 | export function isValidCmyk(value: CmykColor | string | null): boolean { 6 | const cmyk = typeof value === 'string' ? parseCmyk(value) : value; 7 | if (!cmyk) { 8 | return false; 9 | } 10 | 11 | const isPercentage = isBetween(0, 100); 12 | 13 | return isPercentage(cmyk.cyan) && isPercentage(cmyk.magenta) && isPercentage(cmyk.yellow) && isPercentage(cmyk.key); 14 | } 15 | -------------------------------------------------------------------------------- /src/isValidHex.ts: -------------------------------------------------------------------------------- 1 | import { parseHex } from './parseHex'; 2 | import { HexColor } from './types'; 3 | 4 | export function isValidHex(value: HexColor | string | null) { 5 | const normalizedVal = typeof value === 'string' ? parseHex(value) : value; 6 | if (!normalizedVal) { 7 | return false; 8 | } 9 | 10 | const { red, green, blue } = normalizedVal; 11 | 12 | return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(`#${red}${green}${blue}`); 13 | } 14 | -------------------------------------------------------------------------------- /src/isValidHsl.ts: -------------------------------------------------------------------------------- 1 | import { parseHsl } from './parseHsl'; 2 | import { HslColor } from './types'; 3 | import { isBetween } from './utils'; 4 | 5 | export function isValidHsl(value: HslColor | string | null): boolean { 6 | const hsl = typeof value === 'string' ? parseHsl(value) : value; 7 | if (!hsl) { 8 | return false; 9 | } 10 | 11 | const isPercentage = isBetween(0, 100); 12 | 13 | return isBetween(0, 360)(hsl.hue) && isPercentage(hsl.lum) && isPercentage(hsl.sat); 14 | } 15 | -------------------------------------------------------------------------------- /src/isValidHsv.ts: -------------------------------------------------------------------------------- 1 | import { parseHsv } from './parseHsv'; 2 | import { HsvColor } from './types'; 3 | import { isBetween } from './utils'; 4 | 5 | export function isValidHsv(value: HsvColor | string | null): boolean { 6 | const normalizedValue = typeof value === 'string' ? parseHsv(value) : value; 7 | if (!normalizedValue) { 8 | return false; 9 | } 10 | 11 | const isPercentage = isBetween(0, 100); 12 | 13 | return ( 14 | isBetween(0, 360)(normalizedValue.hue) && isPercentage(normalizedValue.val) && isPercentage(normalizedValue.sat) 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /src/isValidRgb.ts: -------------------------------------------------------------------------------- 1 | import { parseRgb } from './parseRgb'; 2 | import { RgbColor } from './types'; 3 | import { isBetween } from './utils'; 4 | 5 | export function isValidRgb(value: RgbColor | string | null) { 6 | const rgb = typeof value === 'string' ? parseRgb(value) : value; 7 | // Handle null values. 8 | if (!rgb) { 9 | return false; 10 | } 11 | 12 | const isInAlphaRange = isBetween(0, 1); 13 | if (typeof rgb.alpha !== 'undefined' && !isInAlphaRange(rgb.alpha)) { 14 | return false; 15 | } 16 | 17 | const isInRange = isBetween(0, 255); 18 | const { red, green, blue } = rgb; 19 | 20 | return isInRange(red) && isInRange(green) && isInRange(blue); 21 | } 22 | -------------------------------------------------------------------------------- /src/mix.ts: -------------------------------------------------------------------------------- 1 | import { RgbColor } from './types'; 2 | import { mixValue } from './utils'; 3 | 4 | export function mix(c1: RgbColor, c2: RgbColor, ratio: number): RgbColor { 5 | const red = Math.floor(mixValue(c1.red, c2.red, ratio)); 6 | const green = Math.floor(mixValue(c1.green, c2.green, ratio)); 7 | const blue = Math.floor(mixValue(c1.blue, c2.blue, ratio)); 8 | 9 | const alpha1 = typeof c1.alpha === 'number' ? c1.alpha : 1; 10 | const alpha2 = typeof c2.alpha === 'number' ? c2.alpha : 1; 11 | 12 | const alpha = mixValue(alpha1, alpha2, ratio); 13 | 14 | return { 15 | alpha, 16 | blue, 17 | green, 18 | red 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /src/parseCmyk.ts: -------------------------------------------------------------------------------- 1 | import { CmykColor } from './types'; 2 | 3 | export function parseCmyk(cmyk: string | null): CmykColor | null { 4 | if (!cmyk) { 5 | return null; 6 | } 7 | 8 | // will consider cmyk/cmyka color prefix as a valid input color 9 | // while the output will be a valid web colors 10 | // valid input colors examples 'cmyk(100, 0, 0, 0, 0.5)', 'cmyka(0, 0, 0, 0)' 11 | // the output for the inputted examples 'cmyka(100, 0, 0, 0.5)', 'cmyk(0, 0, 0)' 12 | const match = cmyk.match(/^cmyka?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,*\s*(\d*(?:\.\d+)*)*\)/i); 13 | if (!match || match.length < 5) { 14 | return null; 15 | } 16 | 17 | return { 18 | alpha: typeof match[5] !== 'undefined' ? Number(match[5]) : undefined, 19 | cyan: Number(match[1]), 20 | key: Number(match[4]), 21 | magenta: Number(match[2]), 22 | yellow: Number(match[3]) 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /src/parseHex.ts: -------------------------------------------------------------------------------- 1 | import { expandHexShorthand } from './expandHexShorthand'; 2 | import { HexColor } from './types'; 3 | 4 | export function parseHex(hex: string | null): HexColor | null { 5 | if (!hex) { 6 | return null; 7 | } 8 | 9 | const expanded = expandHexShorthand(hex); 10 | const match = expanded.match(/^#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})*/i); 11 | if (!match || match.length < 4) { 12 | return null; 13 | } 14 | 15 | return { 16 | alpha: match[4], 17 | blue: match[3], 18 | green: match[2], 19 | red: match[1] 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /src/parseHsl.ts: -------------------------------------------------------------------------------- 1 | import { HslColor } from './types'; 2 | 3 | export interface HslParsingOptions { 4 | allowDecimal: boolean; 5 | } 6 | 7 | const defaultOpts: HslParsingOptions = { 8 | allowDecimal: true 9 | }; 10 | 11 | export function parseHsl(value: string | null, options: HslParsingOptions = defaultOpts): HslColor | null { 12 | if (typeof value !== 'string') { 13 | return null; 14 | } 15 | 16 | let regex = /^hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)%\s*,\s*(\d+(?:\.\d+)?)%\s*,*\s*(\d*(?:\.\d+)*)*\)/i; 17 | if (!options.allowDecimal) { 18 | regex = /^hsla?\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,*\s*(\d*(?:\.\d+)*)*\)/i; 19 | } 20 | 21 | // will consider hsl/hsla color prefix as a valid input color 22 | // while the output will be a valid web colors 23 | // valid input colors examples 'hsl(255, 100%, 50%, 0.5)', 'hsla(100, 100%, 50%)' 24 | // the output for the inputted examples 'hsla(255, 100%, 50%, 0.5)', 'hsl(100, 100%, 50%)' 25 | const match = value.match(regex); 26 | if (!match || match.length < 4) { 27 | return null; 28 | } 29 | 30 | return { 31 | alpha: typeof match[4] !== 'undefined' ? Number(match[4]) : undefined, 32 | hue: Number(match[1]), 33 | lum: Number(match[3]), 34 | sat: Number(match[2]) 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /src/parseHsv.ts: -------------------------------------------------------------------------------- 1 | import { HsvColor } from './types'; 2 | 3 | export interface HsvParsingOptions { 4 | allowDecimal: boolean; 5 | } 6 | 7 | const defaultOpts: HsvParsingOptions = { 8 | allowDecimal: true 9 | }; 10 | 11 | export function parseHsv(value: string | null, options: HsvParsingOptions = defaultOpts): HsvColor | null { 12 | if (typeof value !== 'string') { 13 | return null; 14 | } 15 | 16 | let regex = /^hsva?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)%\s*,\s*(\d+(?:\.\d+)?)%\s*,*\s*(\d*(?:\.\d+)*)*\)/i; 17 | if (!options.allowDecimal) { 18 | regex = /^hsva?\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,*\s*(\d*(?:\.\d+)*)*\)/i; 19 | } 20 | 21 | // will consider hsv/hsva color prefix as a valid input color 22 | // while the output will be a valid web colors 23 | // valid input colors examples 'hsv(255, 100%, 50%, 0.5)', 'hsva(100, 100%, 50%)' 24 | // the output for the inputted examples 'hsva(255, 100%, 50%, 0.5)', 'hsv(100, 100%, 50%)' 25 | const match = value.match(regex); 26 | if (!match || match.length < 4) { 27 | return null; 28 | } 29 | 30 | return { 31 | alpha: typeof match[4] !== 'undefined' ? Number(match[4]) : undefined, 32 | hue: Number(match[1]), 33 | sat: Number(match[2]), 34 | val: Number(match[3]) 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /src/parseRgb.ts: -------------------------------------------------------------------------------- 1 | import { RgbColor } from './types'; 2 | 3 | export function parseRgb(value: string | null): RgbColor | null { 4 | if (typeof value !== 'string') { 5 | return null; 6 | } 7 | 8 | // will consider rgb/rgba color prefix as a valid input color 9 | // while the output will be a valid web colors 10 | // valid input colors examples 'rgb(100, 0, 0, 0.5)', 'rgba(0, 0, 0)' 11 | // the output for the inputted examples 'rgba(100, 0, 0, 0.5)', 'rgb(0, 0, 0)' 12 | const match = value.match(/^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,*\s*(\d*(?:\.\d+)*)*\)/i); 13 | if (!match || match.length < 4) { 14 | return null; 15 | } 16 | 17 | return { 18 | alpha: typeof match[4] !== 'undefined' ? Number(match[4]) : undefined, 19 | blue: Number(match[3]), 20 | green: Number(match[2]), 21 | red: Number(match[1]) 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /src/randomCmyk.ts: -------------------------------------------------------------------------------- 1 | import { CmykColor } from './types'; 2 | import { getRandomInt } from './utils'; 3 | 4 | export function randomCmyk(): CmykColor { 5 | return { 6 | cyan: getRandomInt(0, 100), 7 | key: getRandomInt(0, 100), 8 | magenta: getRandomInt(0, 100), 9 | yellow: getRandomInt(0, 100) 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /src/randomHex.ts: -------------------------------------------------------------------------------- 1 | import { HexColor } from './types'; 2 | import { decNumToHex, getRandomInt } from './utils'; 3 | 4 | export function randomHex(): HexColor { 5 | const randHex = () => { 6 | return decNumToHex(getRandomInt(0, 255)); 7 | }; 8 | 9 | return { 10 | blue: randHex(), 11 | green: randHex(), 12 | red: randHex() 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /src/randomHsl.ts: -------------------------------------------------------------------------------- 1 | import { HslColor } from './types'; 2 | import { getRandomInt } from './utils'; 3 | 4 | export function randomHsl(): HslColor { 5 | return { 6 | hue: getRandomInt(0, 360), 7 | lum: getRandomInt(0, 100), 8 | sat: getRandomInt(0, 100) 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /src/randomHsv.ts: -------------------------------------------------------------------------------- 1 | import { HsvColor } from './types'; 2 | import { getRandomInt } from './utils'; 3 | 4 | export function randomHsv(): HsvColor { 5 | return { 6 | hue: getRandomInt(0, 360), 7 | sat: getRandomInt(0, 100), 8 | val: getRandomInt(0, 100) 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /src/randomRgb.ts: -------------------------------------------------------------------------------- 1 | import { RgbColor } from './types'; 2 | import { getRandomInt } from './utils'; 3 | 4 | export function randomRgb(): RgbColor { 5 | return { 6 | blue: getRandomInt(0, 255), 7 | green: getRandomInt(0, 255), 8 | red: getRandomInt(0, 255), 9 | alpha: 1 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /src/relativeLuminance.ts: -------------------------------------------------------------------------------- 1 | import { RgbColor } from './types'; 2 | 3 | /** 4 | * https://www.w3.org/TR/WCAG20/#relativeluminancedef 5 | */ 6 | export function relativeLuminance(value: RgbColor): number { 7 | const sR = value.red / 255; 8 | const sG = value.green / 255; 9 | const sB = value.blue / 255; 10 | 11 | const R = sR <= 0.03928 ? sR / 12.92 : ((sR + 0.055) / 1.055) ** 2.4; 12 | const G = sG <= 0.03928 ? sG / 12.92 : ((sG + 0.055) / 1.055) ** 2.4; 13 | const B = sB <= 0.03928 ? sB / 12.92 : ((sB + 0.055) / 1.055) ** 2.4; 14 | 15 | return 0.2126 * R + 0.7152 * G + 0.0722 * B; 16 | } 17 | -------------------------------------------------------------------------------- /src/rgbToCmyk.ts: -------------------------------------------------------------------------------- 1 | import { parseRgb } from './parseRgb'; 2 | import { CmykColor, RgbColor } from './types'; 3 | 4 | export function rgbToCmyk(rgb: RgbColor | string | null): CmykColor | null { 5 | const value = typeof rgb === 'string' ? parseRgb(rgb) : rgb; 6 | if (!value) { 7 | return null; 8 | } 9 | 10 | // Convert the RGB values to the range 0-1 11 | const [red, green, blue, alpha] = [value.red / 255, value.green / 255, value.blue / 255, value.alpha]; 12 | 13 | // Find the maximum value of R, G and B. 14 | const max = Math.max(red, green, blue); 15 | 16 | // Calculate the cmyk values 17 | let key = 1 - max; 18 | let cyan = (1 - red - key) / (1 - key); 19 | let magenta = (1 - green - key) / (1 - key); 20 | let yellow = (1 - blue - key) / (1 - key); 21 | 22 | // normalize values 23 | cyan = Math.floor(cyan * 100); 24 | magenta = Math.floor(magenta * 100); 25 | yellow = Math.floor(yellow * 100); 26 | key = Math.floor(key * 100); 27 | 28 | return { 29 | alpha: typeof alpha !== 'undefined' ? alpha : 1, 30 | cyan, 31 | key, 32 | magenta, 33 | yellow 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /src/rgbToHex.ts: -------------------------------------------------------------------------------- 1 | import { parseRgb } from './parseRgb'; 2 | import { HexColor, RgbColor } from './types'; 3 | import { decNumToHex } from './utils'; 4 | 5 | export function rgbToHex(rgb: RgbColor | string | null): HexColor | null { 6 | if (typeof rgb === 'string') { 7 | rgb = parseRgb(rgb); 8 | } 9 | 10 | if (!rgb) { 11 | return null; 12 | } 13 | 14 | const [rr, gg, bb, aa] = [ 15 | decNumToHex(rgb.red), 16 | decNumToHex(rgb.green), 17 | decNumToHex(rgb.blue), 18 | rgb.alpha ? decNumToHex(rgb.alpha * 255) : 'ff' 19 | ]; 20 | 21 | return { 22 | alpha: aa, 23 | blue: bb, 24 | green: gg, 25 | red: rr 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /src/rgbToHsl.ts: -------------------------------------------------------------------------------- 1 | import { parseRgb } from './parseRgb'; 2 | import { HslColor, RgbColor } from './types'; 3 | 4 | export function rgbToHsl(rgb: RgbColor | string | null): HslColor | null { 5 | rgb = typeof rgb === 'string' ? parseRgb(rgb) : rgb; 6 | if (!rgb) { 7 | return null; 8 | } 9 | 10 | // Convert the RGB values to the range 0-1 11 | const [red, green, blue, alpha] = [rgb.red / 255, rgb.green / 255, rgb.blue / 255, rgb.alpha]; 12 | let [hue, sat, lum] = [0, 0, 0]; 13 | 14 | // Find the minimum and maximum values of R, G and B. 15 | const min = Math.min(red, green, blue); 16 | const max = Math.max(red, green, blue); 17 | 18 | // Calculate the lightness value 19 | lum = (min + max) / 2; 20 | 21 | // Calculate the saturation. 22 | if (min !== max) { 23 | sat = lum > 0.5 ? (max - min) / (2 - max - min) : (max - min) / (max + min); 24 | } 25 | 26 | // calculate the hue 27 | if (red >= max && min !== max) { 28 | hue = 60 * ((green - blue) / (max - min)); 29 | } 30 | if (green >= max && min !== max) { 31 | hue = 60 * (2.0 + (blue - red) / (max - min)); 32 | } 33 | if (blue >= max && min !== max) { 34 | hue = 60 * (4.0 + (red - green) / (max - min)); 35 | } 36 | 37 | // normalize values 38 | hue = hue < 0 ? Math.floor(hue + 360) : Math.floor(hue); 39 | sat = Math.floor(sat * 100); 40 | lum = Math.floor(lum * 100); 41 | 42 | return { 43 | alpha, 44 | hue, 45 | lum, 46 | sat 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /src/rgbToHsv.ts: -------------------------------------------------------------------------------- 1 | import { parseRgb } from './parseRgb'; 2 | import { HsvColor, RgbColor } from './types'; 3 | 4 | export function rgbToHsv(rgb: RgbColor | string | null): HsvColor | null { 5 | const value = typeof rgb === 'string' ? parseRgb(rgb) : rgb; 6 | if (!value) { 7 | return null; 8 | } 9 | 10 | // Convert the RGB values to the range 0-1 11 | const [red, green, blue, alpha] = [value.red / 255, value.green / 255, value.blue / 255, value.alpha]; 12 | let [hue, sat, val] = [0, 0, 0]; 13 | 14 | // Find the minimum and maximum values of R, G and B. 15 | const min = Math.min(red, green, blue); 16 | const max = Math.max(red, green, blue); 17 | 18 | // Calculate the saturation. 19 | if (max !== 0) { 20 | sat = (max - min) / max; 21 | } 22 | 23 | // calculate the value 24 | val = max; 25 | 26 | // calculate the hue 27 | if (red >= max && min !== max) { 28 | hue = 60 * ((green - blue) / (max - min)); 29 | } 30 | if (green >= max && min !== max) { 31 | hue = 60 * (2.0 + (blue - red) / (max - min)); 32 | } 33 | if (blue >= max && min !== max) { 34 | hue = 60 * (4.0 + (red - green) / (max - min)); 35 | } 36 | 37 | // normalize values 38 | hue = hue < 0 ? Math.floor(hue + 360) : Math.floor(hue); 39 | sat = Math.floor(sat * 100); 40 | val = Math.floor(val * 100); 41 | 42 | return { 43 | alpha: alpha || 1, 44 | hue, 45 | sat, 46 | val 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /src/toCmyk.ts: -------------------------------------------------------------------------------- 1 | import { hexToRgb } from './hexToRgb'; 2 | import { hslToRgb } from './hslToRgb'; 3 | import { hsvToRgb } from './hsvToRgb'; 4 | import { parseCmyk } from './parseCmyk'; 5 | import { rgbToCmyk } from './rgbToCmyk'; 6 | import { CmykColor } from './types'; 7 | import { whichModel } from './whichModel'; 8 | 9 | export function toCmyk(color: string | null): CmykColor | null { 10 | const model = whichModel(color); 11 | 12 | if (model === 'hex') { 13 | return rgbToCmyk(hexToRgb(color)); 14 | } 15 | 16 | if (model === 'hsl') { 17 | return rgbToCmyk(hslToRgb(color)); 18 | } 19 | 20 | if (model === 'hsv') { 21 | return rgbToCmyk(hsvToRgb(color)); 22 | } 23 | 24 | if (model === 'rgb') { 25 | return rgbToCmyk(color); 26 | } 27 | 28 | if (model === 'cmyk' && typeof color === 'string') { 29 | return parseCmyk(color); 30 | } 31 | 32 | return null; 33 | } 34 | -------------------------------------------------------------------------------- /src/toHex.ts: -------------------------------------------------------------------------------- 1 | import { cmykToRgb } from './cmykToRgb'; 2 | import { hslToRgb } from './hslToRgb'; 3 | import { hsvToRgb } from './hsvToRgb'; 4 | import { parseHex } from './parseHex'; 5 | import { rgbToHex } from './rgbToHex'; 6 | import { HexColor } from './types'; 7 | import { whichModel } from './whichModel'; 8 | 9 | export function toHex(color: string | null): HexColor | null { 10 | const model = whichModel(color); 11 | 12 | if (model === 'rgb') { 13 | return rgbToHex(color); 14 | } 15 | 16 | if (model === 'hsl') { 17 | return rgbToHex(hslToRgb(color)); 18 | } 19 | 20 | if (model === 'hsv') { 21 | return rgbToHex(hsvToRgb(color)); 22 | } 23 | 24 | if (model === 'cmyk') { 25 | return rgbToHex(cmykToRgb(color)); 26 | } 27 | 28 | if (model === 'hex' && typeof color === 'string') { 29 | return parseHex(color); 30 | } 31 | 32 | return null; 33 | } 34 | -------------------------------------------------------------------------------- /src/toHsl.ts: -------------------------------------------------------------------------------- 1 | import { cmykToRgb } from './cmykToRgb'; 2 | import { hexToRgb } from './hexToRgb'; 3 | import { hsvToRgb } from './hsvToRgb'; 4 | import { parseHsl } from './parseHsl'; 5 | import { rgbToHsl } from './rgbToHsl'; 6 | import { HslColor } from './types'; 7 | import { whichModel } from './whichModel'; 8 | 9 | export function toHsl(color: string | null): HslColor | null { 10 | const model = whichModel(color); 11 | 12 | if (model === 'hex') { 13 | return rgbToHsl(hexToRgb(color)); 14 | } 15 | 16 | if (model === 'rgb') { 17 | return rgbToHsl(color); 18 | } 19 | 20 | if (model === 'hsv') { 21 | return rgbToHsl(hsvToRgb(color)); 22 | } 23 | 24 | if (model === 'cmyk') { 25 | return rgbToHsl(cmykToRgb(color)); 26 | } 27 | 28 | if (model === 'hsl' && typeof color === 'string') { 29 | return parseHsl(color); 30 | } 31 | 32 | return null; 33 | } 34 | -------------------------------------------------------------------------------- /src/toHsv.ts: -------------------------------------------------------------------------------- 1 | import { cmykToRgb } from './cmykToRgb'; 2 | import { hexToRgb } from './hexToRgb'; 3 | import { hslToRgb } from './hslToRgb'; 4 | import { parseHsv } from './parseHsv'; 5 | import { rgbToHsv } from './rgbToHsv'; 6 | import { HsvColor } from './types'; 7 | import { whichModel } from './whichModel'; 8 | 9 | export function toHsv(color: string | null): HsvColor | null { 10 | const model = whichModel(color); 11 | 12 | if (model === 'hex') { 13 | return rgbToHsv(hexToRgb(color)); 14 | } 15 | 16 | if (model === 'rgb') { 17 | return rgbToHsv(color); 18 | } 19 | 20 | if (model === 'cmyk') { 21 | return rgbToHsv(cmykToRgb(color)); 22 | } 23 | 24 | if (model === 'hsl') { 25 | return rgbToHsv(hslToRgb(color)); 26 | } 27 | 28 | if (model === 'hsv' && typeof color === 'string') { 29 | return parseHsv(color); 30 | } 31 | 32 | return null; 33 | } 34 | -------------------------------------------------------------------------------- /src/toRgb.ts: -------------------------------------------------------------------------------- 1 | import { cmykToRgb } from './cmykToRgb'; 2 | import { hexToRgb } from './hexToRgb'; 3 | import { hslToRgb } from './hslToRgb'; 4 | import { hsvToRgb } from './hsvToRgb'; 5 | import { parseRgb } from './parseRgb'; 6 | import { RgbColor } from './types'; 7 | import { whichModel } from './whichModel'; 8 | 9 | export function toRgb(color: string | null): RgbColor | null { 10 | const model = whichModel(color); 11 | 12 | if (model === 'hex') { 13 | return hexToRgb(color); 14 | } 15 | 16 | if (model === 'hsl') { 17 | return hslToRgb(color); 18 | } 19 | 20 | if (model === 'hsv') { 21 | return hsvToRgb(color); 22 | } 23 | 24 | if (model === 'cmyk') { 25 | return cmykToRgb(color); 26 | } 27 | 28 | if (model === 'rgb' && typeof color === 'string') { 29 | return parseRgb(color); 30 | } 31 | 32 | return null; 33 | } 34 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | export interface CmykColor { 2 | alpha?: number; 3 | cyan: number; 4 | magenta: number; 5 | yellow: number; 6 | key: number; 7 | } 8 | 9 | export interface HexColor { 10 | alpha?: string; 11 | red: string; 12 | green: string; 13 | blue: string; 14 | } 15 | 16 | export interface HslColor { 17 | hue: number; 18 | lum: number; 19 | sat: number; 20 | alpha?: number; 21 | } 22 | 23 | export interface HsvColor { 24 | hue: number; 25 | val: number; 26 | sat: number; 27 | alpha?: number; 28 | } 29 | 30 | export interface RgbColor { 31 | alpha?: number; 32 | red: number; 33 | green: number; 34 | blue: number; 35 | } 36 | 37 | export interface ColorNameLookup { 38 | [k: string]: string; 39 | } 40 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | export function getCartesianCoords(r: number, theta: number): { x: number; y: number } { 2 | return { 3 | x: r * Math.cos(theta * Math.PI * 2), 4 | y: r * Math.sin(theta * Math.PI * 2) 5 | }; 6 | } 7 | 8 | export function isBetween(lb: number, ub: number): (val: number) => boolean { 9 | return value => { 10 | return value >= lb && value <= ub; 11 | }; 12 | } 13 | 14 | export function getRandomInt(min: number, max: number): number { 15 | return Math.floor(Math.random() * (max - min + 1) + min); 16 | } 17 | 18 | export function mixValue(val1: number, val2: number, ratio: number = 0.5): number { 19 | return Number((val1 * (1 - ratio) + val2 * ratio).toFixed(2)); 20 | } 21 | 22 | export function isValidAlpha(alpha: any): boolean { 23 | if (typeof alpha === 'number' && alpha >= 0 && alpha <= 1) { 24 | return true; 25 | } 26 | 27 | return false; 28 | } 29 | 30 | /** 31 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc#Polyfill 32 | */ 33 | export function truncateNum(num: number): number { 34 | num = +num; 35 | if (!isFinite(num)) { 36 | return num; 37 | } 38 | 39 | return num - (num % 1) || (num < 0 ? -0 : num === 0 ? num : 0); 40 | } 41 | 42 | export function decNumToHex(decNum: number): string { 43 | decNum = Math.floor(decNum); 44 | if (isNaN(decNum)) { 45 | return '00'; 46 | } 47 | 48 | return ('0' + decNum.toString(16)).slice(-2); 49 | } 50 | 51 | export function hexNumToDec(hexNum: string): number { 52 | if (isNaN(parseInt(hexNum, 16))) { 53 | return 0; 54 | } 55 | 56 | return parseInt(hexNum, 16); 57 | } 58 | 59 | export function normalizeDecNum(value: number) { 60 | return Math.min(Math.max(truncateNum(value), 0), 255); 61 | } 62 | -------------------------------------------------------------------------------- /src/whichModel.ts: -------------------------------------------------------------------------------- 1 | enum ColorModel { 2 | UNKNOWN = '', 3 | RGB = 'rgb', 4 | HEX = 'hex', 5 | HSL = 'hsl', 6 | HSV = 'hsv', 7 | CMYK = 'cmyk' 8 | } 9 | 10 | export function whichModel(color: string | null): ColorModel { 11 | if (!color) { 12 | return ColorModel.UNKNOWN; 13 | } 14 | 15 | if (color.slice(0, 1) === '#' && (color.length === 4 || color.length === 7)) { 16 | return ColorModel.HEX; 17 | } 18 | 19 | if (color.slice(0, 1) === '#' && (color.length === 6 || color.length === 9)) { 20 | return ColorModel.HEX; 21 | } 22 | 23 | if (color.slice(0, 3).toUpperCase() === 'RGB') { 24 | return ColorModel.RGB; 25 | } 26 | 27 | if (color.slice(0, 3).toUpperCase() === 'HSL') { 28 | return ColorModel.HSL; 29 | } 30 | 31 | if (color.slice(0, 3).toUpperCase() === 'HSV') { 32 | return ColorModel.HSV; 33 | } 34 | 35 | if (color.slice(0, 4).toUpperCase() === 'CMYK') { 36 | return ColorModel.CMYK; 37 | } 38 | 39 | return ColorModel.UNKNOWN; 40 | } 41 | -------------------------------------------------------------------------------- /test/cmykToRgb.ts: -------------------------------------------------------------------------------- 1 | import { cmykToRgb } from '../src/cmykToRgb'; 2 | 3 | const cmyk = { 4 | cyan: 0, 5 | key: 21, 6 | magenta: 22, 7 | yellow: 0 8 | }; 9 | 10 | const rgb = { 11 | alpha: 1, 12 | blue: 201, 13 | green: 157, 14 | red: 201 15 | }; 16 | 17 | test('converts rgb color to cmyk color', () => { 18 | expect(cmykToRgb(cmyk)).toMatchObject(rgb); 19 | expect(cmykToRgb('cmyk(0, 22, 0, 21)')).toMatchObject(rgb); 20 | expect(cmykToRgb(null)).toBeNull(); 21 | }); 22 | -------------------------------------------------------------------------------- /test/contrastInfo.ts: -------------------------------------------------------------------------------- 1 | import { contrastInfo } from '../src/contrastInfo'; 2 | 3 | test('it provides WCAG contrast info between two HSL colors', () => { 4 | const c1 = { red: 20, green: 210, blue: 255 }; 5 | const c2 = { red: 97, green: 26, blue: 149 }; 6 | 7 | expect(contrastInfo(c1, c2)).toMatchObject({ 8 | ratio: 5.59, 9 | isAA: true, 10 | isAALarge: true, 11 | isAAA: false, 12 | isAAALarge: true, 13 | isUIAA: true 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/expandHexShorthand.ts: -------------------------------------------------------------------------------- 1 | import { expandHexShorthand } from '../src/expandHexShorthand'; 2 | 3 | test('expands hex shorthands', () => { 4 | expect(expandHexShorthand('#fff')).toBe('#ffffff'); 5 | expect(expandHexShorthand('#000')).toBe('#000000'); 6 | expect(expandHexShorthand('#fff0')).toBe('#ffffff00'); // alpha 7 | 8 | // invalid shorthands would remain unchanged. 9 | expect(expandHexShorthand('#ffzz')).toBe('#ffzz'); 10 | }); 11 | -------------------------------------------------------------------------------- /test/formatCmyk.ts: -------------------------------------------------------------------------------- 1 | import { formatCmyk } from '../src/formatCmyk'; 2 | 3 | test('formats CMYK colors to readable strings', () => { 4 | expect(formatCmyk(null)).toBe('Invalid Color'); 5 | expect( 6 | formatCmyk({ 7 | cyan: 1, 8 | magenta: 20, 9 | yellow: 5, 10 | key: 10 11 | }) 12 | ).toBe('cmyk(1,20,5,10)'); 13 | 14 | expect( 15 | formatCmyk({ 16 | cyan: 1, 17 | magenta: 20, 18 | yellow: 5, 19 | key: 10, 20 | alpha: 0.7 21 | }) 22 | ).toBe('cmyka(1,20,5,10,0.7)'); 23 | }); 24 | -------------------------------------------------------------------------------- /test/formatHex.ts: -------------------------------------------------------------------------------- 1 | import { formatHex } from '../src/formatHex'; 2 | 3 | test('formats HEX colors to readable strings', () => { 4 | expect(formatHex(null)).toBe('Invalid Color'); 5 | expect( 6 | formatHex({ 7 | red: 'ff', 8 | green: 'ff', 9 | blue: '00' 10 | }) 11 | ).toBe('#ffff00'); 12 | 13 | expect( 14 | formatHex({ 15 | red: 'ff', 16 | green: 'ff', 17 | blue: '00', 18 | alpha: '00' 19 | }) 20 | ).toBe('#ffff0000'); 21 | }); 22 | -------------------------------------------------------------------------------- /test/formatHsl.ts: -------------------------------------------------------------------------------- 1 | import { formatHsl } from '../src/formatHsl'; 2 | 3 | test('formats HSL colors to readable strings', () => { 4 | expect(formatHsl(null)).toBe('Invalid Color'); 5 | expect( 6 | formatHsl({ 7 | hue: 300, 8 | sat: 50, 9 | lum: 20 10 | }) 11 | ).toBe('hsl(300,50%,20%)'); 12 | 13 | expect( 14 | formatHsl({ 15 | hue: 300, 16 | sat: 50, 17 | lum: 20, 18 | alpha: 0.5 19 | }) 20 | ).toBe('hsla(300,50%,20%,0.5)'); 21 | }); 22 | -------------------------------------------------------------------------------- /test/formatHsv.ts: -------------------------------------------------------------------------------- 1 | import { formatHsv } from '../src/formatHsv'; 2 | 3 | test('formats HSV colors to readable strings', () => { 4 | expect(formatHsv(null)).toBe('Invalid Color'); 5 | expect( 6 | formatHsv({ 7 | hue: 300, 8 | sat: 50, 9 | val: 20 10 | }) 11 | ).toBe('hsv(300,50%,20%)'); 12 | 13 | expect( 14 | formatHsv({ 15 | hue: 300, 16 | sat: 50, 17 | val: 20, 18 | alpha: 0.5 19 | }) 20 | ).toBe('hsva(300,50%,20%,0.5)'); 21 | }); 22 | -------------------------------------------------------------------------------- /test/formatRgb.ts: -------------------------------------------------------------------------------- 1 | import { formatRgb } from '../src/formatRgb'; 2 | 3 | test('formats RGB colors to readable strings', () => { 4 | expect(formatRgb(null)).toBe('Invalid Color'); 5 | expect( 6 | formatRgb({ 7 | red: 255, 8 | green: 30, 9 | blue: 100 10 | }) 11 | ).toBe('rgb(255,30,100)'); 12 | 13 | expect( 14 | formatRgb({ 15 | red: 255, 16 | green: 30, 17 | blue: 100, 18 | alpha: 0.5 19 | }) 20 | ).toBe('rgba(255,30,100,0.5)'); 21 | }); 22 | -------------------------------------------------------------------------------- /test/hexFromName.ts: -------------------------------------------------------------------------------- 1 | import { formatHex } from '../src/formatHex'; 2 | import { hexFromName } from '../src/hexFromName'; 3 | 4 | test('Gets color value from color names', () => { 5 | expect(formatHex(hexFromName('red'))).toBe('#ff0000'); 6 | expect(formatHex(hexFromName('blue'))).toBe('#0000ff'); 7 | expect(formatHex(hexFromName('green'))).toBe('#008000'); 8 | expect(formatHex(hexFromName('pink'))).toBe('#ffc0cb'); 9 | expect(formatHex(hexFromName('hotpink'))).toBe('#ff69b4'); 10 | }); 11 | 12 | test('List of colors can be extended', () => { 13 | expect(formatHex(hexFromName('PANTONE 2350 C', { 'PANTONE 2350 C': '#af231c' }))).toBe('#af231c'); 14 | }); 15 | -------------------------------------------------------------------------------- /test/hexToName.ts: -------------------------------------------------------------------------------- 1 | import { CSS_COLORS } from '../src/data'; 2 | import { hexToName } from '../src/hexToName'; 3 | 4 | test('Gets color name from hex value', () => { 5 | expect(hexToName('#ff0000', CSS_COLORS)).toBe('red'); 6 | expect(hexToName('#0000ff', CSS_COLORS)).toBe('blue'); 7 | expect(hexToName('#008000', CSS_COLORS)).toBe('green'); 8 | expect(hexToName('#ffc0cb', CSS_COLORS)).toBe('pink'); 9 | expect(hexToName('#ff69b4', CSS_COLORS)).toBe('hotpink'); 10 | expect(hexToName('#800', CSS_COLORS)).toBe('darkred'); 11 | }); 12 | -------------------------------------------------------------------------------- /test/hexToRgb.ts: -------------------------------------------------------------------------------- 1 | import { hexToRgb } from '../src/hexToRgb'; 2 | 3 | const hex = { 4 | red: 'a1', 5 | green: '16', 6 | blue: '12' 7 | }; 8 | 9 | const rgb = { 10 | red: 161, 11 | green: 22, 12 | blue: 18, 13 | alpha: 1 14 | }; 15 | 16 | test('converts hex color to rgb color', () => { 17 | expect(hexToRgb(hex)).toMatchObject(rgb); 18 | expect(hexToRgb('#a11612')).toMatchObject(rgb); 19 | expect(hexToRgb(null)).toBeNull(); 20 | expect(hexToRgb({ ...hex, alpha: 'ff' })).toMatchObject(rgb); 21 | }); 22 | -------------------------------------------------------------------------------- /test/hslToRgb.ts: -------------------------------------------------------------------------------- 1 | import { hslToRgb } from '../src/hslToRgb'; 2 | 3 | const hsl = { 4 | hue: 2, 5 | sat: 80, 6 | lum: 35, 7 | alpha: 1 8 | }; 9 | 10 | const rgb = { 11 | red: 160, 12 | green: 22, 13 | blue: 17, 14 | alpha: 1 15 | }; 16 | 17 | test('converts hsl color to rgb color', () => { 18 | expect(hslToRgb(hsl)).toMatchObject(rgb); 19 | expect(hslToRgb('hsl(2, 80%, 35%, 1)')).toMatchObject(rgb); 20 | expect(hslToRgb(null)).toBeNull(); 21 | }); 22 | -------------------------------------------------------------------------------- /test/hsvToRgb.ts: -------------------------------------------------------------------------------- 1 | import { hsvToRgb } from '../src/hsvToRgb'; 2 | 3 | const hsv = { 4 | hue: 188, 5 | sat: 94, 6 | val: 90 7 | }; 8 | 9 | const rgb = { 10 | red: 13, // should be 14 11 | green: 200, // should be 201 12 | blue: 229 // should be 230 13 | }; 14 | 15 | test('converts hsv color to rgb color', () => { 16 | expect(hsvToRgb(hsv)).toMatchObject(rgb); 17 | expect(hsvToRgb('hsv(188, 94%, 90%)')).toMatchObject(rgb); 18 | expect(hsvToRgb(null)).toBeNull(); 19 | }); 20 | -------------------------------------------------------------------------------- /test/isDark.ts: -------------------------------------------------------------------------------- 1 | import { isDark } from '../src/isDark'; 2 | 3 | test('Checks if a color is dark', () => { 4 | expect(isDark({ red: 0, blue: 0, green: 0 })).toBe(true); 5 | expect(isDark({ red: 255, blue: 255, green: 255 })).toBe(false); 6 | 7 | expect(isDark({ red: 7, blue: 45, green: 32 })).toBe(true); 8 | }); 9 | -------------------------------------------------------------------------------- /test/isHexShorthand.ts: -------------------------------------------------------------------------------- 1 | import { isHexShorthand } from '../src/isHexShorthand'; 2 | 3 | test('check if color(s) in hex shorthand form', () => { 4 | expect(isHexShorthand('#c97f6a')).toBe(false); 5 | expect(isHexShorthand('#c97f6a7f')).toBe(false); 6 | expect(isHexShorthand('c97f6a')).toBe(false); 7 | expect(isHexShorthand('#c97f6')).toBe(false); 8 | expect(isHexShorthand('c97')).toBe(false); 9 | expect(isHexShorthand('#ffa')).toBe(true); 10 | expect(isHexShorthand('#c97f')).toBe(true); 11 | }); 12 | -------------------------------------------------------------------------------- /test/isValidCmyk.ts: -------------------------------------------------------------------------------- 1 | import { isValidCmyk } from '../src/isValidCmyk'; 2 | 3 | test('validates CMYK color strings', () => { 4 | expect(isValidCmyk('cmyk(40, 40, 40, 40)')).toBe(true); 5 | expect(isValidCmyk('cmyk(100, 0, 100, 0)')).toBe(true); 6 | expect(isValidCmyk(null)).toBe(false); 7 | expect(isValidCmyk('cmyk(-1, 100, 100, 100)')).toBe(false); 8 | expect(isValidCmyk('cmyk(40, 100, 101, 100)')).toBe(false); 9 | }); 10 | -------------------------------------------------------------------------------- /test/isValidHex.ts: -------------------------------------------------------------------------------- 1 | import { isValidHex } from '../src/isValidHex'; 2 | 3 | test('validates Hex color strings', () => { 4 | expect(isValidHex('#c97f6a')).toBe(true); 5 | expect(isValidHex('#ffa')).toBe(true); 6 | expect(isValidHex('c97f6a')).toBe(false); 7 | expect(isValidHex('#c97f6')).toBe(false); 8 | expect(isValidHex('#z3f5fa')).toBe(false); 9 | }); 10 | -------------------------------------------------------------------------------- /test/isValidHsl.ts: -------------------------------------------------------------------------------- 1 | import { isValidHsl } from '../src/isValidHsl'; 2 | 3 | test('validates HSL color strings', () => { 4 | expect(isValidHsl('hsl(360, 0%, 0%)')).toBe(true); 5 | expect(isValidHsl('hsl(0, 100%, 100%)')).toBe(true); 6 | expect(isValidHsl('hsl(361, 0%, 0%)')).toBe(false); 7 | expect(isValidHsl('hsl(360, 101%, 0%)')).toBe(false); 8 | expect(isValidHsl('hsl(360, 101%)')).toBe(false); 9 | }); 10 | -------------------------------------------------------------------------------- /test/isValidHsv.ts: -------------------------------------------------------------------------------- 1 | import { isValidHsv } from '../src/isValidHsv'; 2 | 3 | test('validates HSV color strings', () => { 4 | expect(isValidHsv('hsv(360, 0%, 0%)')).toBe(true); 5 | expect(isValidHsv('hsv(0, 100%, 100%)')).toBe(true); 6 | expect(isValidHsv('hsv(361, 0%, 0%)')).toBe(false); 7 | expect(isValidHsv('hsv(360, 101%, 0%)')).toBe(false); 8 | expect(isValidHsv('hsv(360, 101%)')).toBe(false); 9 | }); 10 | -------------------------------------------------------------------------------- /test/isValidRgb.ts: -------------------------------------------------------------------------------- 1 | import { isValidRgb } from '../src/isValidRgb'; 2 | 3 | test('validates RGB color strings', () => { 4 | expect(isValidRgb('rgb(255, 100, 200)')).toBe(true); 5 | expect(isValidRgb('rgba(255, 100, 200)')).toBe(true); 6 | expect(isValidRgb('rgb(256, 100, 200)')).toBe(false); 7 | expect(isValidRgb('rgb(255, 100, )')).toBe(false); 8 | expect(isValidRgb('rgb(255, 100, 100, 1.1)')).toBe(false); 9 | }); 10 | -------------------------------------------------------------------------------- /test/mix.ts: -------------------------------------------------------------------------------- 1 | import { mix } from '../src/mix'; 2 | 3 | const color1 = { 4 | red: 255, 5 | green: 0, 6 | blue: 127, 7 | alpha: 0 8 | }; 9 | 10 | const color2 = { 11 | red: 0, 12 | green: 255, 13 | blue: 0, 14 | alpha: 1 15 | }; 16 | 17 | const mixedColor = { 18 | red: 127, 19 | green: 127, 20 | blue: 63, 21 | alpha: 0.5 22 | }; 23 | 24 | test('mix two colors object', () => { 25 | expect(mix(color1, color2, 0.5)).toMatchObject(mixedColor); 26 | }); 27 | -------------------------------------------------------------------------------- /test/parseCmyk.ts: -------------------------------------------------------------------------------- 1 | import { parseCmyk } from '../src/parseCmyk'; 2 | 3 | test('parses CMYK strings', () => { 4 | const parsed = parseCmyk('cmyk(31, 20, 12, 19)'); 5 | 6 | expect(parsed).toMatchObject({ 7 | cyan: 31, 8 | magenta: 20, 9 | yellow: 12, 10 | key: 19 11 | }); 12 | 13 | expect(parseCmyk('welp')).toBeNull(); 14 | expect(parseCmyk(null)).toBeNull(); 15 | }); 16 | -------------------------------------------------------------------------------- /test/parseHex.ts: -------------------------------------------------------------------------------- 1 | import { parseHex } from '../src/parseHex'; 2 | 3 | test('parses HEX strings', () => { 4 | const parsed = parseHex('#c97f6a'); 5 | 6 | expect(parsed).toMatchObject({ 7 | red: 'c9', 8 | green: '7f', 9 | blue: '6a' 10 | }); 11 | }); 12 | 13 | test('parses shorthand HEX strings', () => { 14 | const parsed = parseHex('#0f0'); 15 | 16 | expect(parsed).toMatchObject({ 17 | red: '00', 18 | green: 'ff', 19 | blue: '00' 20 | }); 21 | 22 | expect(parseHex('welp')).toBeNull(); 23 | expect(parseHex(null)).toBe(null); 24 | }); 25 | -------------------------------------------------------------------------------- /test/parseHsl.ts: -------------------------------------------------------------------------------- 1 | import { parseHsl } from '../src/parseHsl'; 2 | 3 | test('parses HSL strings', () => { 4 | const parsed = parseHsl('hsl(31.2, 30.2%, 14.15%)'); 5 | 6 | expect(parsed).toMatchObject({ 7 | hue: 31.2, 8 | sat: 30.2, 9 | lum: 14.15 10 | }); 11 | 12 | expect(parseHsl('welp')).toBeNull(); 13 | 14 | // Decimals can be disabled. 15 | const hsl = parseHsl('hsl(31.2, 30.2%, 14.15%)', { allowDecimal: false }); 16 | expect(hsl).toBe(null); 17 | expect(parseHsl(null)).toBe(null); 18 | }); 19 | -------------------------------------------------------------------------------- /test/parseHsv.ts: -------------------------------------------------------------------------------- 1 | import { parseHsv } from '../src/parseHsv'; 2 | 3 | test('parses Hsv strings', () => { 4 | const parsed = parseHsv('hsv(31.2, 30.2%, 14.15%)'); 5 | 6 | expect(parsed).toMatchObject({ 7 | hue: 31.2, 8 | sat: 30.2, 9 | val: 14.15 10 | }); 11 | 12 | expect(parseHsv('welp')).toBeNull(); 13 | // Decimals can be disabled. 14 | const hsv = parseHsv('hsv(31.2, 30.2%, 14.15%)', { allowDecimal: false }); 15 | expect(hsv).toBe(null); 16 | expect(parseHsv(null)).toBe(null); 17 | }); 18 | -------------------------------------------------------------------------------- /test/parseRgb.ts: -------------------------------------------------------------------------------- 1 | import { parseRgb } from '../src/parseRgb'; 2 | 3 | test('parses RGB strings', () => { 4 | const parsed = parseRgb('rgb(31, 234, 12)'); 5 | 6 | expect(parsed).toMatchObject({ 7 | red: 31, 8 | green: 234, 9 | blue: 12 10 | }); 11 | 12 | expect(parseRgb('welp')).toBeNull(); 13 | expect(parseRgb(null)).toBeNull(); 14 | }); 15 | -------------------------------------------------------------------------------- /test/randomCmyk.ts: -------------------------------------------------------------------------------- 1 | import { randomCmyk } from '../src/randomCmyk'; 2 | 3 | test('generates random cmyk color', () => { 4 | expect(randomCmyk()).toMatchObject({ 5 | cyan: expect.any(Number), 6 | magenta: expect.any(Number), 7 | yellow: expect.any(Number), 8 | key: expect.any(Number) 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /test/randomHex.ts: -------------------------------------------------------------------------------- 1 | import { randomHex } from '../src/randomHex'; 2 | 3 | test('generates random hex', () => { 4 | expect(randomHex()).toMatchObject({ 5 | red: expect.stringMatching(/[0-9A-F]{2}/i), 6 | green: expect.stringMatching(/[0-9A-F]{2}/i), 7 | blue: expect.stringMatching(/[0-9A-F]{2}/i) 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /test/randomHsl.ts: -------------------------------------------------------------------------------- 1 | import { randomHsl } from '../src/randomHsl'; 2 | 3 | test('generates random hex', () => { 4 | expect(randomHsl()).toMatchObject({ 5 | hue: expect.any(Number), 6 | sat: expect.any(Number), 7 | lum: expect.any(Number) 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /test/randomHsv.ts: -------------------------------------------------------------------------------- 1 | import { randomHsv } from '../src/randomHsv'; 2 | 3 | test('generates random hex', () => { 4 | expect(randomHsv()).toMatchObject({ 5 | hue: expect.any(Number), 6 | sat: expect.any(Number), 7 | val: expect.any(Number) 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /test/randomRgb.ts: -------------------------------------------------------------------------------- 1 | import { randomRgb } from '../src/randomRgb'; 2 | 3 | test('generates random hex', () => { 4 | expect(randomRgb()).toMatchObject({ 5 | red: expect.any(Number), 6 | green: expect.any(Number), 7 | blue: expect.any(Number) 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /test/rgbToCmyk.ts: -------------------------------------------------------------------------------- 1 | import { rgbToCmyk } from '../src/rgbToCmyk'; 2 | 3 | const rgb = { 4 | blue: 199, 5 | green: 155, 6 | red: 200 7 | }; 8 | 9 | const cmyk = { 10 | cyan: 0, 11 | magenta: 22, // should be 23 12 | yellow: 0, 13 | key: 21, // should be 22 14 | alpha: 1 15 | }; 16 | 17 | test('converts rgb color to cmyk color', () => { 18 | expect(rgbToCmyk(rgb)).toMatchObject(cmyk); 19 | expect(rgbToCmyk('rgb(200, 155, 199)')).toMatchObject(cmyk); 20 | expect(rgbToCmyk(null)).toBe(null); 21 | }); 22 | -------------------------------------------------------------------------------- /test/rgbToHex.ts: -------------------------------------------------------------------------------- 1 | import { rgbToHex } from '../src/rgbToHex'; 2 | 3 | const rgb = { 4 | red: 13, 5 | green: 200, 6 | blue: 230, 7 | alpha: 0.5 8 | }; 9 | 10 | const hex = { 11 | red: '0d', 12 | green: 'c8', 13 | blue: 'e6', 14 | alpha: '7f' 15 | }; 16 | 17 | test('converts rgb color to hex color', () => { 18 | expect(rgbToHex(rgb)).toMatchObject(hex); 19 | expect(rgbToHex('rgb(13, 200, 230, 0.5)')).toMatchObject(hex); 20 | expect(rgbToHex(null)).toBeNull(); 21 | }); 22 | -------------------------------------------------------------------------------- /test/rgbToHsl.ts: -------------------------------------------------------------------------------- 1 | import { rgbToHsl } from '../src/rgbToHsl'; 2 | 3 | const rgb = { 4 | red: 13, 5 | green: 200, 6 | blue: 230 7 | }; 8 | 9 | const hsl = { 10 | hue: 188, 11 | sat: 89, // should be 89.3 12 | lum: 47 // should be 47.6 13 | }; 14 | 15 | test('converts rgb color to hsl color', () => { 16 | expect(rgbToHsl(rgb)).toMatchObject(hsl); 17 | expect(rgbToHsl('rgb(13, 200, 230)')).toMatchObject(hsl); 18 | 19 | // test edge case when green is max 20 | expect(rgbToHsl({ ...rgb, green: 255 })).toMatchObject({ 21 | hue: 173, 22 | sat: 100, 23 | lum: 52 24 | }); 25 | 26 | expect(rgbToHsl(null)).toBeNull(); 27 | }); 28 | -------------------------------------------------------------------------------- /test/rgbToHsv.ts: -------------------------------------------------------------------------------- 1 | import { rgbToHsv } from '../src/rgbToHsv'; 2 | 3 | const rgb = { 4 | red: 13, 5 | green: 200, 6 | blue: 230 7 | }; 8 | 9 | const hsv = { 10 | hue: 188, 11 | sat: 94, 12 | val: 90 13 | }; 14 | 15 | test('converts rgb color to hsv color', () => { 16 | expect(rgbToHsv(rgb)).toMatchObject(hsv); 17 | expect(rgbToHsv('rgb(13, 200, 230)')).toMatchObject(hsv); 18 | 19 | // test edge case when green is max 20 | expect(rgbToHsv({ ...rgb, green: 255 })).toMatchObject({ 21 | hue: 173, 22 | sat: 94, // should be 94.9 23 | val: 100 24 | }); 25 | 26 | expect(rgbToHsv(null)).toBeNull(); 27 | }); 28 | -------------------------------------------------------------------------------- /test/toCmyk.ts: -------------------------------------------------------------------------------- 1 | import { toCmyk } from '../src/toCmyk'; 2 | 3 | test('converts any of the supported color models to cmyk', () => { 4 | const purplish = { 5 | cyan: 0, 6 | magenta: 58, 7 | yellow: 15, 8 | key: 40 9 | }; 10 | 11 | expect(toCmyk('rgb(152, 63, 128)')).toMatchObject(purplish); 12 | expect(toCmyk('hsl(316, 41%, 42%)')).toMatchObject(purplish); 13 | expect(toCmyk('hsv(315, 58%, 60%)')).toMatchObject(purplish); 14 | expect(toCmyk('#983F80')).toMatchObject(purplish); 15 | expect(toCmyk('cmyk(0, 58, 15, 40)')).toMatchObject(purplish); 16 | expect(toCmyk('wat')).toBe(null); 17 | }); 18 | -------------------------------------------------------------------------------- /test/toHex.ts: -------------------------------------------------------------------------------- 1 | import { toHex } from '../src/toHex'; 2 | 3 | test('converts any of the supported color models to HEX', () => { 4 | const neonGreen = { 5 | blue: '14', 6 | red: '39', 7 | green: 'ff' 8 | }; 9 | 10 | expect(toHex('rgb(57, 255, 20)')).toMatchObject(neonGreen); 11 | // color range strikes again. 12 | expect(toHex('hsl(109.88, 100%, 50%)')).toMatchObject({ 13 | red: '2b', 14 | blue: '00', 15 | green: 'ff' 16 | }); 17 | expect(toHex('hsv(110.55, 92%, 100%)')).toMatchObject(neonGreen); 18 | expect(toHex('#39ff14')).toMatchObject(neonGreen); 19 | // color range strikes again. (close enough!) 20 | expect(toHex('cmyk(78, 0, 92, 0)')).toMatchObject({ 21 | red: '38', // almost 39 22 | blue: '14', 23 | green: 'ff' 24 | }); 25 | expect(toHex('wat')).toBe(null); 26 | }); 27 | -------------------------------------------------------------------------------- /test/toHsl.ts: -------------------------------------------------------------------------------- 1 | import { toHsl } from '../src/toHsl'; 2 | 3 | test('converts any of the supported color models to HSL', () => { 4 | // PANTONE 7406 C 5 | const yellowish = { 6 | hue: 48, 7 | sat: 100, 8 | lum: 47 9 | }; 10 | 11 | expect(toHsl('rgb(241, 196, 0)')).toMatchObject(yellowish); 12 | expect(toHsl('hsl(48, 100%, 47%)')).toMatchObject(yellowish); 13 | expect(toHsl('hsv(48.8, 100%, 94.51%)')).toMatchObject(yellowish); 14 | expect(toHsl('#f1c400')).toMatchObject(yellowish); 15 | expect(toHsl('cmyk(0, 19, 100, 5)')).toMatchObject(yellowish); 16 | expect(toHsl('wat')).toBe(null); 17 | }); 18 | -------------------------------------------------------------------------------- /test/toHsv.ts: -------------------------------------------------------------------------------- 1 | import { toHsv } from '../src/toHsv'; 2 | 3 | test('converts any of the supported color models to HSV', () => { 4 | // PANTONE 7406 C 5 | const yellowish = { 6 | hue: 48, 7 | sat: 100, 8 | val: 94 9 | }; 10 | 11 | expect(toHsv('rgb(241, 196, 0)')).toMatchObject(yellowish); 12 | expect(toHsv('hsl(48.8, 100%, 47.25%)')).toMatchObject(yellowish); 13 | expect(toHsv('hsv(48, 100%, 94%)')).toMatchObject(yellowish); 14 | expect(toHsv('#f1c400')).toMatchObject(yellowish); 15 | expect(toHsv('cmyk(0, 19, 100, 5)')).toMatchObject(yellowish); 16 | expect(toHsv('wat')).toBe(null); 17 | }); 18 | -------------------------------------------------------------------------------- /test/toRgb.ts: -------------------------------------------------------------------------------- 1 | import { toRgb } from '../src/toRgb'; 2 | 3 | test('converts any of the supported color models to RGB', () => { 4 | const blueish = { 5 | red: 33, 6 | green: 89, 7 | blue: 152 8 | }; 9 | 10 | expect(toRgb('rgb(33, 89, 152)')).toMatchObject(blueish); 11 | expect(toRgb('hsl(211.8, 64.32%, 36.27%)')).toMatchObject({ 12 | red: 32, 13 | green: 88, 14 | blue: 151 15 | }); 16 | expect(toRgb('hsv(211.8, 78.29%, 59.61%)')).toMatchObject({ 17 | ...blueish, 18 | green: 88 19 | }); 20 | expect(toRgb('#215998')).toMatchObject(blueish); 21 | expect(toRgb('cmyk(78, 41, 0, 40)')).toMatchObject({ 22 | ...blueish, 23 | blue: 153, 24 | green: 90 25 | }); 26 | expect(toRgb('wat')).toBe(null); 27 | }); 28 | -------------------------------------------------------------------------------- /test/whichModel.ts: -------------------------------------------------------------------------------- 1 | import { whichModel } from '../src/whichModel'; 2 | 3 | // const hex = { 4 | // blue: '12', 5 | // green: '16', 6 | // model: 'hex', 7 | // red: 'a1' 8 | // }; 9 | 10 | // const hsl = { 11 | // hue: 200, 12 | // lum: 50, 13 | // sat: 100 14 | // }; 15 | 16 | // const hsv = { 17 | // hue: 200, 18 | // sat: 100, 19 | // val: 50 20 | // }; 21 | 22 | // const rgb = { 23 | // blue: 230, 24 | // green: 200, 25 | // red: 13 26 | // }; 27 | 28 | // const cmyk = { 29 | // cyan: 65, 30 | // key: 56, 31 | // magenta: 100, 32 | // yellow: 12 33 | // }; 34 | 35 | test('get color model', () => { 36 | // expect(whichModel(hex)).toMatch(/hex/); 37 | // expect(whichModel(hsl)).toMatch(/hsl/); 38 | // expect(whichModel(hsv)).toMatch(/hsv/); 39 | // expect(whichModel(cmyk)).toMatch(/cmyk/); 40 | // expect(whichModel(rgb)).toMatch(/rgb/); 41 | expect(whichModel('#a11612')).toMatch(/hex/); 42 | expect(whichModel('#a11612ff')).toMatch(/hex/); 43 | expect(whichModel('hsl(200, 100%, 50%)')).toMatch(/hsl/); 44 | expect(whichModel('hsla(200, 100%, 50%, 1)')).toMatch(/hsl/); 45 | expect(whichModel('hsv(200, 100%, 50%)')).toMatch(/hsv/); 46 | expect(whichModel('hsva(200, 100%, 50%, 1)')).toMatch(/hsv/); 47 | expect(whichModel('cmyk(13, 100, 64)')).toMatch(/cmyk/); 48 | expect(whichModel('cmyka(13, 100, 32, 1)')).toMatch(/cmyk/); 49 | expect(whichModel('rgb(13, 200, 230)')).toMatch(/rgb/); 50 | expect(whichModel('rgba(13, 200, 230, 1)')).toMatch(/rgb/); 51 | expect(whichModel('')).toBe(''); 52 | }); 53 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "target": "es5", 5 | "module":"es2015", 6 | "lib": ["es2015", "es2016", "es2017", "dom"], 7 | "strict": true, 8 | "sourceMap": true, 9 | "declaration": true, 10 | "allowSyntheticDefaultImports": true, 11 | "experimentalDecorators": true, 12 | "emitDecoratorMetadata": true, 13 | "declarationDir": "dist/types", 14 | "outDir": "dist/lib", 15 | "typeRoots": [ 16 | "node_modules/@types" 17 | ] 18 | }, 19 | "include": [ 20 | "src" 21 | ] 22 | } --------------------------------------------------------------------------------