diff --git a/bin/colopocalypse b/bin/colopocalypse index e6f708d999f37e6b48fa8272dfd14695a60c8b3c..f9613b5add1c798792196a453e43a36cfeca2070 100755 --- a/bin/colopocalypse +++ b/bin/colopocalypse @@ -24,87 +24,38 @@ const FILE_GLOB_IGNORE = [ // "**/metabase/lib/colors.js" ]; -const candidates = {}; +const COLORS_CSS_PATH = "frontend/src/metabase/css/core/colors.css"; -function addCandidate(colorName, baseColor, tints = [1]) { - for (const tint of tints) { - if (tint === 1) { - candidates[colorName] = baseColor.rgb(); - } else { - candidates[`${colorName}-${tint * 100}`] = Color("white") - .mix(baseColor, tint) - .rgb(); - } - } -} +const varForName = name => `--color-${name}`; + +const colors = { + brand: Color("#509EE3"), + accent1: Color("#9CC177"), + accent2: Color("#A989C5"), + accent3: Color("#EF8C8C"), + accent4: Color("#F9D45C"), + accent5: Color("#F1B556"), + accent6: Color("#A6E7F3"), + accent7: Color("#7172AD"), + + white: Color("#FFFFFF"), + + "text-dark": Color("#2E353B"), + "text-medium": Color("#93A1AB"), + "text-light": Color("#DCE1E4"), -// add candidate colors, currently various tints of normal, saturated, and desaturated -// const colors = require("../frontend/src/metabase/lib/colors"); -// const tints = [1, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.05, 0.02]; -// -// for (const [colorName, color] of Object.entries(colors.normal)) { -// addCandidate(colorName, Color(color), tints); -// } -// for (const [colorName, color] of Object.entries(colors.saturated)) { -// addCandidate(colorName + "-saturated", Color(color), tints); -// } -// for (const [colorName, color] of Object.entries(colors.desaturated)) { -// addCandidate(colorName + "-desaturated", Color(color), tints); -// } - -addCandidate("brand", Color("#509EE3")); -addCandidate("accent1", Color("#9CC177")); -addCandidate("accent2", Color("#A989C5")); -addCandidate("accent3", Color("#EF8C8C")); -addCandidate("accent4", Color("#F9D45C")); -addCandidate("accent5", Color("#F1B556")); -addCandidate("accent6", Color("#A6E7F3")); -addCandidate("accent7", Color("#7172AD")); - -addCandidate("white", Color("#FFFFFF")); - -addCandidate("text-dark", Color("#2E353B")); -addCandidate("text-medium", Color("#93A1AB")); -addCandidate("text-light", Color("#DCE1E4")); - -addCandidate("bg-dark", Color("#EDF2F5")); -addCandidate("bg-light", Color("#F9FBFC")); - -addCandidate("shadow", Color("#F4F5F6")); -addCandidate("border", Color("#D7DBDE")); - -addCandidate("success", Color("#84BB4C")); -addCandidate("error", Color("#ED6E6E")); -addCandidate("warning", Color("#F9CF48")); - -// maybe this should weight the difference in hue more heavily? -// function colorDifference(colorA, colorB) { -// return Math.sqrt( -// Math.pow(colorA.red() - colorB.red(), 2) + -// Math.pow(colorA.green() - colorB.green(), 2) + -// Math.pow(colorA.blue() - colorB.blue(), 2), -// ); -// } - -// function getBestCandidate(color) { -// let bestName; -// let bestDistance = Infinity; -// for (const [candidateName, candidate] of Object.entries(candidates)) { -// const distance = colorDifference(color, candidate); -// if (distance < bestDistance) { -// bestName = candidateName; -// bestDistance = distance; -// } -// } -// let bestColor = candidates[bestName]; -// if (color.alpha() < 1) { -// bestColor = bestColor.alpha(color.alpha()); -// } -// // console.log(color.string(), "=>", bestName, bestColor.string(), bestDistance); -// return [bestName, bestColor]; -// } - -const palette = Object.entries(candidates).map(([name, color]) => ({ + "bg-dark": Color("#EDF2F5"), + "bg-light": Color("#F9FBFC"), + + shadow: Color("#F4F5F6"), + border: Color("#D7DBDE"), + + success: Color("#84BB4C"), + error: Color("#ED6E6E"), + warning: Color("#F9CF48"), +}; + +const palette = Object.entries(colors).map(([name, color]) => ({ name, color, R: color.red(), @@ -133,7 +84,35 @@ function replaceSimpleColorValues(content, isCSS) { } else { console.log(color, "=>", newColor.string(), `(${newColorName})`); } - return newColor.string(); + if (newColor.alpha < 1) { + return newColor.string(); + } else if (!isCSS) { + return newColor.hex(); + } else { + return `var(${varForName(newColorName)})`; + } + }); +} + +function replaceCSSVariables(content, isCSS) { + if (!isCSS) { + return content; + } + return content.replace(CSS_VAR_REGEX, variable => { + const color = resolveCSSVariableColor(variable); + if (color) { + const [newColorName, newColor] = getBestCandidate(color); + console.log(variable, "=>", newColor.string(), `(${newColorName})`); + + if (newColor.alpha() < 1) { + // TODO: color(var(...) alpha(...)) + return newColor.string(); + } else { + return `var(${varForName(newColorName)})`; + } + } else { + return variable; + } }); } @@ -165,22 +144,6 @@ function resolveCSSVariableColor(value) { return null; } -function replaceCSSVariables(content, isCSS) { - if (!isCSS) { - return content; - } - return content.replace(CSS_VAR_REGEX, variable => { - const color = resolveCSSVariableColor(variable); - if (color) { - const [newColorName, newColor] = getBestCandidate(color); - console.log(variable, "=>", newColor.string(), `(${newColorName})`); - return newColor.string(); - } else { - return variable; - } - }); -} - function processFiles(files) { for (const file of files) { const isCSS = /\.css/.test(file); @@ -190,6 +153,22 @@ function processFiles(files) { content = replaceCSSVariables(content, isCSS); fs.writeFileSync(file, content); } + + // do this last so we don't replace them + prependColorVarsBlock(); +} + +function prependColorVarsBlock() { + const colorsVarsBlock = + `:root {\n` + + Object.entries(colors) + .map(([name, color]) => ` ${varForName(name)}: ${color.hex()};`) + .join("\n") + + `\n}\n\n`; + fs.writeFileSync( + COLORS_CSS_PATH, + colorsVarsBlock + fs.readFileSync(COLORS_CSS_PATH, "utf-8"), + ); } glob(