pacem-less 0.51.4-babbage → 0.51.4-cantor

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/themebuilder.js +73 -52
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.51.4-babbage",
2
+ "version": "0.51.4-cantor",
3
3
  "name": "pacem-less",
4
4
  "homepage": "https://js.pacem.it",
5
5
  "repository": {
package/themebuilder.js CHANGED
@@ -30,6 +30,14 @@ const argv = yargs
30
30
  .option('void', {
31
31
  type: 'string',
32
32
  description: 'Creates an empty palette file.',
33
+ })
34
+ .option('mincontrast', {
35
+ type: 'number',
36
+ description: 'The minimum WCAG contrast (1 to 21) container-to-background (default 1.5).',
37
+ })
38
+ .option('maxcontrast', {
39
+ type: 'number',
40
+ description: 'The maximum WCAG contrast (1 to 21) default-to-background (default 15).',
33
41
  })
34
42
  .help()
35
43
  .argv;
@@ -55,7 +63,7 @@ function readFile(src) {
55
63
  const json = fs.readFileSync(src, 'utf-8');
56
64
  return JSON.parse(json);
57
65
  }
58
- const palette = readFile(argv.src) ?? {
66
+ const palette = Object.assign({
59
67
  background: '#1e2336',
60
68
  primary: '#429bbb',
61
69
  secondary: '#5e7881',
@@ -75,24 +83,30 @@ const palette = readFile(argv.src) ?? {
75
83
  purple: '#624472',
76
84
  teal: '#429bbb',
77
85
  gray: '#808080',
78
- };
79
- const WCAG_MINIMUM_CONTRAST = 3;
86
+ }, readFile(argv.src) ?? {});
87
+ const WCAG_MINIMUM_CONTRAST = argv.mincontrast ?? 1.5;
80
88
  const WCAG_NORMAL_TEXT_CONTRAST = 4.5;
81
89
  const WCAG_SAFE_CONTRAST = 7;
82
- const WCAG_MAX_CONTRAST = 17;
90
+ const WCAG_MAX_CONTRAST = argv.maxcontrast ?? 15;
91
+ const WCAG_DISABLED_CONTRAST = 1.6;
83
92
  const SURFACE_LIGHT = .95;
84
93
  const SURFACE_DARK = .02;
85
94
  const MIN_SATURATION = .175;
86
95
  const searchFn = Pacem.Mathematics.DataAnalysis.SearchFunctions.goldenRatio;
96
+ let backgroundModule;
97
+ let rootModule;
87
98
  function isColorLight(clr) {
88
99
  return Pacem.Colors.luminance(clr) >= .2;
89
100
  }
90
- function contrastColor(baseColor, targetContrast = WCAG_NORMAL_TEXT_CONTRAST) {
101
+ function contrastColor(baseColor, arg1 = baseColor, arg2 = WCAG_NORMAL_TEXT_CONTRAST, arg3 = null) {
102
+ const benchmarkColor = typeof arg1 === 'object' ? arg1 : baseColor;
103
+ const targetContrast = typeof arg1 === 'number' ? arg1 : (typeof arg2 === 'number' ? arg2 : WCAG_NORMAL_TEXT_CONTRAST);
104
+ const preferDark = typeof arg2 === 'boolean' ? arg2 : arg3;
91
105
  const { h, s, l, a } = Pacem.Colors.hsl(baseColor);
92
- const isDark = !isColorLight(baseColor);
106
+ const isDark = preferDark ?? !isColorLight(baseColor);
93
107
  const min = isDark ? l : 0;
94
108
  const max = isDark ? 1 : l;
95
- const newL = searchFn(min, max, x => Math.abs(targetContrast - Pacem.Colors.contrastRatio(baseColor, { h, s, l: x, a })));
109
+ const newL = searchFn(min, max, x => Math.abs(targetContrast - Pacem.Colors.contrastRatio(benchmarkColor, { h, s, l: x, a })));
96
110
  return Pacem.Colors.rgb({ h, s, l: newL, a });
97
111
  }
98
112
  function luminanceColor(baseColor, targetLuminance) {
@@ -100,11 +114,32 @@ function luminanceColor(baseColor, targetLuminance) {
100
114
  const newL = searchFn(0, 1, x => Math.abs(targetLuminance - Pacem.Colors.luminance({ h, s, l: x, a })));
101
115
  return Pacem.Colors.rgb({ h, s, l: newL, a });
102
116
  }
117
+ function invertModule(mod) {
118
+ return {
119
+ base: mod.contrast,
120
+ contrast: mod.base,
121
+ active: mod.activeContrast,
122
+ activeContrast: mod.active,
123
+ emphasis: mod.base,
124
+ container: mod.containerContrast,
125
+ containerContrast: mod.container,
126
+ containerActive: mod.containerActiveContrast,
127
+ containerActiveContrast: mod.containerActive,
128
+ };
129
+ }
130
+ function desaturateColor(clr, threshold = MIN_SATURATION) {
131
+ const { h, s, l, a } = Pacem.Colors.hsl(clr);
132
+ if (s > threshold) {
133
+ return Pacem.Colors.rgb({ h, s: threshold, l, a });
134
+ }
135
+ else
136
+ return clr;
137
+ }
103
138
  function colorModule(clr, name, arg2) {
104
139
  const isDisabled = name === 'disabled';
105
- const isSurfaceColor = name === 'root' || name === 'background' || isDisabled;
140
+ const isSurfaceColor = name === 'root' || name === 'background';
106
141
  const isDesaturated = isDisabled || name === 'gray';
107
- const forcedContrast = (arg2 && typeof arg2 === 'number') ? arg2 : (isDisabled ? 1.7 : null);
142
+ const forcedContrast = (arg2 && typeof arg2 === 'number') ? arg2 : (isDisabled ? WCAG_DISABLED_CONTRAST : null);
108
143
  const baseContrast = forcedContrast ?? (isSurfaceColor ? WCAG_MAX_CONTRAST : WCAG_SAFE_CONTRAST);
109
144
  const contrastClr = arg2 && typeof arg2 === 'object' ? arg2 : null;
110
145
  const surfaceLight = name === 'background' ? SURFACE_LIGHT + .015 : SURFACE_LIGHT;
@@ -114,19 +149,19 @@ function colorModule(clr, name, arg2) {
114
149
  const lightTargetLuminance = isDisabled ? (surfaceLight - .1) : (isSurfaceColor ? surfaceLight : .333);
115
150
  const referenceColor = isColorRight ? clr
116
151
  : (isSurfaceColor ? (!darkTheme ? luminanceColor(clr, lightTargetLuminance) : luminanceColor(clr, darkTargetLuminance))
117
- : (darkTheme ? luminanceColor(clr, lightTargetLuminance) : luminanceColor(clr, darkTargetLuminance)));
152
+ : contrastColor(clr, backgroundModule.base, WCAG_NORMAL_TEXT_CONTRAST, darkTheme));
118
153
  let actualColor = contrastClr ? clr : referenceColor;
119
154
  if (isDesaturated) {
120
- const { h, s, l, a } = Pacem.Colors.hsl(actualColor);
121
- if (s > MIN_SATURATION) {
122
- actualColor = Pacem.Colors.rgb({ h, s: MIN_SATURATION, l, a });
123
- }
155
+ actualColor = desaturateColor(actualColor);
124
156
  }
125
157
  const luminanceBackContainer = darkTheme ? .075 : .5;
126
158
  const luminanceBackActiveOffset = darkTheme ? (isSurfaceColor ? .035 : .1) : (isSurfaceColor ? -.08 : -.06);
127
159
  const contrastForeActive = forcedContrast ?? WCAG_SAFE_CONTRAST;
128
160
  const contrast = contrastClr ?? contrastColor(actualColor, baseContrast);
129
- const container = luminanceColor(actualColor, luminanceBackContainer);
161
+ const containerTargetContrast = (isDisabled ? Math.min(forcedContrast, .5 * (1 + WCAG_MINIMUM_CONTRAST)) : WCAG_MINIMUM_CONTRAST).roundoff();
162
+ const containerBenchmarkColor = isSurfaceColor ? actualColor : backgroundModule.base;
163
+ const container = contrastColor(actualColor, containerBenchmarkColor, containerTargetContrast, !(+isSurfaceColor ^ +darkTheme));
164
+ console.log(name + ' container yields ' + Pacem.Colors.stringify(container) + ', which should contrast ' + containerTargetContrast + ' with ' + Pacem.Colors.stringify(containerBenchmarkColor));
130
165
  const containerContrast = contrastColor(container, isDisabled ? forcedContrast : WCAG_NORMAL_TEXT_CONTRAST);
131
166
  const active = isDisabled ? actualColor : luminanceColor(actualColor, Pacem.Colors.luminance(actualColor) + luminanceBackActiveOffset);
132
167
  const activeContrast = isDisabled ? contrast : contrastColor(active, contrastForeActive);
@@ -146,8 +181,6 @@ function colorModule(clr, name, arg2) {
146
181
  };
147
182
  }
148
183
  const lessVariables = {};
149
- let backgroundModule;
150
- let rootModule;
151
184
  const a255 = (x) => Math.round(x * 255);
152
185
  const a255s = (c) => `${a255(c.r)}, ${a255(c.g)}, ${a255(c.b)}`;
153
186
  const pushLessVariables = (name, mod) => {
@@ -170,47 +203,35 @@ const pushLessVariables = (name, mod) => {
170
203
  lessVariables[`@color_${name}_emphasis`] = Pacem.Colors.stringify(mod.emphasis);
171
204
  lessVariables[`@color_${name}_emphasis_rgb`] = a255s(mod.emphasis);
172
205
  };
206
+ const backgroundColor = Pacem.Colors.parse(palette['background']);
207
+ backgroundModule = colorModule(backgroundColor, 'background');
208
+ pushLessVariables('background', backgroundModule);
209
+ const rootColor = 'root' in palette ? Pacem.Colors.parse(palette['root']) : Pacem.Colors.darken(backgroundModule.base, darkTheme ? .02 : .01);
210
+ rootModule = colorModule(rootColor, 'root');
211
+ pushLessVariables('root', rootModule);
212
+ if ('default' in palette) {
213
+ const defaultColor = contrastColor(desaturateColor(Pacem.Colors.parse(palette['default'])), rootModule.base, WCAG_MAX_CONTRAST, darkTheme);
214
+ const defaultModule = colorModule(defaultColor, 'default');
215
+ pushLessVariables('default', defaultModule);
216
+ }
217
+ else {
218
+ pushLessVariables('default', invertModule(rootModule));
219
+ }
173
220
  for (let name in palette) {
221
+ if (['default', 'background', 'root'].indexOf(name) >= 0) {
222
+ continue;
223
+ }
174
224
  const color = Pacem.Colors.parse(palette[name]);
175
225
  const mod = colorModule(color, name);
176
- if (name === 'background') {
177
- backgroundModule = mod;
178
- }
179
- if (name === 'root') {
180
- rootModule = mod;
181
- }
182
226
  pushLessVariables(name, mod);
183
227
  }
184
- if (!('root' in palette)) {
185
- const rootColor = Pacem.Colors.darken(backgroundModule.base, darkTheme ? .02 : .01);
186
- rootModule = colorModule(rootColor, 'root');
187
- pushLessVariables('root', rootModule);
188
- }
189
228
  if (!('disabled' in palette)) {
190
- const disabledColor = darkTheme ? Pacem.Colors.lighten(backgroundModule.base, .04) : Pacem.Colors.darken(backgroundModule.base, .085);
191
- pushLessVariables('disabled', colorModule(disabledColor, 'disabled'));
192
- }
193
- if (!('default' in palette)) {
194
- for (let name of ['default', 'invert']) {
195
- lessVariables[`@color_${name}`] = Pacem.Colors.stringify(rootModule.contrast);
196
- lessVariables[`@color_${name}_rgb`] = a255s(rootModule.contrast);
197
- lessVariables[`@color_${name}_inv`] = Pacem.Colors.stringify(rootModule.base);
198
- lessVariables[`@color_${name}_inv_rgb`] = a255s(rootModule.base);
199
- lessVariables[`@color_${name}_container`] = Pacem.Colors.stringify(rootModule.containerContrast);
200
- lessVariables[`@color_${name}_container_rgb`] = a255s(rootModule.containerContrast);
201
- lessVariables[`@color_${name}_container_inv`] = Pacem.Colors.stringify(rootModule.container);
202
- lessVariables[`@color_${name}_container_inv_rgb`] = a255s(rootModule.container);
203
- lessVariables[`@color_${name}_active`] = Pacem.Colors.stringify(rootModule.activeContrast);
204
- lessVariables[`@color_${name}_active_rgb`] = a255s(rootModule.activeContrast);
205
- lessVariables[`@color_${name}_active_inv`] = Pacem.Colors.stringify(rootModule.active);
206
- lessVariables[`@color_${name}_active_inv_rgb`] = a255s(rootModule.active);
207
- lessVariables[`@color_${name}_container_active`] = Pacem.Colors.stringify(rootModule.containerActiveContrast);
208
- lessVariables[`@color_${name}_container_active_rgb`] = a255s(rootModule.containerActiveContrast);
209
- lessVariables[`@color_${name}_container_active_inv`] = Pacem.Colors.stringify(rootModule.containerActive);
210
- lessVariables[`@color_${name}_container_active_inv_rgb`] = a255s(rootModule.containerActive);
211
- lessVariables[`@color_${name}_emphasis`] = lessVariables[`@color_${name}`];
212
- lessVariables[`@color_${name}_emphasis_rgb`] = lessVariables[`@color_${name}_rgb`];
213
- }
229
+ const disabledColor = contrastColor(backgroundModule.base, WCAG_DISABLED_CONTRAST);
230
+ const disabledContrastColor = contrastColor(disabledColor, WCAG_DISABLED_CONTRAST * (darkTheme ? 1 : .9), !darkTheme);
231
+ pushLessVariables('disabled', colorModule(disabledColor, 'disabled', disabledContrastColor));
232
+ }
233
+ if (!('invert' in palette)) {
234
+ pushLessVariables('invert', invertModule(backgroundModule));
214
235
  }
215
236
  let lessText = '@palette_prebuilt: true;\r\n';
216
237
  for (let name in lessVariables) {