├── composer.json ├── readme.MD └── src ├── Color.php └── PaletteGenerator.php /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lukapeharda/tailwindcss-color-palette-generator", 3 | "description": "Generate TailwindCSS color palette from a single color.", 4 | "license": "MIT", 5 | "keywords": ["tailwindcss", "color", "palette", "generator"], 6 | "authors": [ 7 | { 8 | "name": "Luka Peharda", 9 | "email": "luka.peharda@gmail.com" 10 | } 11 | ], 12 | "require": { 13 | "php": ">=7.4" 14 | }, 15 | "autoload": { 16 | "psr-4": { 17 | "LukaPeharda\\TailwindCssColorPaletteGenerator\\": "src/" 18 | } 19 | }, 20 | "minimum-stability": "dev" 21 | } 22 | -------------------------------------------------------------------------------- /readme.MD: -------------------------------------------------------------------------------- 1 | # TailwindCSS Color Palette Generator 2 | 3 | Generates TailwindCSS color palette (ranging from `50` to `900`) from a single base color (which can be given as a hex value, HSL or RGB). 4 | 5 | Uses HSL color model and base color lightness to generate step colors by raising (or lowering) lightness in steps (and considering bound thresholds). 6 | 7 | ## Installation 8 | 9 | Install it via composer by running: 10 | 11 | `composer require lukapeharda/tailwindcss-color-palette-generator` 12 | 13 | ## Usage 14 | 15 | First, import needed namespaces and classes: 16 | 17 | ```php 18 | use LukaPeharda\TailwindCssColorPaletteGenerator\Color; 19 | use LukaPeharda\TailwindCssColorPaletteGenerator\PaletteGenerator; 20 | ``` 21 | 22 | Then create your base color: 23 | 24 | ```php 25 | // from hex 26 | $baseColor = Color::fromHex('#ffff00'); 27 | 28 | // or from RGB 29 | $baseColor = Color::fromRgb(255, 255, 0); 30 | 31 | // or from HSL 32 | $baseColor = Color::fromHsl(60, 100, 50); // or $baseColor = Color::fromHsl(0.6, 1, 0.5); 33 | ``` 34 | 35 | Lastly, use base color to create a color palette: 36 | 37 | ```php 38 | $paletteGenerator = new PaletteGenerator; 39 | $paletteGenerator->setBaseColor($baseColor); 40 | $palette = $paletteGenerator->getPalette(); 41 | ``` 42 | 43 | Generated `$palette` will be an array where keys are TailwindCSS color steps and values `Color` objects: 44 | 45 | ```php 46 | $palette = [ 47 | 50 => Color, 48 | 100 => Color, 49 | 200 => Color, 50 | 300 => Color, 51 | ... 52 | ]; 53 | ``` 54 | 55 | You can then loop over it to generate CSS variables or use it anyway you see fit: 56 | 57 | ```php 58 | foreach ($palette as $key => $color) { 59 | echo '--color-primary-' . $key . ': #' . $color->getHex() . ';'; 60 | } 61 | ``` 62 | 63 | Extend color settings in your `tailwind.config.js` file and add `primary` color pallete: 64 | 65 | ```js 66 | module.exports = { 67 | theme: { 68 | extend: { 69 | colors: { 70 | primary: { 71 | 50: 'var(--color-primary-50, #F5F3FF)', 72 | 100: 'var(--color-primary-100, #EDE9FE)', 73 | 200: 'var(--color-primary-200, #DDD6FE)', 74 | 300: 'var(--color-primary-300, #C4B5FD)', 75 | 400: 'var(--color-primary-400, #A78BFA)', 76 | 500: 'var(--color-primary-500, #8B5CF6)', 77 | 600: 'var(--color-primary-600, #7C3AED)', 78 | 700: 'var(--color-primary-700, #6D28D9)', 79 | 800: 'var(--color-primary-800, #5B21B6)', 80 | 900: 'var(--color-primary-900, #4C1D95)', 81 | } 82 | } 83 | } 84 | } 85 | } 86 | ``` 87 | 88 | Afterwards you can use your color as regular CSS Tailwind class, for example as `text-primary-100` or `bg-primary-300`. 89 | 90 | ## Defaults 91 | 92 | `PaletteGenerator` class has some configurable options set to a sensible defaults. 93 | 94 | ### Base color step value 95 | 96 | By default base color step value is `500`. This means that 5 lighter colors and 4 darker will be generated in a palette. 97 | 98 | You can change it by calling `setBaseValue` method on the `PaletteGenerator` object: 99 | 100 | ```php 101 | $paletteGenerator->setBaseValue(400); 102 | ``` 103 | 104 | By setting the base value to `400` 4 lighter colors and 5 darker will be generated. 105 | 106 | ### Bounds thresholds 107 | 108 | By default the lightest generated color will have a lightness value of 90% and the darkest will have the lightness value of 10%. 109 | 110 | You can change this by calling `setThresholdLightest` and `setThresholdDarkest` methods on `PaletteGenerator` object: 111 | 112 | ```php 113 | $paletteGenerator->setThresholdLightest(80); // or $paletteGenerator->setThresholdLightest(0.8); 114 | $paletteGenerator->setThresholdDarkest(20); // or $paletteGenerator->setThresholdDarkest(0.2); 115 | ``` 116 | 117 | ### Color steps 118 | 119 | By default, TailwindCSS 2.x color steps range is used (from `50` to `900`). 120 | 121 | You can override it by calling `setColorSteps` method on `PaletteGenerator` object by giving it an array with step values: 122 | 123 | ```php 124 | $paletteGenerator->setColorSteps([100, 200, 300, 400, 500, 600, 700]); 125 | ``` 126 | 127 | ## Todos 128 | 129 | Different strategies for generating colors are planned to be developed. 130 | -------------------------------------------------------------------------------- /src/Color.php: -------------------------------------------------------------------------------- 1 | hex = str_replace('#', '', $hex); 35 | 36 | return $color; 37 | } 38 | 39 | /** 40 | * Init object from HSL values. 41 | * 42 | * HSL values can either be a decimal (0.5) number or percentage based (50). 43 | * 44 | * @param float $hue 45 | * @param float $saturation 46 | * @param float $lightness 47 | * 48 | * @return self 49 | */ 50 | public static function fromHsl(float $hue, float $saturation, float $lightness): self 51 | { 52 | $color = new Color; 53 | 54 | // Check if given HSL values are percentage based 55 | if ($hue > 1 || $saturation > 1 || $lightness > 1) { 56 | $color->hsl = [ 57 | self::HUE => $hue / 100, 58 | self::SATURATION => $saturation / 100, 59 | self::LIGHTNESS => $lightness / 100, 60 | ]; 61 | } else { 62 | $color->hsl = [ 63 | self::HUE => $hue, 64 | self::SATURATION => $saturation, 65 | self::LIGHTNESS => $lightness, 66 | ]; 67 | } 68 | 69 | return $color; 70 | } 71 | 72 | /** 73 | * Init object from RGB values. 74 | * 75 | * @param int $red 76 | * @param int $green 77 | * @param int $blue 78 | * 79 | * @return self 80 | */ 81 | public static function fromRgb(int $red, int $green, int $blue): self 82 | { 83 | $color = new Color; 84 | 85 | $color->hex = $color->rgbComponentToHex($red) . $color->rgbComponentToHex($green) . $color->rgbComponentToHex($blue); 86 | 87 | return $color; 88 | } 89 | 90 | /** 91 | * Return color value as hex string. 92 | * 93 | * Does not have "#" prepended. 94 | * 95 | * @return string 96 | */ 97 | public function getHex(): string 98 | { 99 | if ($this->hex === null) { 100 | $this->hex = $this->hslToHex($this->hsl); 101 | } 102 | 103 | return $this->hex; 104 | } 105 | 106 | /** 107 | * Return color value as HSL array. 108 | * 109 | * @return array 110 | */ 111 | public function getHsl(): array 112 | { 113 | if ($this->hsl === null) { 114 | $this->hsl = $this->hexToHsl($this->hex); 115 | } 116 | 117 | return $this->hsl; 118 | } 119 | 120 | /** 121 | * Return color as RGB array. 122 | * 123 | * @return array 124 | */ 125 | public function getRgb(): array 126 | { 127 | return array_map("hexdec", str_split($this->getHex(), 2)); 128 | } 129 | 130 | /** 131 | * Convert hex string to HSL. 132 | * 133 | * @param string $hex 134 | * 135 | * @return array 136 | */ 137 | protected function hexToHsl(string $hex): array 138 | { 139 | if (strpos($hex, '#') === 0) { 140 | $hex = [$hex[1].$hex[2], $hex[3].$hex[4], $hex[5].$hex[6]]; 141 | } else { 142 | $hex = [$hex[0].$hex[1], $hex[2].$hex[3], $hex[4].$hex[5]]; 143 | } 144 | 145 | $rgb = array_map(function($part) { 146 | return hexdec($part) / 255; 147 | }, $hex); 148 | 149 | $max = max($rgb); 150 | $min = min($rgb); 151 | 152 | $l = ($max + $min) / 2; 153 | 154 | if ($max == $min) { 155 | $h = $s = 0; 156 | } else { 157 | $diff = $max - $min; 158 | $s = $l > 0.5 ? $diff / (2 - $max - $min) : $diff / ($max + $min); 159 | 160 | switch($max) { 161 | case $rgb[0]: 162 | $h = ($rgb[1] - $rgb[2]) / $diff + ($rgb[1] < $rgb[2] ? 6 : 0); 163 | break; 164 | case $rgb[1]: 165 | $h = ($rgb[2] - $rgb[0]) / $diff + 2; 166 | break; 167 | case $rgb[2]: 168 | $h = ($rgb[0] - $rgb[1]) / $diff + 4; 169 | break; 170 | } 171 | 172 | $h /= 6; 173 | } 174 | 175 | return [$h, $s, $l]; 176 | } 177 | 178 | /** 179 | * Convert HSL array to hex. 180 | * 181 | * @param array $hsl 182 | * 183 | * @return string 184 | */ 185 | protected function hslToHex(array $hsl): string 186 | { 187 | list($h, $s, $l) = $hsl; 188 | 189 | if ($s == 0) { 190 | $s = 0.000001; 191 | } 192 | 193 | if ($s == 0) { 194 | $r = $g = $b = 1; 195 | } else { 196 | $q = $l < 0.5 ? $l * (1 + $s) : $l + $s - $l * $s; 197 | $p = 2 * $l - $q; 198 | 199 | $r = $this->hueToRgb($p, $q, $h + 1/3); 200 | $g = $this->hueToRgb($p, $q, $h); 201 | $b = $this->hueToRgb($p, $q, $h - 1/3); 202 | } 203 | 204 | return $this->rgbComponentToHex($r) . $this->rgbComponentToHex($g) . $this->rgbComponentToHex($b); 205 | } 206 | 207 | /** 208 | * Convert hue values to RGB. 209 | * 210 | * @param float $p 211 | * @param float $q 212 | * @param float $t 213 | * 214 | * @return float 215 | */ 216 | protected function hueToRgb(float $p, float $q, float $t): float 217 | { 218 | if ($t < 0) $t += 1; 219 | if ($t > 1) $t -= 1; 220 | if ($t < 1/6) return $p + ($q - $p) * 6 * $t; 221 | if ($t < 1/2) return $q; 222 | if ($t < 2/3) return $p + ($q - $p) * (2/3 - $t) * 6; 223 | 224 | return $p; 225 | } 226 | 227 | /** 228 | * Convert RGB's value to hex. 229 | * 230 | * @param float $rgb 231 | * 232 | * @return string 233 | */ 234 | protected function rgbComponentToHex(float $rgb): string 235 | { 236 | return str_pad(dechex((int) ($rgb * 255)), 2, '0', STR_PAD_LEFT); 237 | } 238 | 239 | /** 240 | * Output color's hex code. 241 | * 242 | * @return string 243 | */ 244 | public function __toString(): string 245 | { 246 | return $this->getHex(); 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /src/PaletteGenerator.php: -------------------------------------------------------------------------------- 1 | baseColor = $color; 48 | 49 | return $this; 50 | } 51 | 52 | /** 53 | * Set base color value from TailwindCSS palette steps range. 54 | * 55 | * By default set to 500. 56 | * 57 | * @param int $value 58 | * 59 | * @return self 60 | */ 61 | public function setBaseValue(int $value): self 62 | { 63 | $this->baseValue = $value; 64 | 65 | return $this; 66 | } 67 | 68 | /** 69 | * Set threshold value for lightest color lightness upper bound. 70 | * 71 | * Decimal value of maximal ligthness percentage. 72 | * 73 | * @param float $threshold 74 | * 75 | * @return self 76 | */ 77 | public function setThresholdLightest(float $threshold): self 78 | { 79 | $this->thresholdLightest = $threshold > 1 ? $threshold / 100 : $threshold; 80 | 81 | return $this; 82 | } 83 | 84 | /** 85 | * Set threshold value for darkest color lightness lower bound. 86 | * 87 | * Decimal value of minimal ligthness percentage. 88 | * 89 | * @param float $threshold 90 | * 91 | * @return self 92 | */ 93 | public function setThresholdDarkest(float $threshold): self 94 | { 95 | $this->thresholdDarkest = $threshold > 1 ? $threshold / 100 : $threshold; 96 | 97 | return $this; 98 | } 99 | 100 | public function setColorSteps(array $steps): self 101 | { 102 | $this->colorSteps = $steps; 103 | 104 | return $this; 105 | } 106 | 107 | /** 108 | * Generate palette. 109 | * 110 | * HSL values will be read from the base color. 111 | * 112 | * Lighter and darker colors will be generated by lowering or raisng the 113 | * lightness factor using defined color steps. 114 | * 115 | * @return array 116 | */ 117 | protected function generatePalette(): array 118 | { 119 | $baseColorHsl = $this->baseColor->getHsl(); 120 | 121 | $palette = []; 122 | 123 | $lighterSteps = $this->getLighterSteps(); 124 | 125 | $lighterRangeStep = ($this->thresholdLightest - $baseColorHsl[Color::LIGHTNESS]) / count($lighterSteps); 126 | 127 | foreach (array_reverse($lighterSteps) as $index => $step) { 128 | $palette[$step] = Color::fromHsl( 129 | $baseColorHsl[Color::HUE], 130 | $baseColorHsl[Color::SATURATION], 131 | $baseColorHsl[Color::LIGHTNESS] + ($lighterRangeStep * ($index + 1)), 132 | ); 133 | } 134 | 135 | $palette = array_reverse($palette, true); 136 | 137 | $palette[$this->baseValue] = $this->baseColor; 138 | 139 | $darkerSteps = $this->getDarkerSteps(); 140 | 141 | $darkerRangeStep = ($baseColorHsl[Color::LIGHTNESS] - $this->thresholdDarkest) / count($darkerSteps); 142 | 143 | foreach ($darkerSteps as $index => $step) { 144 | $palette[$step] = Color::fromHsl( 145 | $baseColorHsl[Color::HUE], 146 | $baseColorHsl[Color::SATURATION], 147 | $baseColorHsl[Color::LIGHTNESS] - ($darkerRangeStep * ($index + 1)), 148 | ); 149 | } 150 | 151 | return $palette; 152 | } 153 | 154 | /** 155 | * Return palette as array. 156 | * 157 | * @return array 158 | */ 159 | public function getPalette(): array 160 | { 161 | return $this->generatePalette(); 162 | } 163 | 164 | /** 165 | * Return steps which are higher than base. 166 | * 167 | * @return array 168 | */ 169 | protected function getDarkerSteps(): array 170 | { 171 | return array_values(array_filter($this->colorSteps, function($step) { 172 | return $step > $this->baseValue; 173 | })); 174 | } 175 | 176 | /** 177 | * Return steps which are lower than base. 178 | * 179 | * @return array 180 | */ 181 | protected function getLighterSteps(): array 182 | { 183 | return array_values(array_filter($this->colorSteps, function($step) { 184 | return $step < $this->baseValue; 185 | })); 186 | } 187 | } 188 | --------------------------------------------------------------------------------