aboutsummaryrefslogtreecommitdiff
path: root/FrontEnd/tools/palette.ts
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2023-07-14 00:30:17 +0800
committercrupest <crupest@outlook.com>2023-07-14 00:30:17 +0800
commit810185b8df1fd191cd4a5e7eb92e1986000880b1 (patch)
treeb917c84f12d715fc6c66377feca9e14df787144c /FrontEnd/tools/palette.ts
parent4a069bf1268f393d5467166356f691eb89963152 (diff)
downloadtimeline-810185b8df1fd191cd4a5e7eb92e1986000880b1.tar.gz
timeline-810185b8df1fd191cd4a5e7eb92e1986000880b1.tar.bz2
timeline-810185b8df1fd191cd4a5e7eb92e1986000880b1.zip
...
Diffstat (limited to 'FrontEnd/tools/palette.ts')
-rw-r--r--FrontEnd/tools/palette.ts271
1 files changed, 271 insertions, 0 deletions
diff --git a/FrontEnd/tools/palette.ts b/FrontEnd/tools/palette.ts
new file mode 100644
index 00000000..7b047ce1
--- /dev/null
+++ b/FrontEnd/tools/palette.ts
@@ -0,0 +1,271 @@
+#!/usr/bin/env ts-node
+
+/**
+ * Color variable name scheme:
+ * has variant: --[prefix]-[name]-[variant]-color: [color];
+ * no variant: --[prefix]-[name]-color: [color];
+ * Variant scheme:
+ * [variant-prefix][level]
+ * eg. --cru-primary-color: [color]; --cru-primary-l1-color: [color];
+ */
+
+class HslColor {
+ constructor(
+ public h: number,
+ public s: number,
+ public l: number,
+ ) {}
+
+ lighter(level: number): HslColor {
+ return new HslColor(this.h, this.s, this.l + level * 10);
+ }
+
+ darker(level: number): HslColor {
+ return new HslColor(this.h, this.s, this.l - level * 10);
+ }
+
+ toString(): string {
+ return `hsl(${this.h} ${this.s}% ${this.l}%)`;
+ }
+}
+
+class CssVarColor {
+ constructor(
+ public name: string,
+ public cssVar: string,
+ ) {}
+
+ toString(): string {
+ return `var(--${this.cssVar})`;
+ }
+}
+
+class VariantColor {
+ constructor(
+ public color: HslColor,
+ public variant?: string | null,
+ ) {}
+
+ toCssString(prefix: string, name: string): string {
+ const variantPart = this.variant == null ? "" : `-${this.variant}`;
+ return `--${prefix}-${name}${variantPart}-color: ${this.color.toString()};`;
+ }
+}
+
+type LightnessVariantType = "lighter" | "darker";
+
+interface LightnessVariant {
+ prefix: string;
+ type: LightnessVariantType;
+}
+
+function generateLightnessVariantColors(
+ baseColor: HslColor,
+ lightnessVariant: LightnessVariant,
+ levels: number,
+): VariantColor[] {
+ const result: VariantColor[] = [];
+
+ for (let i = 1; i <= levels; i++) {
+ const color =
+ lightnessVariant.type === "lighter"
+ ? baseColor.lighter(i)
+ : baseColor.darker(i);
+ const colorVariant = `${lightnessVariant.prefix}${i}`;
+ result.push(new VariantColor(color, colorVariant));
+ }
+
+ return result;
+}
+
+type ColorMode = "light" | "dark";
+
+const themeVariantPrefixes = ["l", "d", "f", "b"] as const;
+
+type LightnessVariants = {
+ prefix: (typeof themeVariantPrefixes)[number];
+ type: LightnessVariantType;
+}[];
+
+function generateThemeColorLightnessVariants(
+ mode: ColorMode,
+): LightnessVariants {
+ return [
+ {
+ prefix: "l",
+ type: "lighter",
+ },
+ {
+ prefix: "d",
+ type: "darker",
+ },
+ {
+ prefix: "f",
+ type: mode === "light" ? "lighter" : "darker",
+ },
+ {
+ prefix: "b",
+ type: mode === "light" ? "darker" : "lighter",
+ },
+ ];
+}
+
+class ColorGroup {
+ constructor(
+ public name: string,
+ public baseColor: HslColor,
+ ) {}
+
+ generateVariantColors(
+ lightnessVariants: LightnessVariant[],
+ levels = 3,
+ ): VariantColor[] {
+ const result: VariantColor[] = [new VariantColor(this.baseColor)];
+
+ for (const lightnessVariant of lightnessVariants) {
+ result.push(
+ ...generateLightnessVariantColors(
+ this.baseColor,
+ lightnessVariant,
+ levels,
+ ),
+ );
+ }
+
+ return result;
+ }
+}
+
+class ThemeColorGroup extends ColorGroup {
+ constructor(name: string, baseColor: HslColor) {
+ super(name, baseColor);
+ }
+
+ generateColors(mode: ColorMode): VariantColor[] {
+ return super.generateVariantColors(
+ generateThemeColorLightnessVariants(mode),
+ );
+ }
+
+ generateCss(prefix: string, mode: ColorMode): string {
+ return this.generateColors(mode)
+ .map((c) => c.toCssString(prefix, this.name))
+ .join("\n");
+ }
+}
+
+class VarColorGroup {
+ constructor(
+ public name: string,
+ public varName: string,
+ ) {}
+
+ generateCss(
+ prefix: string,
+ variantPrefixes = themeVariantPrefixes,
+ levels = 3,
+ ): string {
+ const vs: string[] = [];
+ for (const v of variantPrefixes) {
+ for (let l = 1; l <= levels; l++) {
+ vs.push(`${v}${l}`);
+ }
+ }
+ let result = vs
+ .map(
+ (v) =>
+ `--${prefix}-${this.name}-${v}-color: var(--${prefix}-${this.varName}-${v}-color);`,
+ )
+ .join("\n");
+ result = `--${prefix}-${this.name}-color: var(--${prefix}-${this.varName}-color);\n${result}`;
+ return result;
+ }
+}
+
+const themeColorNames = [
+ "primary",
+ "secondary",
+ "tertiary",
+ "danger",
+ "success",
+] as const;
+
+type ThemeColorNames = (typeof themeColorNames)[number];
+
+type ThemeColors = {
+ [key in ThemeColorNames]: HslColor;
+};
+
+// Config region begin
+const prefix = "cru";
+
+const themeColors: ThemeColors = {
+ primary: new HslColor(210, 100, 50),
+ secondary: new HslColor(40, 100, 50),
+ tertiary: new HslColor(160, 100, 50),
+ danger: new HslColor(0, 100, 50),
+ success: new HslColor(120, 100, 50),
+};
+
+// Config region end
+
+let output = "";
+
+function indentText(
+ text: string,
+ level: number,
+ indentWidth = 2,
+ appendNewlines = 1,
+): string {
+ const lines = text.split("\n");
+ const indent = " ".repeat(level * indentWidth);
+ return (
+ lines
+ .map((line) => (line.length === 0 ? "" : `${indent}${line}`))
+ .join("\n") + "\n".repeat(appendNewlines)
+ );
+}
+
+function print(text: string, indent = 0, appendNewlines = 1) {
+ output += indentText(text, indent, 2, appendNewlines);
+}
+
+function generateThemeColorCss(mode: ColorMode): string {
+ let output = "";
+ for (const name of themeColorNames) {
+ const colorGroup = new ThemeColorGroup(name, themeColors[name]);
+ output += colorGroup.generateCss(prefix, mode);
+ }
+ return output;
+}
+
+function generateThemeColorAliasCss(): string {
+ let result = "";
+ for (const name of themeColorNames) {
+ const varColorGroup = new VarColorGroup("theme", name);
+ result += `.${prefix}-${name} {\n${indentText(
+ varColorGroup.generateCss(prefix),
+ 1,
+ )}\n}\n`;
+ }
+ return result;
+}
+
+function main() {
+ print("/* Generated by palette.ts */\n");
+
+ print(":root {");
+ print(generateThemeColorCss("light"), 1);
+ print("}\n");
+
+ print("@media (prefers-color-scheme: dark) {");
+ print(":root {", 1);
+ print(generateThemeColorCss("dark"), 2);
+ print("}", 1);
+ print("}\n");
+
+ print(generateThemeColorAliasCss());
+}
+
+main();
+process.stdout.write(output);