Skip to content

Instantly share code, notes, and snippets.

@necm1
Created January 5, 2025 23:17
Show Gist options
  • Save necm1/e5be3832c0348642d10bfde9bf969276 to your computer and use it in GitHub Desktop.
Save necm1/e5be3832c0348642d10bfde9bf969276 to your computer and use it in GitHub Desktop.
Dynamic Color Mapping from Root Variables (Tailwind / shadcn)
const { join } = require('path');
const postcss = require('postcss');
const sass = require('sass');
function extractRootVariables(scssFile) {
const result = sass.renderSync({ file: scssFile });
const css = result.css.toString();
const rootVariables = {};
postcss.parse(css).walkRules(":root", (rule) => {
rule.walkDecls((decl) => {
if (decl.prop.startsWith("--")) {
rootVariables[decl.prop] = decl.value.trim();
}
});
});
return rootVariables;
}
function processRootVariables(rootVariables) {
const hslWithDegRegex = /^\d+(\.\d+)?(deg)?\s+\d+(\.\d+)?%\s+\d+(\.\d+)?%$/;
const hslFunctionRegex = /^hsl\(\d+(\.\d+)?(deg)?,\s?\d+%,\s?\d+%\)$/;
const hslaWithoutFunctionRegex = /^\d+(\.\d+)?(deg)?\s+\d+%\s+\d+%\s+(0?\.\d+|1)$/;
const hslaFunctionRegex = /^hsla\(\d+(\.\d+)?(deg)?,\s?\d+%,\s?\d+%,\s?(0?\.\d+|1)\)$/;
const hexRegex = /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/;
const rgbaRegex = /^rgba\((\d{1,3},\s?){3}(0?\.\d+|1)\)$/;
const rgbRegex = /^rgb\((\d{1,3},\s?){2}\d{1,3}\)$/;
const isColor = (value) => {
value = value.trim();
return (
hslWithDegRegex.test(value) ||
hslFunctionRegex.test(value) ||
hslaFunctionRegex.test(value) ||
hexRegex.test(value) ||
rgbaRegex.test(value) ||
rgbRegex.test(value) ||
hslaWithoutFunctionRegex.test(value)
);
};
const groupedKeys = {};
Object.entries(rootVariables).forEach(([key, value]) => {
const cleanKey = key.replace(/^--/, "");
const parts = cleanKey.split("-");
const rootKey = parts[0];
if (!groupedKeys[rootKey]) {
groupedKeys[rootKey] = [];
}
groupedKeys[rootKey].push({ cleanKey, parts, value });
});
const rootKeys = {};
const assignValue = (cleanKey, value) => {
if (hslWithDegRegex.test(value)) {
return `hsl(var(--${cleanKey}) / <alpha-value>)`;
}
if (hslFunctionRegex.test(value)) {
return `hsl(${value.replace(/deg/g, "")} / <alpha-value>)`;
}
return `var(--${cleanKey})`;
}
for (const [rootKey, entries] of Object.entries(groupedKeys)) {
if (entries.length === 0) {
console.warn(`No entries found for root key ${rootKey}`);
continue;
}
if (entries.length === 1 && isColor(entries[0].value)) {
let key = rootKey;
if (entries[0].parts.length > 1) {
key = entries[0].cleanKey;
}
rootKeys[key] = assignValue(entries[0].cleanKey, entries[0].value);
continue;
}
for (const { cleanKey, parts, value } of entries) {
if (!isColor(value)) {
continue;
}
if (!rootKeys[rootKey]) {
rootKeys[rootKey] = {};
}
let parent = rootKeys[rootKey];
for (let i = 1; i < parts.length; i++) {
const part = parts[i];
if (i === parts.length - 1) {
if (!parent[part]) {
parent[part] = assignValue(cleanKey, value);
} else if (typeof parent[part] === 'string') {
parent[part] = {
DEFAULT: parent[part],
[part]: assignValue(cleanKey, value),
};
}
} else {
if (typeof parent[part] === 'string') {
parent[part] = { DEFAULT: parent[part] };
}
if (!parent[part]) {
parent[part] = {};
}
parent = parent[part];
}
}
if (parts.length === 1 && isColor(value)) {
if (!rootKeys[rootKey].DEFAULT) {
rootKeys[rootKey].DEFAULT = assignValue(cleanKey, value);
}
}
}
}
return rootKeys;
}
function generateTailwindColors(scssFile) {
const rootVariables = extractRootVariables(scssFile);
return processRootVariables(rootVariables);
}
const colors = generateTailwindColors('./styles.scss');
module.exports = {
darkMode: ['class'],
content: [
join(
__dirname,
'{src,pages,components,app}/**/*!(*.stories|*.spec).{ts,tsx,html}'
),
],
theme: {
extend: {
colors:,
}
},
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment