@uinamic/colors
Version:
CSS color token generator centered on HSL lightness (HSL 기반 명도 중심 CSS 색상 토큰 생성기)
160 lines (154 loc) • 4.92 kB
JavaScript
// src/index.js
import fs from "fs";
// src/logic/spectrum.js
function getFullSpectrumFromCenter(centerL, limit = 5) {
const splitRange = centerL <= 22 || centerL >= 78;
const getLimit = getDynamicLimit(splitRange, limit);
const maxL = 100 - getLimit;
const isLowRange = centerL <= 50;
const lowRange = isLowRange ? centerL - getLimit : maxL - centerL;
const highRange = isLowRange ? maxL - centerL : centerL - getLimit;
if (splitRange) {
const lowRange2 = isLowRange ? centerL - getLimit : maxL - centerL;
const highRange2 = lowRange2 * 4;
const result = calculateSpectrum(centerL, lowRange2, highRange2, isLowRange);
return result;
} else {
const result = calculateSpectrum(centerL, lowRange, highRange, isLowRange);
return result;
}
}
function getDynamicLimit(splitRange, defaultLimit = 5) {
if (splitRange) return 3;
return defaultLimit;
}
function calculateSpectrum(centerL, lowRange, highRange, isLowRange) {
let lowBias = Math.floor(lowRange * 0.1);
lowBias += (lowRange - lowBias) % 4;
const lowDelta = lowRange - lowBias;
let highBias = Math.floor(highRange * 0.1);
highBias += (highRange - highBias) % 4;
const highDelta = highRange - highBias;
let lowSteps;
let lowValues;
let highSteps;
let highValues;
if (isLowRange) {
lowSteps = smartLowBias(lowBias).map((w) => lowDelta / 4 + w);
lowValues = accumulateReverse(centerL, lowSteps);
highSteps = smartHighBias(highBias).map((w) => highDelta / 4 + w).reverse();
highValues = accumulate(centerL, highSteps);
} else {
lowSteps = smartLowBias(lowBias).map((w) => lowDelta / 4 + w).reverse();
lowValues = accumulate(centerL, lowSteps);
highSteps = smartHighBias(highBias).map((w) => highDelta / 4 + w);
highValues = accumulateReverse(centerL, highSteps);
}
const spectrum = [.../* @__PURE__ */ new Set([...lowValues, ...highValues])].sort((a, b) => a - b);
return spectrum;
}
function smartLowBias(total) {
const result = [0, 0, 0, 0];
for (let i = 0; i < total; i++) {
const index = 3 - i % 4;
result[index]++;
}
return result;
}
function smartHighBias(n) {
const result = [0, 0, 0, 0];
const weights = [3, 2, 1, 0];
for (let i = 0; i < n; i++) {
const idx = weights[i % 4];
result[idx]++;
}
return result;
}
function accumulate(start, steps) {
return steps.reduce(
(acc, curr) => {
const last = acc[acc.length - 1];
return [...acc, +(last + curr).toFixed(1)];
},
[start]
);
}
function accumulateReverse(end, steps) {
return steps.reduceRight(
(acc, curr) => {
const next = acc[0];
return [+(next - curr).toFixed(1), ...acc];
},
[end]
);
}
// src/logic/writeCss.js
function writeCss(map, spectrumFn, options = {}) {
const { format = "css", prefix = "--color-", limit = 5 } = options;
const baseColors = [` ${prefix}black: hsl(0, 0%, 0%);`, ` ${prefix}white: hsl(0, 0%, 100%);`];
const baseColorsBlock = baseColors.join("\n");
const groupBlocks = [];
const groupFlatJson = {};
for (const [name, [h, s, l]] of Object.entries(map)) {
const ramp = spectrumFn(l, limit);
const group = ramp.map((lightness, i) => {
const level = (i + 1) * 100;
const key = `${name}-${level}`;
const value = `hsl(${h}, ${s}%, ${lightness}%)`;
if (format === "json") {
groupFlatJson[key] = value;
return null;
}
return ` ${prefix}${key}: ${value};`;
}).filter(Boolean);
groupBlocks.push(group.join("\n"));
}
if (format === "json") {
return JSON.stringify(groupFlatJson, null, 2);
}
if (format === "scss") {
return [baseColorsBlock, "", ...groupBlocks].join("\n\n");
}
return `:root {
${baseColorsBlock}
${groupBlocks.join("\n\n")}
}`;
}
// src/colorMap.js
var defaultColorHSLMap = {
red: [0, 100, 50],
blue: [240, 100, 50],
yellow: [60, 100, 50],
orange: [39, 100, 50],
green: [120, 100, 40],
purple: [270, 100, 60],
pink: [340, 100, 88],
teal: [180, 100, 45],
gray: [0, 0, 50],
darkgray: [0, 0, 30],
lightgray: [0, 0, 70],
coral: [16, 100, 65],
mint: [160, 100, 50],
cyan: [190, 100, 60],
violet: [290, 76, 72],
indigo: [225, 100, 45],
amber: [45, 100, 50]
};
// src/index.js
function generateColorTokens(map = defaultColorHSLMap, options = {}) {
const { format = "css", name = "uinamic-color", path: outputPath = "./theme", prefix = "color", limit = 5 } = options;
const resolvedPrefix = format === "scss" ? `$${prefix}-` : `--${prefix}-`;
const content = writeCss(map, getFullSpectrumFromCenter, {
format,
prefix: resolvedPrefix,
limit
});
const ext = format === "json" ? "json" : format === "scss" ? "scss" : "css";
const filePath = `${outputPath}/${name}.${ext}`;
fs.mkdirSync(outputPath, { recursive: true });
fs.writeFileSync(filePath, content, "utf-8");
return content;
}
export {
generateColorTokens
};