@symbo.ls/scratch 3.8.1 → 3.8.7

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/dist/cjs/set.js CHANGED
@@ -101,14 +101,37 @@ const setEach = (factoryName, props) => {
101
101
  const changeGlobalTheme = (newTheme) => {
102
102
  const CONFIG = (0, import_factory.getActiveConfig)();
103
103
  CONFIG.globalTheme = newTheme;
104
+ if (typeof document !== "undefined" && newTheme && newTheme !== "auto") {
105
+ document.documentElement.setAttribute("data-theme", newTheme);
106
+ }
104
107
  for (const key in CONFIG.CSS_VARS) {
105
108
  if (key.startsWith("--theme-")) delete CONFIG.CSS_VARS[key];
106
109
  }
107
110
  for (const key in CONFIG.CSS_MEDIA_VARS) {
108
111
  delete CONFIG.CSS_MEDIA_VARS[key];
109
112
  }
110
- if (CONFIG.theme) {
111
- setEach("theme", CONFIG.theme);
113
+ const source = CONFIG._originalTheme || CONFIG.theme;
114
+ if (source) {
115
+ const fresh = JSON.parse(JSON.stringify(source));
116
+ CONFIG.theme = fresh;
117
+ setEach("theme", fresh);
118
+ }
119
+ if (typeof document !== "undefined" && CONFIG.CSS_VARS) {
120
+ const sheets = document.styleSheets;
121
+ for (let i = 0; i < sheets.length; i++) {
122
+ try {
123
+ const rules = sheets[i].cssRules;
124
+ for (let j = 0; j < rules.length; j++) {
125
+ if (rules[j].selectorText === ":root") {
126
+ for (const key in CONFIG.CSS_VARS) {
127
+ rules[j].style.setProperty(key, CONFIG.CSS_VARS[key]);
128
+ }
129
+ return CONFIG;
130
+ }
131
+ }
132
+ } catch (e) {
133
+ }
134
+ }
112
135
  }
113
136
  return CONFIG;
114
137
  };
@@ -155,6 +178,9 @@ const set = (recivedConfig, options = SET_OPTIONS) => {
155
178
  CONFIG.SEMANTIC_ICONS = CONFIG.semantic_icons;
156
179
  }
157
180
  if (CONFIG.verbose) console.log(CONFIG);
181
+ if (typeof document !== "undefined" && CONFIG.globalTheme && CONFIG.globalTheme !== "auto") {
182
+ document.documentElement.setAttribute("data-theme", CONFIG.globalTheme);
183
+ }
158
184
  if (!CONFIG.__svg_cache) CONFIG.__svg_cache = {};
159
185
  const keys = Object.keys(config);
160
186
  const keySet = new Set(keys);
@@ -164,6 +190,9 @@ const set = (recivedConfig, options = SET_OPTIONS) => {
164
190
  (0, import_utils.deepMerge)(config[lower], config[key]);
165
191
  }
166
192
  });
