tailwindcss-palette-generator
Version:
Color palette generation library for TailwindCSS.
519 lines (501 loc) • 15.3 kB
JavaScript
import tailwindcss_plugin from "tailwindcss/plugin";
;// CONCATENATED MODULE: external "tailwindcss/plugin"
;// CONCATENATED MODULE: ./src/utils/error.ts
class PaletteError extends Error {
constructor(message){
const errorMessage = `[PaletteError] ${message}`;
super(errorMessage);
this.name = 'PaletteError';
}
}
/* export default */ const utils_error = (PaletteError);
;// CONCATENATED MODULE: ./src/utils/color.ts
// Color space conversion utilities
const rgbToLab = (r, g, b)=>{
// Normalize RGB values
r = r / 255;
g = g / 255;
b = b / 255;
// Apply gamma correction
r = r > 0.04045 ? ((r + 0.055) / 1.055) ** 2.4 : r / 12.92;
g = g > 0.04045 ? ((g + 0.055) / 1.055) ** 2.4 : g / 12.92;
b = b > 0.04045 ? ((b + 0.055) / 1.055) ** 2.4 : b / 12.92;
// Convert to XYZ
let x = (r * 0.4124564 + g * 0.3575761 + b * 0.1804375) * 100;
let y = (r * 0.2126729 + g * 0.7151522 + b * 0.072175) * 100;
let z = (r * 0.0193339 + g * 0.119192 + b * 0.9503041) * 100;
// Normalize for D65 illuminant
x = x / 95.047;
y = y / 100.0;
z = z / 108.883;
// Convert to LAB
x = x > 0.008856 ? x ** (1 / 3) : 7.787 * x + 16 / 116;
y = y > 0.008856 ? y ** (1 / 3) : 7.787 * y + 16 / 116;
z = z > 0.008856 ? z ** (1 / 3) : 7.787 * z + 16 / 116;
const L = 116 * y - 16;
const a = 500 * (x - y);
const b_lab = 200 * (y - z);
return [
L,
a,
b_lab
];
};
const labToRgb = (L, a, b)=>{
// Convert LAB to XYZ
let y = (L + 16) / 116;
let x = a / 500 + y;
let z = y - b / 200;
const y3 = y ** 3;
const x3 = x ** 3;
const z3 = z ** 3;
y = y3 > 0.008856 ? y3 : (y - 16 / 116) / 7.787;
x = x3 > 0.008856 ? x3 : (x - 16 / 116) / 7.787;
z = z3 > 0.008856 ? z3 : (z - 16 / 116) / 7.787;
// Denormalize for D65
x = x * 95.047;
y = y * 100.0;
z = z * 108.883;
// Convert XYZ to RGB
x = x / 100;
y = y / 100;
z = z / 100;
let r = x * 3.2404542 + y * -1.5371385 + z * -0.4985314;
let g = x * -0.969266 + y * 1.8760108 + z * 0.041556;
let b_rgb = x * 0.0556434 + y * -0.2040259 + z * 1.0572252;
// Apply gamma correction
r = r > 0.0031308 ? 1.055 * r ** (1 / 2.4) - 0.055 : 12.92 * r;
g = g > 0.0031308 ? 1.055 * g ** (1 / 2.4) - 0.055 : 12.92 * g;
b_rgb = b_rgb > 0.0031308 ? 1.055 * b_rgb ** (1 / 2.4) - 0.055 : 12.92 * b_rgb;
// Clamp and convert to 0-255
r = Math.max(0, Math.min(255, Math.round(r * 255)));
g = Math.max(0, Math.min(255, Math.round(g * 255)));
b_rgb = Math.max(0, Math.min(255, Math.round(b_rgb * 255)));
return [
r,
g,
b_rgb
];
};
const parseHexColor = (hex)=>{
hex = hex.replace(/^#/, '');
if (hex.length === 3) {
hex = hex.split('').map((char)=>char + char).join('');
}
const num = parseInt(hex, 16);
if (Number.isNaN(num)) {
throw new utils_error('Invalid hex color');
}
return [
num >> 16 & 255,
num >> 8 & 255,
num & 255
];
};
const rgbToHex = (r, g, b)=>{
return '#' + [
r,
g,
b
].map((x)=>Math.max(0, Math.min(255, Math.round(x)))).map((x)=>x.toString(16).padStart(2, '0')).join('');
};
const darkenColor = (color, amount)=>{
// Parse the input color
const [r, g, b] = parseHexColor(color);
// Convert to LAB
const [L, a, b_lab] = rgbToLab(r, g, b);
// Darken by reducing L value (chroma.js uses Kn = 18)
const darkenedL = L - amount * 18;
// Convert back to RGB
const [newR, newG, newB] = labToRgb(darkenedL, a, b_lab);
// Convert to hex
return rgbToHex(newR, newG, newB);
};
const NAMED_COLORS = {
aliceblue: '#f0f8ff',
antiquewhite: '#faebd7',
aqua: '#00ffff',
aquamarine: '#7fffd4',
azure: '#f0ffff',
beige: '#f5f5dc',
bisque: '#ffe4c4',
black: '#000000',
blanchedalmond: '#ffebcd',
blue: '#0000ff',
blueviolet: '#8a2be2',
brown: '#a52a2a',
burlywood: '#deb887',
cadetblue: '#5f9ea0',
chartreuse: '#7fff00',
chocolate: '#d2691e',
coral: '#ff7f50',
cornflowerblue: '#6495ed',
cornsilk: '#fff8dc',
crimson: '#dc143c',
cyan: '#00ffff',
darkblue: '#00008b',
darkcyan: '#008b8b',
darkgoldenrod: '#b8860b',
darkgray: '#a9a9a9',
darkgreen: '#006400',
darkgrey: '#a9a9a9',
darkkhaki: '#bdb76b',
darkmagenta: '#8b008b',
darkolivegreen: '#556b2f',
darkorange: '#ff8c00',
darkorchid: '#9932cc',
darkred: '#8b0000',
darksalmon: '#e9967a',
darkseagreen: '#8fbc8f',
darkslateblue: '#483d8b',
darkslategray: '#2f4f4f',
darkslategrey: '#2f4f4f',
darkturquoise: '#00ced1',
darkviolet: '#9400d3',
deeppink: '#ff1493',
deepskyblue: '#00bfff',
dimgray: '#696969',
dimgrey: '#696969',
dodgerblue: '#1e90ff',
firebrick: '#b22222',
floralwhite: '#fffaf0',
forestgreen: '#228b22',
fuchsia: '#ff00ff',
gainsboro: '#dcdcdc',
ghostwhite: '#f8f8ff',
gold: '#ffd700',
goldenrod: '#daa520',
gray: '#808080',
green: '#008000',
greenyellow: '#adff2f',
grey: '#808080',
honeydew: '#f0fff0',
hotpink: '#ff69b4',
indianred: '#cd5c5c',
indigo: '#4b0082',
ivory: '#fffff0',
khaki: '#f0e68c',
lavender: '#e6e6fa',
lavenderblush: '#fff0f5',
lawngreen: '#7cfc00',
lemonchiffon: '#fffacd',
lightblue: '#add8e6',
lightcoral: '#f08080',
lightcyan: '#e0ffff',
lightgoldenrodyellow: '#fafad2',
lightgray: '#d3d3d3',
lightgreen: '#90ee90',
lightgrey: '#d3d3d3',
lightpink: '#ffb6c1',
lightsalmon: '#ffa07a',
lightseagreen: '#20b2aa',
lightskyblue: '#87cefa',
lightslategray: '#778899',
lightslategrey: '#778899',
lightsteelblue: '#b0c4de',
lightyellow: '#ffffe0',
lime: '#00ff00',
limegreen: '#32cd32',
linen: '#faf0e6',
magenta: '#ff00ff',
maroon: '#800000',
mediumaquamarine: '#66cdaa',
mediumblue: '#0000cd',
mediumorchid: '#ba55d3',
mediumpurple: '#9370db',
mediumseagreen: '#3cb371',
mediumslateblue: '#7b68ee',
mediumspringgreen: '#00fa9a',
mediumturquoise: '#48d1cc',
mediumvioletred: '#c71585',
midnightblue: '#191970',
mintcream: '#f5fffa',
mistyrose: '#ffe4e1',
moccasin: '#ffe4b5',
navajowhite: '#ffdead',
navy: '#000080',
oldlace: '#fdf5e6',
olive: '#808000',
olivedrab: '#6b8e23',
orange: '#ffa500',
orangered: '#ff4500',
orchid: '#da70d6',
palegoldenrod: '#eee8aa',
palegreen: '#98fb98',
paleturquoise: '#afeeee',
palevioletred: '#db7093',
papayawhip: '#ffefd5',
peachpuff: '#ffdab9',
peru: '#cd853f',
pink: '#ffc0cb',
plum: '#dda0dd',
powderblue: '#b0e0e6',
purple: '#800080',
rebeccapurple: '#663399',
red: '#ff0000',
rosybrown: '#bc8f8f',
royalblue: '#4169e1',
saddlebrown: '#8b4513',
salmon: '#fa8072',
sandybrown: '#f4a460',
seagreen: '#2e8b57',
seashell: '#fff5ee',
sienna: '#a0522d',
silver: '#c0c0c0',
skyblue: '#87ceeb',
slateblue: '#6a5acd',
slategray: '#708090',
slategrey: '#708090',
snow: '#fffafa',
springgreen: '#00ff7f',
steelblue: '#4682b4',
tan: '#d2b48c',
teal: '#008080',
thistle: '#d8bfd8',
tomato: '#ff6347',
turquoise: '#40e0d0',
violet: '#ee82ee',
wheat: '#f5deb3',
white: '#ffffff',
whitesmoke: '#f5f5f5',
yellow: '#ffff00',
yellowgreen: '#9acd32'
};
const hslToRgb = (h, s, l)=>{
s /= 100;
l /= 100;
const k = (n)=>(n + h / 30) % 12;
const a = s * Math.min(l, 1 - l);
const f = (n)=>l - a * Math.max(-1, Math.min(k(n) - 3, 9 - k(n), 1));
return [
255 * f(0),
255 * f(8),
255 * f(4)
];
};
const parseColor = (color)=>{
color = color.toLowerCase().trim();
// Named color
if (NAMED_COLORS[color]) {
return parseHexColor(NAMED_COLORS[color]);
}
// Hex color
if (color.startsWith('#')) {
try {
return parseHexColor(color);
} catch (_e) {
return null;
}
}
// rgb/rgba color
const rgbMatch = color.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*[\d.]+)?\)$/);
if (rgbMatch) {
return [
parseInt(rgbMatch[1], 10),
parseInt(rgbMatch[2], 10),
parseInt(rgbMatch[3], 10)
];
}
// hsl/hsla color
const hslMatch = color.match(/^hsla?\((\d+),\s*([\d.]+)%?,\s*([\d.]+)%?(?:,\s*[\d.]+)?\)$/);
if (hslMatch) {
const h = parseInt(hslMatch[1], 10);
const s = parseFloat(hslMatch[2]);
const l = parseFloat(hslMatch[3]);
return hslToRgb(h, s, l);
}
return null;
};
const getHexColor = (color)=>{
const rgb = parseColor(color);
if (!rgb) {
throw new utils_error(`Invalid color: '${color}'`);
}
return rgbToHex(rgb[0], rgb[1], rgb[2]).toLowerCase();
};
const isValidColor = (color)=>{
return parseColor(color) !== null;
};
;// CONCATENATED MODULE: ./src/utils/index.ts
const initialOptions = {
mainShade: 500,
shades: [
50,
100,
200,
300,
400,
500,
600,
700,
800,
900,
950
]
};
const checkParam = (palette)=>{
const { color, name, shade, shades } = palette;
if (!color || typeof color !== 'string' || !isValidColor(color)) {
throw new utils_error(`Invalid color: '${color}'. Please provide a valid color.`);
}
if (!name || typeof name !== 'string') {
throw new utils_error(`Invalid name: '${name}'. Please provide a valid name.`);
}
if (shade && typeof shade !== 'number') {
throw new utils_error(`Invalid shade value: '${shade}'. Shade must be a number.`);
}
if (shades) {
if (!Array.isArray(shades)) {
throw new utils_error(`Invalid shades: '${shades}'. Shades must be an array.`);
}
if (shades.length < 3) {
throw new utils_error('Shades array must contain at least 3 elements.');
}
if (shade && !shades.includes(shade)) {
throw new utils_error(`Main shade '${shade}' is not included in the shades array.`);
}
} else {
if (shade && !initialOptions.shades.includes(shade)) {
throw new utils_error(`Main shade '${shade}' must be one of: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950.`);
}
}
return true;
};
const getPalettesFromOptions = (options)=>{
const palettes = [];
for (const [key, value] of Object.entries(options)){
const palette = {
name: key,
color: value,
shade: initialOptions.mainShade,
shades: initialOptions.shades
};
palettes.push(palette);
}
return palettes;
};
const convertResultToCSS = (result, prefix)=>{
const colors = {};
for (const [key, color] of Object.entries(result)){
colors[`--${prefix}${key}`] = color.DEFAULT;
for (const [shade, colorValue] of Object.entries(color)){
if (shade === 'DEFAULT') continue;
colors[`--${prefix}${key}-${shade}`] = colorValue;
}
}
return colors;
};
;// CONCATENATED MODULE: ./src/getPalette.ts
const calculateDarkenValue = (shade, mainShade)=>{
return (shade - mainShade) / 100 / 2;
};
const shadeColor = (primaryColor, mainShade, shade)=>{
return darkenColor(primaryColor, calculateDarkenValue(shade, mainShade));
};
const colorResult = (fn, options)=>{
return options.shades.reduce((acc, shade)=>{
acc[String(shade)] = fn(options.primaryColor, options.mainShade, shade);
return acc;
}, {});
};
const generateColorPalette = (options)=>{
try {
const palette = colorResult(shadeColor, options);
palette.DEFAULT = getHexColor(options.primaryColor);
return Object.freeze(palette);
} catch (error) {
console.error('Error generating color palette:', error);
return Object.create(null);
}
};
const getPalette = (params)=>{
const palette = {};
if (!params) {
throw new utils_error('Please provide a valid palette configuration.');
}
const getColorOptions = (colorPalette)=>{
const { color, shade, shades } = colorPalette;
return {
mainShade: shade ?? initialOptions.mainShade,
primaryColor: getHexColor(color),
shades: shades ?? initialOptions.shades
};
};
const addPalette = (name, options)=>{
palette[name] = generateColorPalette(options);
};
if (Array.isArray(params)) {
for (const colorPalette of params){
if (checkParam(colorPalette)) {
const options = getColorOptions(colorPalette);
addPalette(colorPalette.name, options);
}
}
} else if (typeof params === 'object' && params !== null) {
if (checkParam(params)) {
const options = getColorOptions(params);
addPalette(params.name, options);
}
} else if (typeof params === 'string') {
if (!isValidColor(params)) {
throw new utils_error(`Invalid color: '${params}'. Please provide a valid color.`);
}
const options = {
mainShade: initialOptions.mainShade,
primaryColor: getHexColor(params),
shades: initialOptions.shades
};
addPalette('primary', options);
}
return palette;
};
/* export default */ const src_getPalette = ((/* unused pure expression or super */ null && (getPalette)));
;// CONCATENATED MODULE: ./src/index.ts
const pluginFn = tailwindcss_plugin.withOptions((options = {})=>{
return function({ addBase, theme }) {
if (!options) {
throw new utils_error('Please provide options to the plugin.');
}
let colorPrefix = theme('--prefix', 'color-');
colorPrefix = colorPrefix.replace(/['"]/g, '');
const palettes = getPalettesFromOptions(options);
if (palettes.length > 0) {
const result = getPalette(palettes);
const css = convertResultToCSS(result, colorPrefix);
addBase({
':root': css
});
}
};
}, (options = {})=>{
if (!options) {
throw new utils_error('Please provide options to the plugin.');
}
const palettes = getPalettesFromOptions(options);
if (palettes.length > 0) {
const result = getPalette(palettes);
return {
theme: {
extend: {
colors: result
}
}
};
}
return {};
});
// is options function
//! Error: The plugin "tailwindcss-palette-generator" does not accept options
pluginFn.__isOptionsFunction = true;
const paletteGeneratorPlugin = Object.assign(pluginFn, {
__isOptionsFunction: true
});
Object.defineProperty(paletteGeneratorPlugin, '__isOptionsFunction', {
value: true,
writable: false,
configurable: false,
enumerable: false
});
/* export default */ const src = (paletteGeneratorPlugin);
export { src as default };
//# sourceMappingURL=index.js.map