├── .gitattributes ├── .gitignore ├── jest.config.js ├── tsconfig.json ├── types └── index.d.ts ├── package.json ├── lib └── index.ts ├── README.md └── tests └── index.spec.ts /.gitattributes: -------------------------------------------------------------------------------- 1 | /* linguist-detectable=false 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules* 2 | dist/ 3 | package-lock.json 4 | pnpm-lock.yaml 5 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | transform: { 3 | '^.+\\.ts$': 'ts-jest', 4 | }, 5 | testMatch: [ 6 | '/tests/**/*.spec.ts', 7 | ], 8 | moduleFileExtensions: [ 'ts', 'js' ], 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ "esnext" ], 5 | "module": "commonjs", 6 | "declaration": true, 7 | "outDir": "dist", 8 | "strict": true, 9 | "moduleResolution": "node", 10 | "esModuleInterop": true, 11 | "removeComments": true, 12 | }, 13 | "include": [ 14 | "lib", 15 | ], 16 | } 17 | -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | export interface ColorPalette { 2 | [key: string]: string | ColorRange 3 | } 4 | 5 | export interface PaletteOptions { 6 | name?: string 7 | ui?: boolean 8 | uiMix?: number 9 | grayscale?: boolean 10 | grayscaleMix?: number 11 | palette?: ColorPalette 12 | colorscale?: Array 13 | } 14 | 15 | export interface ColorRange { 16 | [key: string]: string 17 | } 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ky-is/tailwind-color-palette", 3 | "version": "1.0.0", 4 | "description": "Personalized color palettes for Tailwindcss.", 5 | "author": "Kyle Coburn", 6 | "license": "ISC", 7 | "main": "dist/index.js", 8 | "types": "dist/index.d.ts", 9 | "scripts": { 10 | "build": "rimraf dist && tsc", 11 | "prepare": "rimraf dist && tsc && sed -i '' -e $'s/ /\t/g' $(find dist -type f) && jest", 12 | "test": "jest" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/ky-is/tailwind-color-palette.git" 17 | }, 18 | "bugs": { 19 | "url": "https://github.com/ky-is/tailwind-color-palette/issues" 20 | }, 21 | "homepage": "https://github.com/ky-is/tailwind-color-palette#readme", 22 | "eslintConfig": { 23 | "extends": "@ky-is/eslint-config/typescript" 24 | }, 25 | "eslintIgnore": [ 26 | "dist", 27 | "node_modules*" 28 | ], 29 | "files": [ 30 | "dist", 31 | "types" 32 | ], 33 | "dependencies": { 34 | "chroma-js": "^2.1.0" 35 | }, 36 | "devDependencies": { 37 | "@ky-is/eslint-config": "^1.8.2", 38 | "@types/chroma-js": "^1.4.3", 39 | "@types/jest": "^24.0.25", 40 | "@typescript-eslint/eslint-plugin": "^2.13.0", 41 | "@typescript-eslint/parser": "^2.13.0", 42 | "eslint": "^6.8.0", 43 | "jest": "^24.9.0", 44 | "rimraf": "^3.0.0", 45 | "ts-jest": "^24.2.0", 46 | "tslint": "^5.20.1", 47 | "typescript": "^3.7.4" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/index.ts: -------------------------------------------------------------------------------- 1 | import chroma, { Color } from 'chroma-js' 2 | 3 | import { ColorRange, PaletteOptions } from '../types' 4 | 5 | export = function (color: string, options: PaletteOptions = {}) { 6 | if (!color || typeof color !== 'string') { 7 | throw new Error('Please provide a valid "color" string parameter') 8 | } 9 | const colorChroma = chroma(color) 10 | const defaultColorscale = [ 100, 200, 300, 400, 500, 600, 700, 800, 900 ] 11 | const { name = 'brand', ui = false, uiMix = 0.2, grayscale = false, grayscaleMix = 0.03, palette = {}, colorscale = defaultColorscale } = options 12 | 13 | function mix (baseColor: Color | string, amount: number): string { 14 | return chroma.mix(baseColor, colorChroma, amount, 'lab').hex() 15 | } 16 | 17 | function addToPalette (name: string, value: string | ColorRange) { 18 | if (!palette[name]) { 19 | palette[name] = value 20 | } 21 | } 22 | 23 | function scalePalette (baseColor: Color | string, suffixes: Array, padding: number = 0.1): ColorRange { 24 | const colorscale = chroma.scale([ 'white', baseColor, 'black' ]).padding(padding).colors(suffixes.length) 25 | const colorRange: ColorRange = {} 26 | suffixes.forEach((suffix, index) => colorRange[suffix] = colorscale[index]) 27 | return colorRange 28 | } 29 | 30 | addToPalette(name, scalePalette(colorChroma, colorscale)) 31 | 32 | // UI Colors https://github.com/adevade/color-scheme-generator 33 | if (ui) { 34 | addToPalette('cta', scalePalette(colorChroma.set('hsl.h', '+150'), colorscale)) 35 | addToPalette('info', scalePalette(mix('#3df', uiMix), colorscale)) 36 | addToPalette('warning', scalePalette(mix('#fd0', uiMix), colorscale)) 37 | addToPalette('success', scalePalette(mix('#3e4', uiMix), colorscale)) 38 | addToPalette('danger', scalePalette(mix('#f34', uiMix), colorscale)) 39 | } 40 | 41 | // Grayscale 42 | if (grayscale) { 43 | //addToPalette('white', '#fff') 44 | addToPalette('white', mix('#fff', grayscaleMix)) 45 | addToPalette('black', mix('#000', grayscaleMix)) 46 | addToPalette('transparent', 'transparent') 47 | addToPalette('gray', scalePalette(mix('#adadad', grayscaleMix), colorscale)) 48 | } 49 | return palette 50 | } 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tailwind-color-palette 2 | 3 | Personalized color palettes for Tailwindcss. Generates the nine `100`–`900` classes for your color, plus optional UI and grayscale colors (based on https://github.com/adevade/color-scheme-generator). 4 | 5 | For Tailwind 1.0: 6 | ```bash 7 | npm install --save-dev @ky-is/tailwind-color-palette 8 | ``` 9 | 10 | For Tailwind 0.x, see the [0.x branch](https://github.com/ky-is/tailwind-color-palette/tree/tailwind-0.x). 11 | 12 | ## Configuration 13 | 14 | In `tailwind.config.js`: 15 | 16 | ```js 17 | module.exports = { 18 | theme: { 19 | colors: require('@ky-is/tailwind-color-palette')('#e7a', {grayscale: true, ui: true}), 20 | // ... 21 | ``` 22 | 23 | This generates the following colors, tinted by the provided color: 24 | - Brand: `brand-(100–900)` 25 | - Grayscale: `gray-(100–900)` 26 | - UI: `cta-(100–900)`, `info-(100–900)`, `warning-(100–900)`, `success-(100–900)`, `danger-(100–900)` 27 | 28 | ## Options 29 | 30 | The first parameter is the `color` to generate the palette from. The second is an optional object with: 31 | 32 | - `name` (string, default: `'brand'`): The class name prefix for this color (e.g. `text-brand-light` for the default). 33 | - `ui` (boolean, default: `false`): Whether to generate UI color classes (`cta`, `info`, `warning`, `success`, `danger`), tinted with `color`. 34 | - `grayscale` (boolean, default: `false`): Whether to generate grayscale color classes, tinted with `color`. 35 | - `colorscale` (Array, default: `[100, 200, 300, 400, 500, 600, 700, 800, 900]`): The suffixes for your color names. 36 | 37 | ### Advanced Options 38 | 39 | - `uiMix` (number, default: `0.2`, range: 0–1): The proportion of the `color` parameter to mix into the UI colors. 40 | - `grayscaleMix` (number, default: `0.03`, range: 0–1): The proportion of the `color` parameter to mix into the gray colors. 41 | - `palette` ({[key: string]: string | ColorRange}, default: `{}`): An object of existing color definitions to be appended to. 42 | 43 | ## Examples 44 | 45 | Simple all colors: 46 | ```js 47 | const colors = tailwindColorPalette('#FFC0CB', {grayscale: true, ui: true}) 48 | ``` 49 | 50 | Primary + secondary: 51 | ```js 52 | const primaryColors = tailwindColorPalette('#38C172', {name: 'primary', grayscale: true, ui: true}) 53 | const secondaryColors = tailwindColorPalette('#3490DC', {name: 'secondary'}) 54 | const colors = Object.assign(secondaryColors, primaryColors) 55 | ``` 56 | 57 | No color mixing: 58 | ```js 59 | const colors = tailwindColorPalette('#E3342F', {grayscale: true, grayscaleMix: 0, ui: true, uiMix: 0}) 60 | ``` 61 | -------------------------------------------------------------------------------- /tests/index.spec.ts: -------------------------------------------------------------------------------- 1 | import tailwindColorPalette from '../dist' 2 | import { ColorPalette } from '../types' 3 | 4 | describe('parameters', () => { 5 | it('requires a valid color', () => { 6 | // @ts-ignore 7 | expect(() => tailwindColorPalette()).toThrow('"color"') 8 | // @ts-ignore 9 | expect(() => tailwindColorPalette(1)).toThrow('"color"') 10 | expect(() => tailwindColorPalette('')).toThrow('"color"') 11 | expect(() => tailwindColorPalette('1')).toThrow('unknown format:') 12 | expect(() => tailwindColorPalette('reed')).toThrow('unknown hex') 13 | }) 14 | it('does not require options', () => { 15 | expect(() => tailwindColorPalette('#000')).not.toThrow() 16 | }) 17 | }) 18 | 19 | describe('default', () => { 20 | it('generates an object of one color scale', () => { 21 | const colors = tailwindColorPalette('red') 22 | expect(colors).toHaveProperty('brand') 23 | expect(colors.brand).toHaveProperty('900') 24 | expect(colors).not.toHaveProperty('cta') 25 | expect(colors).not.toHaveProperty('black') 26 | }) 27 | }) 28 | 29 | describe('options', () => { 30 | it('name', () => { 31 | const colors = tailwindColorPalette('red', { name: 'rd' }) 32 | expect(colors).toHaveProperty('rd') 33 | expect(colors).not.toHaveProperty('brand') 34 | }) 35 | it('ui', () => { 36 | const colors = tailwindColorPalette('red', { ui: true }) 37 | expect((colors['info'] as ColorPalette)['500']).toEqual('#a0c1cc') 38 | expect(colors).not.toHaveProperty('black') 39 | }) 40 | it('uiMix', () => { 41 | const colors = tailwindColorPalette('red', { ui: true, uiMix: 0.5 }) 42 | expect((colors['info'] as ColorPalette)['500']).toEqual('#da9383') 43 | }) 44 | it('grayscale', () => { 45 | const colors = tailwindColorPalette('red', { grayscale: true }) 46 | expect((colors['gray'] as ColorPalette)['500']).toEqual('#b2aaa8') 47 | expect(colors).not.toHaveProperty('cta') 48 | }) 49 | it('grayscaleMix', () => { 50 | const colors = tailwindColorPalette('red', { grayscale: true, grayscaleMix: 0.5 }) 51 | expect((colors['gray'] as ColorPalette)['500']).toEqual('#e4775c') 52 | }) 53 | it('palette', () => { 54 | const colors = tailwindColorPalette('red', { palette: { custom: '#123' } }) 55 | expect(colors).toHaveProperty('custom') 56 | }) 57 | it('colorScale', () => { 58 | const colors = tailwindColorPalette('red', { colorscale: ["lighter", "light", "medium", "dark", "darker"] }) 59 | const brandColors = colors['brand'] as ColorPalette 60 | expect(brandColors['lighter']).toEqual('#ffcccc') 61 | expect(brandColors['light']).toEqual('#ff6666') 62 | }) 63 | it('colorScaleNumeric', () => { 64 | const colors = tailwindColorPalette('red', { colorscale: [1,2,3,4,5] }) 65 | const brandColors = colors['brand'] as ColorPalette 66 | expect(brandColors['1']).toEqual('#ffcccc') 67 | expect(brandColors['2']).toEqual('#ff6666') 68 | }) 69 | it('colorScaleUiGray', () => { 70 | const colors = tailwindColorPalette('blue', { colorscale: ["light", "medium", "dark"], grayscale: true, ui: true }) 71 | const brandColors = colors['brand'] as ColorPalette 72 | expect((brandColors as ColorPalette)['light']).toEqual('#ccccff') 73 | expect((colors['gray'] as ColorPalette)['medium']).toEqual('#aca9b0') 74 | expect((colors['danger'] as ColorPalette)['dark']).toEqual('#2f0615') 75 | }) 76 | }) 77 | --------------------------------------------------------------------------------