193
+ if (config.theme && !CONFIG._originalTheme) {
194
+ CONFIG._originalTheme = JSON.parse(JSON.stringify(config.theme));
195
+ }
167
196
  keys.map((key) => {
168
197
  const lower = key.toLowerCase();
169
198
  if (lower !== key && keySet.has(lower)) return;
@@ -28,6 +28,163 @@ var import_color = require("./color");
28
28
  var import_factory = require("../factory.js");
29
29
  var import_color2 = require("../utils/color.js");
30
30
  var import_utils = require("@domql/utils");
31
+ const CSS_NAMED_COLORS = /* @__PURE__ */ new Set([
32
+ "black",
33
+ "white",
34
+ "red",
35
+ "green",
36
+ "blue",
37
+ "yellow",
38
+ "orange",
39
+ "purple",
40
+ "pink",
41
+ "brown",
42
+ "gray",
43
+ "grey",
44
+ "cyan",
45
+ "magenta",
46
+ "lime",
47
+ "olive",
48
+ "navy",
49
+ "teal",
50
+ "aqua",
51
+ "maroon",
52
+ "silver",
53
+ "fuchsia",
54
+ "transparent",
55
+ "currentColor",
56
+ "currentcolor",
57
+ "inherit",
58
+ "initial",
59
+ "unset",
60
+ "none",
61
+ "aliceblue",
62
+ "antiquewhite",
63
+ "aquamarine",
64
+ "azure",
65
+ "beige",
66
+ "bisque",
67
+ "blanchedalmond",
68
+ "blueviolet",
69
+ "burlywood",
70
+ "cadetblue",
71
+ "chartreuse",
72
+ "chocolate",
73
+ "coral",
74
+ "cornflowerblue",
75
+ "cornsilk",
76
+ "crimson",
77
+ "darkblue",
78
+ "darkcyan",
79
+ "darkgoldenrod",
80
+ "darkgray",
81
+ "darkgreen",
82
+ "darkgrey",
83
+ "darkkhaki",
84
+ "darkmagenta",
85
+ "darkolivegreen",
86
+ "darkorange",
87
+ "darkorchid",
88
+ "darkred",
89
+ "darksalmon",
90
+ "darkseagreen",
91
+ "darkslateblue",
92
+ "darkslategray",
93
+ "darkslategrey",
94
+ "darkturquoise",
95
+ "darkviolet",
96
+ "deeppink",
97
+ "deepskyblue",
98
+ "dimgray",
99
+ "dimgrey",
100
+ "dodgerblue",
101
+ "firebrick",
102
+ "floralwhite",
103
+ "forestgreen",
104
+ "gainsboro",
105
+ "ghostwhite",
106
+ "gold",
107
+ "goldenrod",
108
+ "greenyellow",
109
+ "honeydew",
110
+ "hotpink",
111
+ "indianred",
112
+ "indigo",
113
+ "ivory",
114
+ "khaki",
115
+ "lavender",
116
+ "lavenderblush",
117
+ "lawngreen",
118
+ "lemonchiffon",
119
+ "lightblue",
120
+ "lightcoral",
121
+ "lightcyan",
122
+ "lightgoldenrodyellow",
123
+ "lightgray",
124
+ "lightgreen",
125
+ "lightgrey",
126
+ "lightpink",
127
+ "lightsalmon",
128
+ "lightseagreen",
129
+ "lightskyblue",
130
+ "lightslategray",
131
+ "lightslategrey",
132
+ "lightsteelblue",
133
+ "lightyellow",
134
+ "limegreen",
135
+ "linen",
136
+ "mediumaquamarine",
137
+ "mediumblue",
138
+ "mediumorchid",
139
+ "mediumpurple",
140
+ "mediumseagreen",
141
+ "mediumslateblue",
142
+ "mediumspringgreen",
143
+ "mediumturquoise",
144
+ "mediumvioletred",
145
+ "midnightblue",
146
+ "mintcream",
147
+ "mistyrose",
148
+ "moccasin",
149
+ "navajowhite",
150
+ "oldlace",
151
+ "olivedrab",
152
+ "orangered",
153
+ "orchid",
154
+ "palegoldenrod",
155
+ "palegreen",
156
+ "paleturquoise",
157
+ "palevioletred",
158
+ "papayawhip",
159
+ "peachpuff",
160
+ "peru",
161
+ "plum",
162
+ "powderblue",
163
+ "rosybrown",
164
+ "royalblue",
165
+ "saddlebrown",
166
+ "salmon",
167
+ "sandybrown",
168
+ "seagreen",
169
+ "seashell",
170
+ "sienna",
171
+ "skyblue",
172
+ "slateblue",
173
+ "slategray",
174
+ "slategrey",
175
+ "snow",
176
+ "springgreen",
177
+ "steelblue",
178
+ "tan",
179
+ "thistle",
180
+ "tomato",
181
+ "turquoise",
182
+ "violet",
183
+ "wheat",
184
+ "whitesmoke",
185
+ "yellowgreen",
186
+ "rebeccapurple"
187
+ ]);
31
188
  const setThemeValue = (theme) => {
32
189
  const value = {};
33
190
  const { state, media, helpers, ...rest } = theme;
@@ -151,6 +308,23 @@ const generateAutoVars = (schemes, varPrefix, CONFIG) => {
151
308
  for (const scheme in schemes) {
152
309
  if (schemes[scheme]) for (const k of Object.keys(schemes[scheme])) allKeys.add(k);
153
310
  }
311
+ const brokenSchemes = /* @__PURE__ */ new Set();
312
+ if (globalTheme === "auto") {
313
+ for (const param of allKeys) {
314
+ const symb = param.slice(0, 1);
315
+ if (symb === "@" || symb === "." || symb === ":") continue;
316
+ for (const scheme in schemes) {
317
+ if (brokenSchemes.has(scheme)) continue;
318
+ const val = schemes[scheme]?.[param];
319
+ if (val === void 0) continue;
320
+ const color = (0, import_color.getColor)(val, `@${scheme}`);
321
+ if (color === void 0) continue;
322
+ if ((0, import_utils.isString)(color) && /^[a-z][a-zA-Z]+$/.test(color) && !CSS_NAMED_COLORS.has(color)) {
323
+ brokenSchemes.add(scheme);
324
+ }
325
+ }
326
+ }
327
+ }
154
328
  for (const param of allKeys) {
155
329
  const symb = param.slice(0, 1);
156
330
  const hasObject = Object.values(schemes).some((s) => (0, import_utils.isObjectLike)(s?.[param]));
@@ -171,11 +345,16 @@ const generateAutoVars = (schemes, varPrefix, CONFIG) => {
171
345
  } else if (symb !== "@" && symb !== "." && symb !== ":") {
172
346
  const autoVar = `--theme-${varPrefix}-${param}`;
173
347
  if (globalTheme === "auto") {
348
+ let fallbackColor;
174
349
  for (const scheme in schemes) {
350
+ if (brokenSchemes.has(scheme)) continue;
175
351
  const val = schemes[scheme]?.[param];
176
352
  if (val === void 0) continue;
177
353
  const color = (0, import_color.getColor)(val, `@${scheme}`);
178
354
  if (color === void 0) continue;
355
+ if (scheme === "light" || fallbackColor === void 0) {
356
+ fallbackColor = color;
357
+ }
179
358
  const selector = `[data-theme="${scheme}"]`;
180
359
  if (!MEDIA_VARS[selector]) MEDIA_VARS[selector] = {};
181
360
  MEDIA_VARS[selector][autoVar] = color;
@@ -185,6 +364,9 @@ const generateAutoVars = (schemes, varPrefix, CONFIG) => {
185
364
  MEDIA_VARS[mq][autoVar] = color;
186
365
  }
187
366
  }
367
+ if (fallbackColor !== void 0) {
368
+ CSS_VARS[autoVar] = fallbackColor;
369
+ }
188
370
  } else {
189
371
  const forced = String(globalTheme).replace(/^'|'$/g, "");
190
372
  const source = schemes[forced]?.[param];
package/dist/esm/set.js CHANGED
@@ -88,14 +88,37 @@ const setEach = (factoryName, props) => {
88
88
  const changeGlobalTheme = (newTheme) => {
89
89
  const CONFIG = getActiveConfig();
90
90
  CONFIG.globalTheme = newTheme;
91
+ if (typeof document !== "undefined" && newTheme && newTheme !== "auto") {
92
+ document.documentElement.setAttribute("data-theme", newTheme);
93
+ }
91
94
  for (const key in CONFIG.CSS_VARS) {
92
95
  if (key.startsWith("--theme-")) delete CONFIG.CSS_VARS[key];
93
96
  }
94
97
  for (const key in CONFIG.CSS_MEDIA_VARS) {
95
98
  delete CONFIG.CSS_MEDIA_VARS[key];
96
99
  }
97
- if (CONFIG.theme) {
98
- setEach("theme", CONFIG.theme);
100
+ const source = CONFIG._originalTheme || CONFIG.theme;
101
+ if (source) {
102
+ const fresh = JSON.parse(JSON.stringify(source));
103
+ CONFIG.theme = fresh;
104
+ setEach("theme", fresh);
105
+ }
106
+ if (typeof document !== "undefined" && CONFIG.CSS_VARS) {
107
+ const sheets = document.styleSheets;
108
+ for (let i = 0; i < sheets.length; i++) {
109
+ try {
110
+ const rules = sheets[i].cssRules;
111
+ for (let j = 0; j < rules.length; j++) {
112
+ if (rules[j].selectorText === ":root") {
113
+ for (const key in CONFIG.CSS_VARS) {
114
+ rules[j].style.setProperty(key, CONFIG.CSS_VARS[key]);
115
+ }
116
+ return CONFIG;
117
+ }
118
+ }
119
+ } catch (e) {
120
+ }
121
+ }
99
122
  }
100
123
  return CONFIG;
101
124
  };
@@ -142,6 +165,9 @@ const set = (recivedConfig, options = SET_OPTIONS) => {
142
165
  CONFIG.SEMANTIC_ICONS = CONFIG.semantic_icons;
143
166
  }
144
167
  if (CONFIG.verbose) console.log(CONFIG);
168
+ if (typeof document !== "undefined" && CONFIG.globalTheme && CONFIG.globalTheme !== "auto") {
169
+ document.documentElement.setAttribute("data-theme", CONFIG.globalTheme);
170
+ }
145
171
  if (!CONFIG.__svg_cache) CONFIG.__svg_cache = {};
146
172
  const keys = Object.keys(config);
147
173
  const keySet = new Set(keys);
@@ -151,6 +177,9 @@ const set = (recivedConfig, options = SET_OPTIONS) => {
151
177
  deepMerge(config[lower], config[key]);
152
178
  }
153
179
  });
180
+ if (config.theme && !CONFIG._originalTheme) {
181
+ CONFIG._originalTheme = JSON.parse(JSON.stringify(config.theme));
182
+ }
154
183
  keys.map((key) => {
155
184
  const lower = key.toLowerCase();
156
185
  if (lower !== key && keySet.has(lower)) return;
@@ -7,6 +7,163 @@ import {
7
7
  isObjectLike,
8
8
  isArray
9
9
  } from "@domql/utils";
10
+ const CSS_NAMED_COLORS = /* @__PURE__ */ new Set([
11
+ "black",
12
+ "white",
13
+ "red",
14
+ "green",
15
+ "blue",
16
+ "yellow",
17
+ "orange",
18
+ "purple",
19
+ "pink",
20
+ "brown",
21
+ "gray",
22
+ "grey",
23
+ "cyan",
24
+ "magenta",
25
+ "lime",
26
+ "olive",
27
+ "navy",
28
+ "teal",
29
+ "aqua",
30
+ "maroon",
31
+ "silver",
32
+ "fuchsia",
33
+ "transparent",
34
+ "currentColor",
35
+ "currentcolor",
36
+ "inherit",
37
+ "initial",
38
+ "unset",
39
+ "none",
40
+ "aliceblue",
41
+ "antiquewhite",
42
+ "aquamarine",
43
+ "azure",
44
+ "beige",
45
+ "bisque",
46
+ "blanchedalmond",
47
+ "blueviolet",
48
+ "burlywood",
49
+ "cadetblue",
50
+ "chartreuse",
51
+ "chocolate",
52
+ "coral",
53
+ "cornflowerblue",
54
+ "cornsilk",
55
+ "crimson",
56
+ "darkblue",
57
+ "darkcyan",
58
+ "darkgoldenrod",
59
+ "darkgray",
60
+ "darkgreen",
61
+ "darkgrey",
62
+ "darkkhaki",
63
+ "darkmagenta",
64
+ "darkolivegreen",
65
+ "darkorange",
66
+ "darkorchid",
67
+ "darkred",
68
+ "darksalmon",
69
+ "darkseagreen",
70
+ "darkslateblue",
71
+ "darkslategray",
72
+ "darkslategrey",
73
+ "darkturquoise",
74
+ "darkviolet",
75
+ "deeppink",
76
+ "deepskyblue",
77
+ "dimgray",
78
+ "dimgrey",
79
+ "dodgerblue",
80
+ "firebrick",
81
+ "floralwhite",
82
+ "forestgreen",
83
+ "gainsboro",
84
+ "ghostwhite",
85
+ "gold",
86
+ "goldenrod",
87
+ "greenyellow",
88
+ "honeydew",
89
+ "hotpink",
90
+ "indianred",
91
+ "indigo",
92
+ "ivory",
93
+ "khaki",
94
+ "lavender",
95
+ "lavenderblush",
96
+ "lawngreen",
97
+ "lemonchiffon",
98
+ "lightblue",
99
+ "lightcoral",
100
+ "lightcyan",
101
+ "lightgoldenrodyellow",
102
+ "lightgray",
103
+ "lightgreen",
104
+ "lightgrey",
105
+ "lightpink",
106
+ "lightsalmon",
107
+ "lightseagreen",
108
+ "lightskyblue",
109
+ "lightslategray",
110
+ "lightslategrey",
111
+ "lightsteelblue",
112
+ "lightyellow",
113
+ "limegreen",
114
+ "linen",
115
+ "mediumaquamarine",
116
+ "mediumblue",
117
+ "mediumorchid",
118
+ "mediumpurple",
119
+ "mediumseagreen",
120
+ "mediumslateblue",
121
+ "mediumspringgreen",
122
+ "mediumturquoise",
123
+ "mediumvioletred",
124
+ "midnightblue",
125
+ "mintcream",
126
+ "mistyrose",
127
+ "moccasin",
128
+ "navajowhite",
129
+ "oldlace",
130
+ "olivedrab",
131
+ "orangered",
132
+ "orchid",
133
+ "palegoldenrod",
134
+ "palegreen",
135
+ "paleturquoise",
136
+ "palevioletred",
137
+ "papayawhip",
138
+ "peachpuff",
139
+ "peru",
140
+ "plum",
141
+ "powderblue",
142
+ "rosybrown",
143
+ "royalblue",
144
+ "saddlebrown",
145
+ "salmon",
146
+ "sandybrown",
147
+ "seagreen",
148
+ "seashell",
149
+ "sienna",
150
+ "skyblue",
151
+ "slateblue",
152
+ "slategray",
153
+ "slategrey",
154
+ "snow",
155
+ "springgreen",
156
+ "steelblue",
157
+ "tan",
158
+ "thistle",
159
+ "tomato",
160
+ "turquoise",
161
+ "violet",
162
+ "wheat",
163
+ "whitesmoke",
164
+ "yellowgreen",
165
+ "rebeccapurple"
166
+ ]);
10
167
  const setThemeValue = (theme) => {
11
168
  const value = {};
12
169
  const { state, media, helpers, ...rest } = theme;
@@ -130,6 +287,23 @@ const generateAutoVars = (schemes, varPrefix, CONFIG) => {
130
287
  for (const scheme in schemes) {
131
288
  if (schemes[scheme]) for (const k of Object.keys(schemes[scheme])) allKeys.add(k);
132
289
  }
290
+ const brokenSchemes = /* @__PURE__ */ new Set();
291
+ if (globalTheme === "auto") {
292
+ for (const param of allKeys) {
293
+ const symb = param.slice(0, 1);
294
+ if (symb === "@" || symb === "." || symb === ":") continue;
295
+ for (const scheme in schemes) {
296
+ if (brokenSchemes.has(scheme)) continue;
297
+ const val = schemes[scheme]?.[param];
298
+ if (val === void 0) continue;
299
+ const color = getColor(val, `@${scheme}`);
300
+ if (color === void 0) continue;
301
+ if (isString(color) && /^[a-z][a-zA-Z]+$/.test(color) && !CSS_NAMED_COLORS.has(color)) {
302
+ brokenSchemes.add(scheme);
303
+ }
304
+ }
305
+ }
306
+ }
133
307
  for (const param of allKeys) {
134
308
  const symb = param.slice(0, 1);
135
309
  const hasObject = Object.values(schemes).some((s) => isObjectLike(s?.[param]));
@@ -150,11 +324,16 @@ const generateAutoVars = (schemes, varPrefix, CONFIG) => {
150
324
  } else if (symb !== "@" && symb !== "." && symb !== ":") {
151
325
  const autoVar = `--theme-${varPrefix}-${param}`;
152
326
  if (globalTheme === "auto") {
327
+ let fallbackColor;
153
328
  for (const scheme in schemes) {
329
+ if (brokenSchemes.has(scheme)) continue;
154
330
  const val = schemes[scheme]?.[param];
155
331
  if (val === void 0) continue;
156
332
  const color = getColor(val, `@${scheme}`);
157
333
  if (color === void 0) continue;
334
+ if (scheme === "light" || fallbackColor === void 0) {
335
+ fallbackColor = color;
336
+ }
158
337
  const selector = `[data-theme="${scheme}"]`;
159
338
  if (!MEDIA_VARS[selector]) MEDIA_VARS[selector] = {};
160
339
  MEDIA_VARS[selector][autoVar] = color;
@@ -164,6 +343,9 @@ const generateAutoVars = (schemes, varPrefix, CONFIG) => {
164
343
  MEDIA_VARS[mq][autoVar] = color;
165
344
  }
166
345
  }
346
+ if (fallbackColor !== void 0) {
347
+ CSS_VARS[autoVar] = fallbackColor;
348
+ }
167
349
  } else {
168
350
  const forced = String(globalTheme).replace(/^'|'$/g, "");
169
351
  const source = schemes[forced]?.[param];
@@ -182,6 +182,7 @@ var SmblsScratch = (() => {
182
182
  exec = (param, element, state, context) => {
183
183
  if (isFunction(param)) {
184
184
  if (!element) return;
185
+ if (typeof param.call !== "function") return param;
185
186
  const result = param.call(
186
187
  element,
187
188
  element,
@@ -197,6 +198,17 @@ var SmblsScratch = (() => {
197
198
  }
198
199
  return result;
199
200
  }
201
+ if (param != null && element?.context?.plugins && (isArray(param) || isObject(param) && !isDOMNode(param))) {
202
+ const plugins = element.context.plugins;
203
+ for (const plugin of plugins) {
204
+ if (plugin.resolveHandler) {
205
+ const resolved = plugin.resolveHandler(param, element);
206
+ if (typeof resolved === "function") {
207
+ return exec(resolved, element, state, context);
208
+ }
209
+ }
210
+ }
211
+ }
200
212
  return param;
201
213
  };
202
214
  merge = (element, obj, excludeFrom = []) => {
@@ -964,9 +976,10 @@ var SmblsScratch = (() => {
964
976
  // ../smbls-utils/dist/esm/index.js
965
977
  init_esm();
966
978
  var toCamelCase = (str) => {
979
+ if (typeof str !== "string") str = String(str || "");
967
980
  return str.replace(/(?:^\w|[A-Z]|\b\w)/g, function(word, index) {
968
981
  return index === 0 ? word.toLowerCase() : word.toUpperCase();
969
- }).replaceAll(/\s+/g, "");
982
+ }).replace(/\s+/g, "");
970
983
  };
971
984
  var toDashCase = (val) => val.replace(/[^a-zA-Z0-9]/g, " ").trim().toLowerCase().replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
972
985
  var arrayzeValue = (val) => {
@@ -1867,6 +1880,163 @@ var SmblsScratch = (() => {
1867
1880
 
1868
1881
  // src/system/theme.js
1869
1882
  init_esm();
1883
+ var CSS_NAMED_COLORS = /* @__PURE__ */ new Set([
1884
+ "black",
1885
+ "white",
1886
+ "red",
1887
+ "green",
1888
+ "blue",
1889
+ "yellow",
1890
+ "orange",
1891
+ "purple",
1892
+ "pink",
1893
+ "brown",
1894
+ "gray",
1895
+ "grey",
1896
+ "cyan",
1897
+ "magenta",
1898
+ "lime",
1899
+ "olive",
1900
+ "navy",
1901
+ "teal",
1902
+ "aqua",
1903
+ "maroon",
1904
+ "silver",
1905
+ "fuchsia",
1906
+ "transparent",
1907
+ "currentColor",
1908
+ "currentcolor",
1909
+ "inherit",
1910
+ "initial",
1911
+ "unset",
1912
+ "none",
1913
+ "aliceblue",
1914
+ "antiquewhite",
1915
+ "aquamarine",
1916
+ "azure",
1917
+ "beige",
1918
+ "bisque",
1919
+ "blanchedalmond",
1920
+ "blueviolet",
1921
+ "burlywood",
1922
+ "cadetblue",
1923
+ "chartreuse",
1924
+ "chocolate",
1925
+ "coral",
1926
+ "cornflowerblue",
1927
+ "cornsilk",
1928
+ "crimson",
1929
+ "darkblue",
1930
+ "darkcyan",
1931
+ "darkgoldenrod",
1932
+ "darkgray",
1933
+ "darkgreen",
1934
+ "darkgrey",
1935
+ "darkkhaki",
1936
+ "darkmagenta",
1937
+ "darkolivegreen",
1938
+ "darkorange",
1939
+ "darkorchid",
1940
+ "darkred",
1941
+ "darksalmon",
1942
+ "darkseagreen",
1943
+ "darkslateblue",
1944
+ "darkslategray",
1945
+ "darkslategrey",
1946
+ "darkturquoise",
1947
+ "darkviolet",
1948
+ "deeppink",
1949
+ "deepskyblue",
1950
+ "dimgray",
1951
+ "dimgrey",
1952
+ "dodgerblue",
1953
+ "firebrick",
1954
+ "floralwhite",
1955
+ "forestgreen",
1956
+ "gainsboro",
1957
+ "ghostwhite",
1958
+ "gold",
1959
+ "goldenrod",
1960
+ "greenyellow",
1961
+ "honeydew",
1962
+ "hotpink",
1963
+ "indianred",
1964
+ "indigo",
1965
+ "ivory",
1966
+ "khaki",
1967
+ "lavender",
1968
+ "lavenderblush",
1969
+ "lawngreen",
1970
+ "lemonchiffon",
1971
+ "lightblue",
1972
+ "lightcoral",
1973
+ "lightcyan",
1974
+ "lightgoldenrodyellow",
1975
+ "lightgray",
1976
+ "lightgreen",
1977
+ "lightgrey",
1978
+ "lightpink",
1979
+ "lightsalmon",
1980
+ "lightseagreen",
1981
+ "lightskyblue",
1982
+ "lightslategray",
1983
+ "lightslategrey",
1984
+ "lightsteelblue",
1985
+ "lightyellow",
1986
+ "limegreen",
1987
+ "linen",
1988
+ "mediumaquamarine",
1989
+ "mediumblue",
1990
+ "mediumorchid",
1991
+ "mediumpurple",
1992
+ "mediumseagreen",
1993
+ "mediumslateblue",
1994
+ "mediumspringgreen",
1995
+ "mediumturquoise",
1996
+ "mediumvioletred",
1997
+ "midnightblue",
1998
+ "mintcream",
1999
+ "mistyrose",
2000
+ "moccasin",
2001
+ "navajowhite",
2002
+ "oldlace",
2003
+ "olivedrab",
2004
+ "orangered",
2005
+ "orchid",
2006
+ "palegoldenrod",
2007
+ "palegreen",
2008
+ "paleturquoise",
2009
+ "palevioletred",
2010
+ "papayawhip",
2011
+ "peachpuff",
2012
+ "peru",
2013
+ "plum",
2014
+ "powderblue",
2015
+ "rosybrown",
2016
+ "royalblue",
2017
+ "saddlebrown",
2018
+ "salmon",
2019
+ "sandybrown",
2020
+ "seagreen",
2021
+ "seashell",
2022
+ "sienna",
2023
+ "skyblue",
2024
+ "slateblue",
2025
+ "slategray",
2026
+ "slategrey",
2027
+ "snow",
2028
+ "springgreen",
2029
+ "steelblue",
2030
+ "tan",
2031
+ "thistle",
2032
+ "tomato",
2033
+ "turquoise",
2034
+ "violet",
2035
+ "wheat",
2036
+ "whitesmoke",
2037
+ "yellowgreen",
2038
+ "rebeccapurple"
2039
+ ]);
1870
2040
  var setThemeValue = (theme2) => {
1871
2041
  const value = {};
1872
2042
  const { state, media: media2, helpers, ...rest } = theme2;
@@ -1984,6 +2154,23 @@ var SmblsScratch = (() => {
1984
2154
  for (const scheme in schemes) {
1985
2155
  if (schemes[scheme]) for (const k of Object.keys(schemes[scheme])) allKeys.add(k);
1986
2156
  }
2157
+ const brokenSchemes = /* @__PURE__ */ new Set();
2158
+ if (globalTheme === "auto") {
2159
+ for (const param of allKeys) {
2160
+ const symb = param.slice(0, 1);
2161
+ if (symb === "@" || symb === "." || symb === ":") continue;
2162
+ for (const scheme in schemes) {
2163
+ if (brokenSchemes.has(scheme)) continue;
2164
+ const val = schemes[scheme]?.[param];
2165
+ if (val === void 0) continue;
2166
+ const color2 = getColor(val, `@${scheme}`);
2167
+ if (color2 === void 0) continue;
2168
+ if (isString(color2) && /^[a-z][a-zA-Z]+$/.test(color2) && !CSS_NAMED_COLORS.has(color2)) {
2169
+ brokenSchemes.add(scheme);
2170
+ }
2171
+ }
2172
+ }
2173
+ }
1987
2174
  for (const param of allKeys) {
1988
2175
  const symb = param.slice(0, 1);
1989
2176
  const hasObject = Object.values(schemes).some((s) => isObjectLike(s?.[param]));
@@ -2004,11 +2191,16 @@ var SmblsScratch = (() => {
2004
2191
  } else if (symb !== "@" && symb !== "." && symb !== ":") {
2005
2192
  const autoVar = `--theme-${varPrefix}-${param}`;
2006
2193
  if (globalTheme === "auto") {
2194
+ let fallbackColor;
2007
2195
  for (const scheme in schemes) {
2196
+ if (brokenSchemes.has(scheme)) continue;
2008
2197
  const val = schemes[scheme]?.[param];
2009
2198
  if (val === void 0) continue;
2010
2199
  const color2 = getColor(val, `@${scheme}`);
2011
2200
  if (color2 === void 0) continue;
2201
+ if (scheme === "light" || fallbackColor === void 0) {
2202
+ fallbackColor = color2;
2203
+ }
2012
2204
  const selector = `[data-theme="${scheme}"]`;
2013
2205
  if (!MEDIA_VARS[selector]) MEDIA_VARS[selector] = {};
2014
2206
  MEDIA_VARS[selector][autoVar] = color2;
@@ -2018,6 +2210,9 @@ var SmblsScratch = (() => {
2018
2210
  MEDIA_VARS[mq][autoVar] = color2;
2019
2211
  }
2020
2212
  }
2213
+ if (fallbackColor !== void 0) {
2214
+ CSS_VARS2[autoVar] = fallbackColor;
2215
+ }
2021
2216
  } else {
2022
2217
  const forced = String(globalTheme).replace(/^'|'$/g, "");
2023
2218
  const source = schemes[forced]?.[param];
@@ -2873,14 +3068,37 @@ var SmblsScratch = (() => {
2873
3068
  var changeGlobalTheme = (newTheme) => {
2874
3069
  const CONFIG2 = getActiveConfig();
2875
3070
  CONFIG2.globalTheme = newTheme;
3071
+ if (typeof document !== "undefined" && newTheme && newTheme !== "auto") {
3072
+ document.documentElement.setAttribute("data-theme", newTheme);
3073
+ }
2876
3074
  for (const key in CONFIG2.CSS_VARS) {
2877
3075
  if (key.startsWith("--theme-")) delete CONFIG2.CSS_VARS[key];
2878
3076
  }
2879
3077
  for (const key in CONFIG2.CSS_MEDIA_VARS) {
2880
3078
  delete CONFIG2.CSS_MEDIA_VARS[key];
2881
3079
  }
2882
- if (CONFIG2.theme) {
2883
- setEach("theme", CONFIG2.theme);
3080
+ const source = CONFIG2._originalTheme || CONFIG2.theme;
3081
+ if (source) {
3082
+ const fresh = JSON.parse(JSON.stringify(source));
3083
+ CONFIG2.theme = fresh;
3084
+ setEach("theme", fresh);
3085
+ }
3086
+ if (typeof document !== "undefined" && CONFIG2.CSS_VARS) {
3087
+ const sheets = document.styleSheets;
3088
+ for (let i = 0; i < sheets.length; i++) {
3089
+ try {
3090
+ const rules = sheets[i].cssRules;
3091
+ for (let j = 0; j < rules.length; j++) {
3092
+ if (rules[j].selectorText === ":root") {
3093
+ for (const key in CONFIG2.CSS_VARS) {
3094
+ rules[j].style.setProperty(key, CONFIG2.CSS_VARS[key]);
3095
+ }
3096
+ return CONFIG2;
3097
+ }
3098
+ }
3099
+ } catch (e) {
3100
+ }
3101
+ }
2884
3102
  }
2885
3103
  return CONFIG2;
2886
3104
  };
@@ -2927,6 +3145,9 @@ var SmblsScratch = (() => {
2927
3145
  CONFIG2.SEMANTIC_ICONS = CONFIG2.semantic_icons;
2928
3146
  }
2929
3147
  if (CONFIG2.verbose) console.log(CONFIG2);
3148
+ if (typeof document !== "undefined" && CONFIG2.globalTheme && CONFIG2.globalTheme !== "auto") {
3149
+ document.documentElement.setAttribute("data-theme", CONFIG2.globalTheme);
3150
+ }
2930
3151
  if (!CONFIG2.__svg_cache) CONFIG2.__svg_cache = {};
2931
3152
  const keys = Object.keys(config);
2932
3153
  const keySet = new Set(keys);
@@ -2936,6 +3157,9 @@ var SmblsScratch = (() => {
2936
3157
  deepMerge(config[lower], config[key]);
2937
3158
  }
2938
3159
  });
3160
+ if (config.theme && !CONFIG2._originalTheme) {
3161
+ CONFIG2._originalTheme = JSON.parse(JSON.stringify(config.theme));
3162
+ }
2939
3163
  keys.map((key) => {
2940
3164
  const lower = key.toLowerCase();
2941
3165
  if (lower !== key && keySet.has(lower)) return;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@symbo.ls/scratch",
3
3
  "description": "Φ / CSS framework and methodology.",
4
4
  "author": "symbo.ls",
5
- "version": "3.8.1",
5
+ "version": "3.8.7",
6
6
  "files": [
7
7
  "dist",
8
8
  "*.js",
@@ -33,8 +33,8 @@
33
33
  "prepublish": "npm run build && npm run copy:package:cjs"
34
34
  },
35
35
  "dependencies": {
36
- "@domql/utils": "^3.8.1",
37
- "@symbo.ls/smbls-utils": "^3.8.1",
36
+ "@domql/utils": "^3.8.7",
37
+ "@symbo.ls/smbls-utils": "^3.8.7",
38
38
  "color-contrast-checker": "^1.5.0"
39
39
  },
40
40
  "gitHead": "9fc1b79b41cdc725ca6b24aec64920a599634681",
package/src/set.js CHANGED
@@ -109,6 +109,11 @@ export const changeGlobalTheme = (newTheme) => {
109
109
  const CONFIG = getActiveConfig()
110
110
  CONFIG.globalTheme = newTheme
111
111
 
112
+ // Set data-theme attribute on document element for CSS selector-based theming
113
+ if (typeof document !== 'undefined' && newTheme && newTheme !== 'auto') {
114
+ document.documentElement.setAttribute('data-theme', newTheme)
115
+ }
116
+
112
117
  // Clear theme-related CSS vars
113
118
  for (const key in CONFIG.CSS_VARS) {
114
119
  if (key.startsWith('--theme-')) delete CONFIG.CSS_VARS[key]
@@ -119,9 +124,31 @@ export const changeGlobalTheme = (newTheme) => {
119
124
  delete CONFIG.CSS_MEDIA_VARS[key]
120
125
  }
121
126
 
122
- // Re-process all themes with the new globalTheme
123
- if (CONFIG.theme) {
124
- setEach('theme', CONFIG.theme)
127
+ // Re-process themes from original definitions (not mutated processed ones)
128
+ const source = CONFIG._originalTheme || CONFIG.theme
129
+ if (source) {
130
+ // Deep clone to avoid re-mutating the originals
131
+ const fresh = JSON.parse(JSON.stringify(source))
132
+ CONFIG.theme = fresh
133
+ setEach('theme', fresh)
134
+ }
135
+
136
+ // Apply updated CSS vars to the existing :root stylesheet rule
137
+ if (typeof document !== 'undefined' && CONFIG.CSS_VARS) {
138
+ const sheets = document.styleSheets
139
+ for (let i = 0; i < sheets.length; i++) {
140
+ try {
141
+ const rules = sheets[i].cssRules
142
+ for (let j = 0; j < rules.length; j++) {
143
+ if (rules[j].selectorText === ':root') {
144
+ for (const key in CONFIG.CSS_VARS) {
145
+ rules[j].style.setProperty(key, CONFIG.CSS_VARS[key])
146
+ }
147
+ return CONFIG
148
+ }
149
+ }
150
+ } catch (e) { /* cross-origin stylesheet */ }
151
+ }
125
152
  }
126
153
 
127
154
  return CONFIG
@@ -174,6 +201,11 @@ export const set = (recivedConfig, options = SET_OPTIONS) => {
174
201
  }
175
202
  if (CONFIG.verbose) console.log(CONFIG)
176
203
 
204
+ // Set data-theme attribute on document for CSS selector-based theming
205
+ if (typeof document !== 'undefined' && CONFIG.globalTheme && CONFIG.globalTheme !== 'auto') {
206
+ document.documentElement.setAttribute('data-theme', CONFIG.globalTheme)
207
+ }
208
+
177
209
  if (!CONFIG.__svg_cache) CONFIG.__svg_cache = {}
178
210
 
179
211
  const keys = Object.keys(config)
@@ -187,6 +219,11 @@ export const set = (recivedConfig, options = SET_OPTIONS) => {
187
219
  }
188
220
  })
189
221
 
222
+ // Store original theme definitions before processing mutates them
223
+ if (config.theme && !CONFIG._originalTheme) {
224
+ CONFIG._originalTheme = JSON.parse(JSON.stringify(config.theme))
225
+ }
226
+
190
227
  // Process only lowercase keys (skip UPPERCASE when lowercase equivalent exists)
191
228
  keys.map((key) => {
192
229
  const lower = key.toLowerCase()
@@ -11,6 +11,38 @@ import {
11
11
  isArray
12
12
  } from '@domql/utils'
13
13
 
14
+ // Common CSS named colors — used to distinguish valid CSS color names
15
+ // from unresolved design-system tokens (e.g. 'blackRussian', 'grayMid')
16
+ const CSS_NAMED_COLORS = new Set([
17
+ 'black', 'white', 'red', 'green', 'blue', 'yellow', 'orange', 'purple',
18
+ 'pink', 'brown', 'gray', 'grey', 'cyan', 'magenta', 'lime', 'olive',
19
+ 'navy', 'teal', 'aqua', 'maroon', 'silver', 'fuchsia', 'transparent',
20
+ 'currentColor', 'currentcolor', 'inherit', 'initial', 'unset', 'none',
21
+ 'aliceblue', 'antiquewhite', 'aquamarine', 'azure', 'beige', 'bisque',
22
+ 'blanchedalmond', 'blueviolet', 'burlywood', 'cadetblue', 'chartreuse',
23
+ 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson', 'darkblue',
24
+ 'darkcyan', 'darkgoldenrod', 'darkgray', 'darkgreen', 'darkgrey', 'darkkhaki',
25
+ 'darkmagenta', 'darkolivegreen', 'darkorange', 'darkorchid', 'darkred',
26
+ 'darksalmon', 'darkseagreen', 'darkslateblue', 'darkslategray', 'darkslategrey',
27
+ 'darkturquoise', 'darkviolet', 'deeppink', 'deepskyblue', 'dimgray', 'dimgrey',
28
+ 'dodgerblue', 'firebrick', 'floralwhite', 'forestgreen', 'gainsboro', 'ghostwhite',
29
+ 'gold', 'goldenrod', 'greenyellow', 'honeydew', 'hotpink', 'indianred', 'indigo',
30
+ 'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen', 'lemonchiffon',
31
+ 'lightblue', 'lightcoral', 'lightcyan', 'lightgoldenrodyellow', 'lightgray',
32
+ 'lightgreen', 'lightgrey', 'lightpink', 'lightsalmon', 'lightseagreen',
33
+ 'lightskyblue', 'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow',
34
+ 'limegreen', 'linen', 'mediumaquamarine', 'mediumblue', 'mediumorchid',
35
+ 'mediumpurple', 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen',
36
+ 'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream', 'mistyrose',
37
+ 'moccasin', 'navajowhite', 'oldlace', 'olivedrab', 'orangered', 'orchid',
38
+ 'palegoldenrod', 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip',
39
+ 'peachpuff', 'peru', 'plum', 'powderblue', 'rosybrown', 'royalblue',
40
+ 'saddlebrown', 'salmon', 'sandybrown', 'seagreen', 'seashell', 'sienna',
41
+ 'skyblue', 'slateblue', 'slategray', 'slategrey', 'snow', 'springgreen',
42
+ 'steelblue', 'tan', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat',
43
+ 'whitesmoke', 'yellowgreen', 'rebeccapurple'
44
+ ])
45
+
14
46
  const setThemeValue = theme => {
15
47
  const value = {}
16
48
  const { state, media, helpers, ...rest } = theme
@@ -166,6 +198,27 @@ const generateAutoVars = (schemes, varPrefix, CONFIG) => {
166
198
  if (schemes[scheme]) for (const k of Object.keys(schemes[scheme])) allKeys.add(k)
167
199
  }
168
200
 
201
+ // Pre-scan: detect schemes with unresolvable color values
202
+ // If any color in a scheme fails to resolve, skip the ENTIRE scheme
203
+ // to avoid partial theme application (e.g. white text without dark background)
204
+ const brokenSchemes = new Set()
205
+ if (globalTheme === 'auto') {
206
+ for (const param of allKeys) {
207
+ const symb = param.slice(0, 1)
208
+ if (symb === '@' || symb === '.' || symb === ':') continue
209
+ for (const scheme in schemes) {
210
+ if (brokenSchemes.has(scheme)) continue
211
+ const val = schemes[scheme]?.[param]
212
+ if (val === undefined) continue
213
+ const color = getColor(val, `@${scheme}`)
214
+ if (color === undefined) continue
215
+ if (isString(color) && /^[a-z][a-zA-Z]+$/.test(color) && !CSS_NAMED_COLORS.has(color)) {
216
+ brokenSchemes.add(scheme)
217
+ }
218
+ }
219
+ }
220
+ }
221
+
169
222
  for (const param of allKeys) {
170
223
  const symb = param.slice(0, 1)
171
224
 
@@ -193,12 +246,19 @@ const generateAutoVars = (schemes, varPrefix, CONFIG) => {
193
246
  const autoVar = `--theme-${varPrefix}-${param}`
194
247
 
195
248
  if (globalTheme === 'auto') {
249
+ let fallbackColor
196
250
  for (const scheme in schemes) {
251
+ if (brokenSchemes.has(scheme)) continue
197
252
  const val = schemes[scheme]?.[param]
198
253
  if (val === undefined) continue
199
254
  const color = getColor(val, `@${scheme}`)
200
255
  if (color === undefined) continue
201
256
 
257
+ // Use 'light' scheme (or first resolved) as base fallback
258
+ if (scheme === 'light' || fallbackColor === undefined) {
259
+ fallbackColor = color
260
+ }
261
+
202
262
  // [data-theme] selector for ALL schemes (custom + standard)
203
263
  const selector = `[data-theme="${scheme}"]`
204
264
  if (!MEDIA_VARS[selector]) MEDIA_VARS[selector] = {}
@@ -211,6 +271,12 @@ const generateAutoVars = (schemes, varPrefix, CONFIG) => {
211
271
  MEDIA_VARS[mq][autoVar] = color
212
272
  }
213
273
  }
274
+
275
+ // Set fallback default (light or first scheme) so the variable
276
+ // always resolves even without a matching media query
277
+ if (fallbackColor !== undefined) {
278
+ CSS_VARS[autoVar] = fallbackColor
279
+ }
214
280
  } else {
215
281
  // Force specific theme — set non-suffixed var directly
216
282
  const forced = String(globalTheme).replace(/^'|'$/g, '')