@digigov/cli-theme-colors 1.0.0-rc.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.rush/temp/shrinkwrap-deps.json +11 -0
- package/index.js +64 -0
- package/leo-colors.js +247 -0
- package/package.json +27 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"../../tooling/cli-theme-colors": "../../tooling/cli-theme-colors:8ym4c0GEJGHHNRG7ngMe8eVYStPlxBLLTPcy+vS0qmQ=:",
|
|
3
|
+
"/@adobe/leonardo-contrast-colors@1.0.0-alpha.13": "Missing shrinkwrap entry!",
|
|
4
|
+
"/fs-extra@11.2.0": "Missing shrinkwrap entry!",
|
|
5
|
+
"/glob@7.1.6": "Missing shrinkwrap entry!",
|
|
6
|
+
"/polished@4.3.1": "Missing shrinkwrap entry!",
|
|
7
|
+
"/postcss-js@4.0.0(postcss@8.4.4)": "Missing shrinkwrap entry!",
|
|
8
|
+
"/postcss-load-config@3.1.4(postcss@8.4.4)(ts-node@10.9.2(@types/node@18.19.0)(typescript@5.6.2))": "Missing shrinkwrap entry!",
|
|
9
|
+
"/postcss@8.4.4": "Missing shrinkwrap entry!",
|
|
10
|
+
"/publint@0.1.8": "Missing shrinkwrap entry!"
|
|
11
|
+
}
|
package/index.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
const { resolveProject, DigigovCommand } = require("@digigov/cli/lib");
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const generateThemeColors = require("./leo-colors.js");
|
|
5
|
+
const postcss = require("postcss");
|
|
6
|
+
const postcssJs = require("postcss-js");
|
|
7
|
+
|
|
8
|
+
module.exports = class CreateTailwindTheme extends DigigovCommand {
|
|
9
|
+
static description = "generate CSS colors";
|
|
10
|
+
static id = "generate-colors";
|
|
11
|
+
dirname = __dirname;
|
|
12
|
+
static examples = [`$ digigov generate-colors`];
|
|
13
|
+
static strict = true;
|
|
14
|
+
|
|
15
|
+
static args = [
|
|
16
|
+
{
|
|
17
|
+
name: "theme",
|
|
18
|
+
description:
|
|
19
|
+
"Generate colors for only the specified theme. Otherwise, colors of all defined themes are generated.",
|
|
20
|
+
},
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
static load() {
|
|
24
|
+
return CreateTailwindTheme;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async run() {
|
|
28
|
+
const { args } = this.parse(CreateTailwindTheme);
|
|
29
|
+
try {
|
|
30
|
+
const project = resolveProject();
|
|
31
|
+
const brandConfigPath = path.join(project.root, "brandConfig.json");
|
|
32
|
+
|
|
33
|
+
if (!fs.existsSync(brandConfigPath))
|
|
34
|
+
return console.log("No `brandConfig.json` found");
|
|
35
|
+
|
|
36
|
+
const colorsFilePath = path.join(project.root, "colors.css");
|
|
37
|
+
if (fs.existsSync(colorsFilePath))
|
|
38
|
+
return console.log("`colors.css` already exists");
|
|
39
|
+
|
|
40
|
+
const brandConfig = JSON.parse(fs.readFileSync(brandConfigPath, "utf8"));
|
|
41
|
+
const defaultThemeName = brandConfig.defaultTheme;
|
|
42
|
+
|
|
43
|
+
const colorsObject = generateThemeColors(brandConfig, args.theme);
|
|
44
|
+
const cssObject = Object.keys(colorsObject).reduce(
|
|
45
|
+
(acc, currentThemeName) => {
|
|
46
|
+
const rootSelector =
|
|
47
|
+
defaultThemeName === currentThemeName
|
|
48
|
+
? ":root"
|
|
49
|
+
: `:root.${currentThemeName}, .${currentThemeName}`;
|
|
50
|
+
acc[rootSelector] = colorsObject[currentThemeName];
|
|
51
|
+
return acc;
|
|
52
|
+
},
|
|
53
|
+
{},
|
|
54
|
+
);
|
|
55
|
+
const css = await postcss()
|
|
56
|
+
.process(cssObject, { from: null, parser: postcssJs })
|
|
57
|
+
.then((res) => res.css);
|
|
58
|
+
|
|
59
|
+
fs.writeFileSync(colorsFilePath, css);
|
|
60
|
+
} catch (err) {
|
|
61
|
+
console.log(err);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
};
|
package/leo-colors.js
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
const {
|
|
2
|
+
Theme,
|
|
3
|
+
Color,
|
|
4
|
+
BackgroundColor,
|
|
5
|
+
} = require("@adobe/leonardo-contrast-colors");
|
|
6
|
+
const polished = require("polished");
|
|
7
|
+
|
|
8
|
+
module.exports = function generateColors(config, specifiedTheme) {
|
|
9
|
+
if (specifiedTheme)
|
|
10
|
+
return {
|
|
11
|
+
[specifiedTheme]: createThemeColorsObject(config, specifiedTheme),
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const allThemes = Object.keys(config.themes).filter(
|
|
15
|
+
(themeName) => themeName !== "default",
|
|
16
|
+
);
|
|
17
|
+
return allThemes.reduce((acc, themeName) => {
|
|
18
|
+
const themeColors = createThemeColorsObject(config, themeName);
|
|
19
|
+
acc[themeName] = themeColors;
|
|
20
|
+
return acc;
|
|
21
|
+
}, {});
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function createThemeColorsObject(config, specifiedTheme) {
|
|
25
|
+
console.log(`generating colors for ${specifiedTheme} theme`);
|
|
26
|
+
const lightness = config.themes[specifiedTheme]._config.lightness;
|
|
27
|
+
const fgColor = config.themes[specifiedTheme]._config.fgColor;
|
|
28
|
+
|
|
29
|
+
const { colors: dynamicColors, backgroundColorObject } = createColors(
|
|
30
|
+
config.colors.dynamic,
|
|
31
|
+
config.contrastRatios,
|
|
32
|
+
config.backgroundColor,
|
|
33
|
+
config.backgroundContrastRatios,
|
|
34
|
+
);
|
|
35
|
+
const theme = new Theme({
|
|
36
|
+
colors: dynamicColors,
|
|
37
|
+
backgroundColor: backgroundColorObject,
|
|
38
|
+
lightness,
|
|
39
|
+
fgColor,
|
|
40
|
+
contrast: 1,
|
|
41
|
+
});
|
|
42
|
+
const { colors: staticColors } = createColors(
|
|
43
|
+
config.colors.static,
|
|
44
|
+
null,
|
|
45
|
+
null,
|
|
46
|
+
null,
|
|
47
|
+
);
|
|
48
|
+
const staticTheme = new Theme({
|
|
49
|
+
colors: staticColors,
|
|
50
|
+
backgroundColor: backgroundColorObject,
|
|
51
|
+
lightness: 100,
|
|
52
|
+
fgColor,
|
|
53
|
+
contrast: 1,
|
|
54
|
+
});
|
|
55
|
+
staticTheme.contrastColors.shift(1);
|
|
56
|
+
const colors = theme.contrastColors.concat(
|
|
57
|
+
staticTheme.contrastColors.map((sColor) => {
|
|
58
|
+
sColor.values[0].value = config.colors.static[sColor.name][0];
|
|
59
|
+
return sColor;
|
|
60
|
+
}),
|
|
61
|
+
);
|
|
62
|
+
const brandColorObjects = createBrandColors(config.brandColors);
|
|
63
|
+
const brandTheme = new Theme({
|
|
64
|
+
colors: brandColorObjects,
|
|
65
|
+
backgroundColor: backgroundColorObject,
|
|
66
|
+
lightness: 100,
|
|
67
|
+
fgColor,
|
|
68
|
+
contrast: 1,
|
|
69
|
+
});
|
|
70
|
+
const brandColorNames = Object.keys(config.brandColors);
|
|
71
|
+
brandTheme.contrastColors.shift(1);
|
|
72
|
+
const brandColors = brandTheme.contrastColors.map((color, index) => {
|
|
73
|
+
color.values = [
|
|
74
|
+
{
|
|
75
|
+
name: brandColorNames[index],
|
|
76
|
+
value: config.brandColors[brandColorNames[index]],
|
|
77
|
+
},
|
|
78
|
+
color.values[0],
|
|
79
|
+
{
|
|
80
|
+
...color.values[1],
|
|
81
|
+
value: config.brandColors[brandColorNames[index]],
|
|
82
|
+
},
|
|
83
|
+
color.values[2],
|
|
84
|
+
];
|
|
85
|
+
return color;
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const themeNames = Object.keys(config.themes).filter(
|
|
89
|
+
(name) => name !== "default",
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const themesConfig = Object.keys(config.themes).reduce(
|
|
93
|
+
(newThemesConfig, themeName) => {
|
|
94
|
+
if (themeNames.includes(themeName)) {
|
|
95
|
+
newThemesConfig[themeName] = {
|
|
96
|
+
...config.themes.default,
|
|
97
|
+
...config.themes[themeName],
|
|
98
|
+
};
|
|
99
|
+
delete newThemesConfig[themeName]._config;
|
|
100
|
+
}
|
|
101
|
+
return newThemesConfig;
|
|
102
|
+
},
|
|
103
|
+
{},
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const activeSemanticColors = getSemanticColors(
|
|
107
|
+
themesConfig[specifiedTheme],
|
|
108
|
+
colors,
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
let jsonTheme = colors
|
|
112
|
+
.filter((color) => color.name !== undefined)
|
|
113
|
+
.concat(brandColors)
|
|
114
|
+
.concat(activeSemanticColors)
|
|
115
|
+
.reduce((acc, cur) => {
|
|
116
|
+
const { name: groupName, values } = cur;
|
|
117
|
+
const group = groupName.trim().toLowerCase().replace(/\s+/g, "-");
|
|
118
|
+
acc[group] = {};
|
|
119
|
+
if (values) {
|
|
120
|
+
values.forEach((colorVal) => {
|
|
121
|
+
const { name, value } = colorVal;
|
|
122
|
+
let colorNum = name.match(/\d+/g) ? name.match(/\d+/g)[0] : name;
|
|
123
|
+
if (group === colorNum) {
|
|
124
|
+
colorNum = "default";
|
|
125
|
+
}
|
|
126
|
+
acc[group][colorNum] = value;
|
|
127
|
+
return;
|
|
128
|
+
});
|
|
129
|
+
return acc;
|
|
130
|
+
}
|
|
131
|
+
return acc;
|
|
132
|
+
}, {});
|
|
133
|
+
|
|
134
|
+
return extractColorVars(jsonTheme);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function createColors(
|
|
138
|
+
colorConfig,
|
|
139
|
+
contrastRatios,
|
|
140
|
+
backgroundColor,
|
|
141
|
+
backgroundContrastRatios,
|
|
142
|
+
) {
|
|
143
|
+
let backgroundColorObject;
|
|
144
|
+
const colors = Object.keys(colorConfig).map((key) => {
|
|
145
|
+
if (key === backgroundColor) {
|
|
146
|
+
backgroundColorObject = new BackgroundColor({
|
|
147
|
+
name: key,
|
|
148
|
+
colorKeys: colorConfig[key],
|
|
149
|
+
ratios: backgroundContrastRatios,
|
|
150
|
+
colorspace: "CAM02",
|
|
151
|
+
smooth: true,
|
|
152
|
+
});
|
|
153
|
+
return backgroundColorObject;
|
|
154
|
+
} else {
|
|
155
|
+
if (typeof colorConfig[key] === "string") {
|
|
156
|
+
colorConfig[key] = [colorConfig[key]];
|
|
157
|
+
}
|
|
158
|
+
if (!contrastRatios) {
|
|
159
|
+
contrastRatios = [polished.getContrast("#fff", colorConfig[key][0])];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return new Color({
|
|
163
|
+
name: key,
|
|
164
|
+
colorKeys: colorConfig[key],
|
|
165
|
+
ratios: contrastRatios,
|
|
166
|
+
colorspace: "CAM02",
|
|
167
|
+
smooth: true,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
return { colors, backgroundColorObject };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function getColorByNameAndNumber(valueName, valueNumber, colors) {
|
|
175
|
+
let color = colors
|
|
176
|
+
.find((color) => color.name === valueName)
|
|
177
|
+
.values.find((color) => color.name === valueName + valueNumber);
|
|
178
|
+
return color;
|
|
179
|
+
}
|
|
180
|
+
function getSemanticColorShades(semanticColor, states, colors) {
|
|
181
|
+
let values = [];
|
|
182
|
+
|
|
183
|
+
Object.keys(states).forEach((key) => {
|
|
184
|
+
values.push({
|
|
185
|
+
name: key,
|
|
186
|
+
contrast: getColorByNameAndNumber(states[key][0], states[key][1], colors)
|
|
187
|
+
.contrast,
|
|
188
|
+
value: getColorByNameAndNumber(states[key][0], states[key][1], colors)
|
|
189
|
+
.value,
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
return {
|
|
193
|
+
name: semanticColor,
|
|
194
|
+
values: values,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function getSemanticColors(values, paletteColors) {
|
|
199
|
+
return Object.keys(values).map((key) => {
|
|
200
|
+
return getSemanticColorShades(key, values[key], paletteColors);
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function createBrandColors(brandColorConfig) {
|
|
205
|
+
return Object.keys(brandColorConfig).map((colorKey) => {
|
|
206
|
+
const colorValue = brandColorConfig[colorKey];
|
|
207
|
+
return new Color({
|
|
208
|
+
name: colorKey,
|
|
209
|
+
colorKeys: [colorValue],
|
|
210
|
+
ratios: [
|
|
211
|
+
polished.getContrast("#fff", colorValue) -
|
|
212
|
+
polished.getContrast("#fff", colorValue) * 0.25,
|
|
213
|
+
polished.getContrast("#fff", colorValue),
|
|
214
|
+
polished.getContrast("#fff", colorValue) +
|
|
215
|
+
polished.getContrast("#fff", colorValue) * 0.25,
|
|
216
|
+
],
|
|
217
|
+
colorspace: "CAM02",
|
|
218
|
+
smooth: true,
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function extractColorVars(colorObj, colorGroup = "") {
|
|
224
|
+
return Object.keys(colorObj).reduce((vars, colorKey) => {
|
|
225
|
+
const value = colorObj[colorKey];
|
|
226
|
+
const colorName =
|
|
227
|
+
colorKey == "default"
|
|
228
|
+
? `--color${colorGroup}`
|
|
229
|
+
: `--color${colorGroup}-${colorKey}`;
|
|
230
|
+
const newVars =
|
|
231
|
+
typeof value === "string"
|
|
232
|
+
? {
|
|
233
|
+
[colorName]: value,
|
|
234
|
+
[`${colorName}-rgb`]: hexToRGB(value),
|
|
235
|
+
}
|
|
236
|
+
: extractColorVars(value, `-${colorKey}`);
|
|
237
|
+
return { ...vars, ...newVars };
|
|
238
|
+
}, {});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function hexToRGB(hex) {
|
|
242
|
+
const r = parseInt(hex.slice(1, 3), 16),
|
|
243
|
+
g = parseInt(hex.slice(3, 5), 16),
|
|
244
|
+
b = parseInt(hex.slice(5, 7), 16);
|
|
245
|
+
|
|
246
|
+
return r + ", " + g + ", " + b;
|
|
247
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@digigov/cli-theme-colors",
|
|
3
|
+
"version": "1.0.0-rc.21",
|
|
4
|
+
"description": "CLI for generating CSS colors",
|
|
5
|
+
"main": "./index.js",
|
|
6
|
+
"author": "GRNET Devs <devs@lists.grnet.gr>",
|
|
7
|
+
"license": "BSD-2-Clause",
|
|
8
|
+
"private": false,
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"fs-extra": "11.2.0",
|
|
11
|
+
"glob": "7.1.6",
|
|
12
|
+
"postcss": "8.4.4",
|
|
13
|
+
"postcss-js": "4.0.0",
|
|
14
|
+
"postcss-load-config": "3.1.4",
|
|
15
|
+
"@adobe/leonardo-contrast-colors": "1.0.0-alpha.13",
|
|
16
|
+
"polished": "4.3.1"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"publint": "0.1.8"
|
|
20
|
+
},
|
|
21
|
+
"peerDependencies": {
|
|
22
|
+
"@digigov/cli": "1.1.1-rc.21"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"publint": "publint"
|
|
26
|
+
}
|
|
27
|
+
}
|