UNPKG

@fylgja/props-builder

Version:

Effortlessly generate Design Tokens (CSS custom properties) from JavaScript objects.

110 lines (87 loc) 2.75 kB
// Fylgja (https://fylgja.dev) // Licensed under MIT Open Source import { cssVarRegex, kebabCase, flattenObj } from "./utils.js"; const toScssVar = (token) => { if (typeof token !== "string") return token; return token.replace(cssVarRegex, (match, tokenVal) => { return `#{$${tokenVal}}`; }); }; /** * Creates array of tokens from a javascript object with CSS props. * * @param {Object} props - The input object with CSS properties. * @param {Object} options - The options for generating tokens. * @param {string} options.selector - The CSS selector (default: ":root"). * @param {string} options.mediaDark - Media query for dark mode (default: "@media (prefers-color-scheme: dark)"). * @param {string} options.varSyntax - The syntax for variables, e.g. "--" for CSS variables or "$" for SCSS (default: "--"). * @returns {string} The generated CSS/SCSS styles as a string. */ const toStyleTokens = ( props, { selector = ":root", mediaDark = "@media (prefers-color-scheme: dark)", varSyntax = "--", } = {}, ) => { const indent = varSyntax === "--" ? "\t" : ""; const flatProps = flattenObj(props); const isScss = varSyntax === "$"; let styles = ""; let stylesDark = ""; let appendedMeta = ""; let result = ""; Object.entries(flatProps).forEach(([name, value]) => { const isKeyFrame = name.endsWith("-@"); const isDarkMode = name.includes("-@media:dark"); if (Array.isArray(value)) value = value.join(", "); if (isKeyFrame) { appendedMeta += `${appendedMeta ? "\n\n" : ""}${value}`; return; } if (name.includes(".")) { name = name.replaceAll(".", ""); } name = kebabCase(name); if (name.endsWith("-default")) { name = name.replace("-default", ""); } let varName = `${varSyntax}${name}`; if (isDarkMode) { varName = varName.replace("-@media:dark", isScss ? "-dark" : ""); } // Handle SCSS specific escaped values if (typeof value === "string" && isScss) { const regex = /[\/><=]|^\(.*\)$/; // If the value contains any of the characters /, >, <, >=, <= // or is wrapped in parentheses, wrap it in quotes if (regex.test(value)) { value = `"${value}"`; } } if (isScss) { value = toScssVar(value); } if (isDarkMode) { stylesDark += `${indent}${indent}${varName}: ${value};\n`; } else { styles += `${indent}${varName}: ${value};\n`; } }); if (isScss) { result = styles; result += stylesDark; } else { result = styles ? `${selector} {\n${styles}}\n` : ""; if (stylesDark) { result += `\n${mediaDark} {\n\t${selector} {\n${stylesDark}\t}\n}`; } } // Append keyframes/meta data if present if (appendedMeta) { result += `\n${appendedMeta}`; } return result.trim(); }; export default toStyleTokens;