@symbo.ls/scratch 3.14.2 → 3.14.4

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 (141) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/cjs/index.js +1 -0
  3. package/dist/cjs/package.json +1 -0
  4. package/dist/cjs/src/defaultConfig/animation.js +1 -0
  5. package/dist/cjs/src/defaultConfig/cases.js +1 -0
  6. package/dist/cjs/src/defaultConfig/class.js +1 -0
  7. package/dist/cjs/src/defaultConfig/color.js +1 -0
  8. package/dist/cjs/src/defaultConfig/document.js +1 -0
  9. package/dist/cjs/src/defaultConfig/font-family.js +1 -0
  10. package/dist/cjs/src/defaultConfig/font.js +1 -0
  11. package/dist/cjs/src/defaultConfig/grid.js +1 -0
  12. package/dist/cjs/src/defaultConfig/icons.js +1 -0
  13. package/dist/cjs/src/defaultConfig/index.js +1 -0
  14. package/dist/cjs/src/defaultConfig/media.js +1 -0
  15. package/dist/cjs/src/defaultConfig/responsive.js +1 -0
  16. package/dist/cjs/src/defaultConfig/sequence.js +1 -0
  17. package/dist/cjs/src/defaultConfig/shadow.js +1 -0
  18. package/dist/cjs/src/defaultConfig/spacing.js +1 -0
  19. package/dist/cjs/src/defaultConfig/svg.js +1 -0
  20. package/dist/cjs/src/defaultConfig/templates.js +1 -0
  21. package/dist/cjs/src/defaultConfig/theme.js +1 -0
  22. package/dist/cjs/src/defaultConfig/timing.js +1 -0
  23. package/dist/cjs/src/defaultConfig/typography.js +1 -0
  24. package/dist/cjs/src/defaultConfig/unit.js +1 -0
  25. package/dist/cjs/src/factory.js +1 -0
  26. package/dist/cjs/src/index.js +1 -0
  27. package/dist/cjs/src/set.js +1 -0
  28. package/dist/cjs/src/system/color.js +1 -0
  29. package/dist/cjs/src/system/document.js +1 -0
  30. package/dist/cjs/src/system/font.js +1 -0
  31. package/dist/cjs/src/system/index.js +1 -0
  32. package/dist/cjs/src/system/reset.js +1 -0
  33. package/dist/cjs/src/system/shadow.js +1 -0
  34. package/dist/cjs/src/system/spacing.js +1 -0
  35. package/dist/cjs/src/system/svg.js +1 -0
  36. package/dist/cjs/src/system/theme.js +1 -0
  37. package/dist/cjs/src/system/timing.js +1 -0
  38. package/dist/cjs/src/system/typography.js +1 -0
  39. package/dist/cjs/src/tests/index.js +1 -0
  40. package/dist/cjs/src/transforms/index.js +1 -0
  41. package/dist/cjs/src/utils/color.js +1 -0
  42. package/dist/cjs/src/utils/font.js +9 -0
  43. package/dist/cjs/src/utils/index.js +1 -0
  44. package/dist/cjs/src/utils/sequence.js +1 -0
  45. package/dist/cjs/src/utils/sprite.js +1 -0
  46. package/dist/cjs/src/utils/theme.js +1 -0
  47. package/dist/cjs/src/utils/unit.js +1 -0
  48. package/dist/cjs/src/utils/var.js +1 -0
  49. package/dist/esm/index.js +1 -0
  50. package/dist/esm/src/defaultConfig/animation.js +1 -0
  51. package/dist/esm/src/defaultConfig/cases.js +1 -0
  52. package/dist/esm/src/defaultConfig/class.js +1 -0
  53. package/dist/esm/src/defaultConfig/color.js +1 -0
  54. package/dist/esm/src/defaultConfig/document.js +1 -0
  55. package/dist/esm/src/defaultConfig/font-family.js +1 -0
  56. package/dist/esm/src/defaultConfig/font.js +1 -0
  57. package/dist/esm/src/defaultConfig/grid.js +1 -0
  58. package/dist/esm/src/defaultConfig/icons.js +1 -0
  59. package/dist/esm/src/defaultConfig/index.js +1 -0
  60. package/dist/esm/src/defaultConfig/media.js +1 -0
  61. package/dist/esm/src/defaultConfig/responsive.js +1 -0
  62. package/dist/esm/src/defaultConfig/sequence.js +1 -0
  63. package/dist/esm/src/defaultConfig/shadow.js +1 -0
  64. package/dist/esm/src/defaultConfig/spacing.js +1 -0
  65. package/dist/esm/src/defaultConfig/svg.js +1 -0
  66. package/dist/esm/src/defaultConfig/templates.js +1 -0
  67. package/dist/esm/src/defaultConfig/theme.js +1 -0
  68. package/dist/esm/src/defaultConfig/timing.js +1 -0
  69. package/dist/esm/src/defaultConfig/typography.js +1 -0
  70. package/dist/esm/src/defaultConfig/unit.js +1 -0
  71. package/dist/esm/src/factory.js +1 -0
  72. package/dist/esm/src/index.js +1 -0
  73. package/dist/esm/src/set.js +1 -0
  74. package/dist/esm/src/system/color.js +1 -0
  75. package/dist/esm/src/system/document.js +1 -0
  76. package/dist/esm/src/system/font.js +1 -0
  77. package/dist/esm/src/system/index.js +1 -0
  78. package/dist/esm/src/system/reset.js +1 -0
  79. package/dist/esm/src/system/shadow.js +1 -0
  80. package/dist/esm/src/system/spacing.js +1 -0
  81. package/dist/esm/src/system/svg.js +1 -0
  82. package/dist/esm/src/system/theme.js +1 -0
  83. package/dist/esm/src/system/timing.js +1 -0
  84. package/dist/esm/src/system/typography.js +1 -0
  85. package/dist/esm/src/tests/index.js +1 -0
  86. package/dist/esm/src/transforms/index.js +1 -0
  87. package/dist/esm/src/utils/color.js +1 -0
  88. package/dist/esm/src/utils/font.js +9 -0
  89. package/dist/esm/src/utils/index.js +1 -0
  90. package/dist/esm/src/utils/sequence.js +1 -0
  91. package/dist/esm/src/utils/sprite.js +1 -0
  92. package/dist/esm/src/utils/theme.js +1 -0
  93. package/dist/esm/src/utils/unit.js +1 -0
  94. package/dist/esm/src/utils/var.js +1 -0
  95. package/package.json +18 -17
  96. package/index.js +0 -1
  97. package/src/defaultConfig/animation.js +0 -3
  98. package/src/defaultConfig/cases.js +0 -3
  99. package/src/defaultConfig/class.js +0 -6
  100. package/src/defaultConfig/color.js +0 -9
  101. package/src/defaultConfig/document.js +0 -3
  102. package/src/defaultConfig/font-family.js +0 -15
  103. package/src/defaultConfig/font.js +0 -12
  104. package/src/defaultConfig/grid.js +0 -5
  105. package/src/defaultConfig/icons.js +0 -4
  106. package/src/defaultConfig/index.js +0 -24
  107. package/src/defaultConfig/media.js +0 -10
  108. package/src/defaultConfig/responsive.js +0 -29
  109. package/src/defaultConfig/sequence.js +0 -24
  110. package/src/defaultConfig/shadow.js +0 -3
  111. package/src/defaultConfig/spacing.js +0 -19
  112. package/src/defaultConfig/svg.js +0 -4
  113. package/src/defaultConfig/templates.js +0 -3
  114. package/src/defaultConfig/theme.js +0 -13
  115. package/src/defaultConfig/timing.js +0 -18
  116. package/src/defaultConfig/typography.js +0 -22
  117. package/src/defaultConfig/unit.js +0 -5
  118. package/src/factory.js +0 -135
  119. package/src/index.js +0 -24
  120. package/src/set.js +0 -353
  121. package/src/system/color.js +0 -327
  122. package/src/system/document.js +0 -16
  123. package/src/system/font.js +0 -66
  124. package/src/system/index.js +0 -12
  125. package/src/system/reset.js +0 -119
  126. package/src/system/shadow.js +0 -107
  127. package/src/system/spacing.js +0 -152
  128. package/src/system/svg.js +0 -132
  129. package/src/system/theme.js +0 -454
  130. package/src/system/timing.js +0 -32
  131. package/src/system/typography.js +0 -110
  132. package/src/tests/index.js +0 -4
  133. package/src/transforms/index.js +0 -270
  134. package/src/utils/color.js +0 -269
  135. package/src/utils/font.js +0 -116
  136. package/src/utils/index.js +0 -9
  137. package/src/utils/sequence.js +0 -386
  138. package/src/utils/sprite.js +0 -107
  139. package/src/utils/theme.js +0 -8
  140. package/src/utils/unit.js +0 -72
  141. package/src/utils/var.js +0 -129
package/src/set.js DELETED
@@ -1,353 +0,0 @@
1
- 'use strict'
2
-
3
- import { FACTORY, getActiveConfig, setActiveConfig, toCamel } from './factory.js' // eslint-disable-line no-unused-vars
4
- import {
5
- setColor,
6
- setGradient,
7
- setFont,
8
- setFontFamily,
9
- setTheme,
10
- setSvgIcon,
11
- setSVG,
12
- applyTypographySequence,
13
- applySpacingSequence,
14
- applyReset,
15
- applyTimingSequence,
16
- applyDocument,
17
- setShadow
18
- } from './system/index.js'
19
-
20
- import { deepMerge, isObject, isArray } from '@symbo.ls/utils'
21
-
22
- const setVars = (val, key) => {
23
- const CONFIG = getActiveConfig()
24
- const { cssVars } = CONFIG
25
- const varName = key.startsWith('--') ? key : `--${key}`
26
- cssVars[varName] = val
27
- return val
28
- }
29
-
30
- const asIs = (val) => val
31
-
32
- // `toCamel` (imported above) normalises snake_case designSystem keys
33
- // (e.g. `font_family` → `fontFamily`) before transformer dispatch — callers
34
- // can use either casing without needing to register both variants here.
35
-
36
- export const VALUE_TRANSFORMERS = {
37
- color: setColor,
38
- gradient: setGradient,
39
- font: setFont,
40
- fontFamily: setFontFamily,
41
- fontfamily: setFontFamily,
42
- theme: setTheme,
43
- icons: setSvgIcon,
44
- semanticIcons: asIs,
45
- semanticicons: asIs,
46
- svg: setSVG,
47
- svgData: asIs,
48
- typography: asIs,
49
- shadow: setShadow,
50
- spacing: asIs,
51
- media: asIs,
52
- grid: asIs,
53
- class: asIs,
54
- timing: asIs,
55
- reset: asIs,
56
- unit: asIs,
57
- animation: asIs,
58
- vars: setVars
59
- }
60
-
61
- /**
62
- *
63
- * @param {Object} FACTORY_NAME Defines which factory it goes to
64
- * @param {*} value Value of the property
65
- * @param {String} key Key, or the name of the property
66
- * @returns {Object} Factory
67
- */
68
- export const setValue = (factoryName, value, key) => {
69
- const CONFIG = getActiveConfig()
70
- const camelName = toCamel(factoryName)
71
- const lowerName = camelName.toLowerCase()
72
- const FACTORY = CONFIG[camelName] || CONFIG[lowerName] || CONFIG[factoryName]
73
-
74
- const transformer = VALUE_TRANSFORMERS[camelName] || VALUE_TRANSFORMERS[lowerName]
75
- if (transformer) {
76
- try {
77
- const result = transformer(value, key)
78
- if (FACTORY) FACTORY[key] = result
79
- return FACTORY
80
- } catch (error) {
81
- if (CONFIG.verbose)
82
- console.warn('Error setting', lowerName, 'value', value, key, error)
83
- }
84
- }
85
-
86
- if (CONFIG.verbose)
87
- console.warn('Can not find', lowerName, 'method in scratch')
88
- }
89
-
90
- export const setEach = (factoryName, props) => {
91
- const CONFIG = getActiveConfig()
92
- const lowerName = factoryName.toLowerCase()
93
- // Process primitive (string/number) values before composite (array/object)
94
- // ones so composite values can dereference primitives by name. Without this
95
- // ordering, a color like `selectedDay: ['--blue .12', '--blue .18']` could
96
- // be processed before `blue` itself was registered in CONFIG.color, leaving
97
- // refColor undefined and falling back to CSS keyword resolution.
98
- const keys = Object.keys(props).sort((a, b) => {
99
- const aComposite = isObject(props[a]) || isArray(props[a]) ? 1 : 0
100
- const bComposite = isObject(props[b]) || isArray(props[b]) ? 1 : 0
101
- return aComposite - bComposite
102
- })
103
-
104
- keys.forEach((key) => {
105
- try {
106
- return setValue(lowerName, props[key], key)
107
- } catch (error) {
108
- if (CONFIG.verbose)
109
- console.warn(
110
- 'Error setting',
111
- lowerName,
112
- 'value',
113
- props[key],
114
- key,
115
- error
116
- )
117
- }
118
- })
119
-
120
- return CONFIG[lowerName] || CONFIG[factoryName]
121
- }
122
-
123
- export const changeGlobalTheme = (newTheme, targetConfig) => {
124
- // Accept an explicit config so scoped code (inside pushConfig) can
125
- // target the right app's theme without relying on the stack timing.
126
- const CONFIG = targetConfig || getActiveConfig()
127
- CONFIG.globalTheme = newTheme
128
-
129
- // Apply the theme on the scope root. For a single-design-system page we
130
- // write to `<html>`; multiple design systems on the same page each carry
131
- // their own root element (set by the caller on CONFIG.themeRoot), so the
132
- // attribute is applied there and leaves the rest of the document alone.
133
- //
134
- // `'auto'` switches to OS-driven mode: we resolve the current
135
- // `prefers-color-scheme` and register (once) a listener so later OS
136
- // changes flip the attribute live.
137
- // Resolve the target document from the config, not the global `document`.
138
- // In a multi-app setup the CONFIG belongs to an iframe — its themeRoot is
139
- // inside iframe doc, and its stylesheets live in iframe doc. Reading
140
- // global `document` here flips the parent's data-theme attribute instead.
141
- // `CONFIG.document` is also a factory bucket (font/theme defaults consumed
142
- // by `applyDocument`); only treat it as a DOM document override if it
143
- // actually looks like one.
144
- const configDoc = CONFIG.document && CONFIG.document.documentElement ? CONFIG.document : null
145
- const targetDoc = configDoc || (typeof document !== 'undefined' ? document : null)
146
- const targetWin = (targetDoc && targetDoc.defaultView) || (typeof window !== 'undefined' ? window : null)
147
- if (targetDoc) {
148
- const root = CONFIG.themeRoot || targetDoc.documentElement
149
- const forced = newTheme && newTheme !== 'auto'
150
- if (forced) {
151
- root.setAttribute('data-theme', newTheme)
152
- // Mirror `data-theme` into CSS `color-scheme` so Chrome stops
153
- // forcibly re-rendering form controls (buttons, inputs, selects) in
154
- // the opposite scheme of the design system. Only the standard
155
- // `dark`/`light` schemes map to a UA color-scheme; custom schemes
156
- // (sepia etc.) fall back to `light dark` so the browser picks by OS.
157
- if (newTheme === 'dark' || newTheme === 'light') {
158
- root.style.colorScheme = newTheme
159
- } else {
160
- root.style.colorScheme = 'light dark'
161
- }
162
- } else if (targetWin && targetWin.matchMedia) {
163
- const apply = (mq) => { root.setAttribute('data-theme', mq.matches ? 'dark' : 'light') }
164
- const mq = targetWin.matchMedia('(prefers-color-scheme: dark)')
165
- apply(mq)
166
- root.style.colorScheme = 'light dark'
167
- if (!CONFIG.__prefersListener) {
168
- CONFIG.__prefersListener = apply
169
- try { mq.addEventListener('change', apply) } catch (e) { mq.addListener(apply) }
170
- }
171
- } else {
172
- root.setAttribute('data-theme', 'light')
173
- root.style.colorScheme = 'light'
174
- }
175
- }
176
-
177
- // Clear theme-related CSS vars (with or without varPrefix)
178
- const themeVarPrefix = CONFIG.varPrefix ? `--${CONFIG.varPrefix}-theme-` : '--theme-'
179
- for (const key in CONFIG.cssVars) {
180
- if (key.startsWith(themeVarPrefix)) delete CONFIG.cssVars[key]
181
- }
182
-
183
- // Clear all cssMediaVars (media queries + data-theme selectors)
184
- for (const key in CONFIG.cssMediaVars) {
185
- delete CONFIG.cssMediaVars[key]
186
- }
187
-
188
- // Apply updated CSS vars to the config's scoped stylesheet rule
189
- // (`:root` for the primary app, `[data-smbls-app="<key>"]` for secondary apps).
190
- if (targetDoc && CONFIG.cssVars) {
191
- const targetSelector = CONFIG.scopeSelector || ':root'
192
- const sheets = targetDoc.styleSheets
193
- for (let i = 0; i < sheets.length; i++) {
194
- try {
195
- const rules = sheets[i].cssRules
196
- for (let j = 0; j < rules.length; j++) {
197
- if (rules[j].selectorText === targetSelector) {
198
- for (const key in CONFIG.cssVars) {
199
- rules[j].style.setProperty(key, CONFIG.cssVars[key])
200
- }
201
- return CONFIG
202
- }
203
- }
204
- } catch (e) {
205
- /* cross-origin stylesheet */
206
- }
207
- }
208
- }
209
-
210
- return CONFIG
211
- }
212
-
213
- const SET_OPTIONS = {}
214
-
215
- export const set = (recivedConfig, options = SET_OPTIONS) => {
216
- // Accept an explicit config instance via options.config so callers
217
- // (e.g. renderProject for an embedded app) can operate on an isolated
218
- // config without touching the global singleton.
219
- let CONFIG = options.config || getActiveConfig()
220
-
221
- const {
222
- version,
223
- verbose,
224
- useVariable,
225
- useReset,
226
- useSvgSprite,
227
- useFontImport,
228
- useIconSprite,
229
- globalTheme,
230
- themeRoot,
231
- useDocumentTheme,
232
- useDefaultConfig,
233
- semanticIcons,
234
- files,
235
- assets,
236
- ...config
237
- } = recivedConfig
238
-
239
- if (options.newConfig) {
240
- CONFIG = setActiveConfig(options.newConfig)
241
- }
242
-
243
- if (files !== undefined) CONFIG.files = files
244
- if (assets !== undefined) CONFIG.assets = assets
245
- if (verbose !== undefined) CONFIG.verbose = verbose
246
- if (useVariable !== undefined) CONFIG.useVariable = useVariable
247
- if (useReset !== undefined) CONFIG.useReset = useReset
248
- if (useFontImport !== undefined) CONFIG.useFontImport = useFontImport
249
- if (useSvgSprite !== undefined) CONFIG.useSvgSprite = useSvgSprite
250
- if (useIconSprite !== undefined) CONFIG.useIconSprite = useIconSprite
251
- if (useDocumentTheme !== undefined) CONFIG.useDocumentTheme = useDocumentTheme
252
- if (globalTheme !== undefined) CONFIG.globalTheme = globalTheme
253
- if (themeRoot !== undefined) CONFIG.themeRoot = themeRoot
254
- if (recivedConfig.useThemeSuffixedVars !== undefined)
255
- CONFIG.useThemeSuffixedVars = recivedConfig.useThemeSuffixedVars
256
- if (useDefaultConfig !== undefined) CONFIG.useDefaultConfig = useDefaultConfig
257
- if (semanticIcons !== undefined) CONFIG.semanticIcons = semanticIcons
258
- if (CONFIG.verbose) console.log(CONFIG)
259
-
260
- // Apply `data-theme` to the scope root so CSS selectors resolve correctly.
261
- // `auto` means "follow the OS": we read `prefers-color-scheme` and install
262
- // a one-time listener so a later OS toggle flips `data-theme` live without
263
- // reloading the page. Forced themes (`'dark'`, `'light'`, or any custom
264
- // scheme name like `'ocean'`) stick until the user calls
265
- // `changeGlobalTheme(...)` again.
266
- // `CONFIG.document` is also a factory bucket (font/theme defaults consumed
267
- // by `applyDocument`); only treat it as a DOM document override if it
268
- // actually looks like one.
269
- const setConfigDoc = CONFIG.document && CONFIG.document.documentElement ? CONFIG.document : null
270
- const setTargetDoc = setConfigDoc || (typeof document !== 'undefined' ? document : null)
271
- const setTargetWin = (setTargetDoc && setTargetDoc.defaultView) || (typeof window !== 'undefined' ? window : null)
272
- const setRoot = setTargetDoc && (CONFIG.themeRoot || setTargetDoc.documentElement)
273
- if (setRoot && typeof setRoot.setAttribute === 'function') {
274
- const forced = CONFIG.globalTheme && CONFIG.globalTheme !== 'auto'
275
- if (forced) {
276
- setRoot.setAttribute('data-theme', CONFIG.globalTheme)
277
- if (CONFIG.globalTheme === 'dark' || CONFIG.globalTheme === 'light') {
278
- setRoot.style.colorScheme = CONFIG.globalTheme
279
- } else {
280
- setRoot.style.colorScheme = 'light dark'
281
- }
282
- } else if (setTargetWin && setTargetWin.matchMedia) {
283
- const apply = (mq) => { setRoot.setAttribute('data-theme', mq.matches ? 'dark' : 'light') }
284
- const mq = setTargetWin.matchMedia('(prefers-color-scheme: dark)')
285
- apply(mq)
286
- setRoot.style.colorScheme = 'light dark'
287
- if (!CONFIG.__prefersListener) {
288
- CONFIG.__prefersListener = apply
289
- try { mq.addEventListener('change', apply) } catch (e) { mq.addListener(apply) }
290
- }
291
- } else {
292
- setRoot.setAttribute('data-theme', 'light')
293
- setRoot.style.colorScheme = 'light'
294
- }
295
- }
296
-
297
- if (!CONFIG.__svgCache) CONFIG.__svgCache = {}
298
-
299
- const keys = Object.keys(config)
300
- const keySet = new Set(keys)
301
-
302
- // Pre-merge: fold UPPERCASE default keys into lowercase project keys
303
- keys.forEach((key) => {
304
- const lower = key.toLowerCase()
305
- if (lower !== key && keySet.has(lower)) {
306
- deepMerge(config[lower], config[key])
307
- }
308
- })
309
-
310
- // Process only lowercase keys (skip UPPERCASE when lowercase equivalent exists)
311
- keys.map((key) => {
312
- const lower = key.toLowerCase()
313
- if (lower !== key && keySet.has(lower)) return
314
- return setEach(key, config[key])
315
- })
316
-
317
- // Propagate varPrefix to sequence sub-configs for CSS variable name isolation
318
- if (CONFIG.varPrefix) {
319
- if (CONFIG.typography) CONFIG.typography.varPrefix = CONFIG.varPrefix
320
- if (CONFIG.spacing) CONFIG.spacing.varPrefix = CONFIG.varPrefix
321
- if (CONFIG.timing) CONFIG.timing.varPrefix = CONFIG.varPrefix
322
- }
323
-
324
- // apply generic configs
325
- if (config.typography) {
326
- try {
327
- applyTypographySequence()
328
- } catch (e) {
329
- if (CONFIG.verbose) console.warn('Error applying typography sequence', e)
330
- }
331
- }
332
- if (config.spacing) {
333
- try {
334
- applySpacingSequence()
335
- } catch (e) {
336
- if (CONFIG.verbose) console.warn('Error applying spacing sequence', e)
337
- }
338
- }
339
- if (config.timing) {
340
- try {
341
- applyTimingSequence()
342
- } catch (e) {
343
- if (CONFIG.verbose) console.warn('Error applying timing sequence', e)
344
- }
345
- }
346
- applyDocument()
347
- // Capture so init.js injects the full html/body reset (with default
348
- // font-family); dropping the return leaves CONFIG.reset as the bare seed.
349
- const computedReset = applyReset()
350
- if (computedReset) CONFIG.reset = computedReset
351
-
352
- return CONFIG
353
- }
@@ -1,327 +0,0 @@
1
- 'use strict'
2
-
3
- import { isObject, isArray, isString } from '@symbo.ls/utils'
4
- import { getActiveConfig } from '../factory.js'
5
-
6
- import {
7
- colorStringToRgbaArray,
8
- getRgbTone,
9
- isCSSVar,
10
- parseColorToken
11
- } from '../utils/index.js'
12
-
13
- export const getColor = (value, key, config) => {
14
- const CONFIG = config || getActiveConfig()
15
- if (!isString(value)) {
16
- if (CONFIG.verbose) console.warn(value, '- type for color is not valid')
17
- return
18
- }
19
-
20
- if (isCSSVar(value)) return `var(${value})`
21
-
22
- if (key && value[key]) value = value[key]
23
-
24
- let name, alpha, tone
25
- if (isArray(value)) {
26
- [name, alpha, tone] = value
27
- } else {
28
- const parsed = parseColorToken(value)
29
- if (!parsed) {
30
- // Direct lookup for hyphenated names (e.g. 'gradient-light', 'gradient-dark-active')
31
- const { color: COLOR, gradient: GRADIENT } = CONFIG
32
- const directVal = GRADIENT[value] || COLOR[value]
33
- if (directVal) return CONFIG.useVariable ? `var(${directVal.var})` : directVal.value
34
- return value
35
- }
36
- if (parsed.passthrough) return parsed.passthrough
37
- if (parsed.cssVar) return `var(${parsed.cssVar})`
38
- name = parsed.name
39
- alpha = parsed.alpha
40
- tone = parsed.tone
41
- }
42
-
43
- const { color: COLOR, gradient: GRADIENT } = CONFIG
44
-
45
- let val = (COLOR[name] || GRADIENT[name])
46
-
47
- if (!val) {
48
- if (CONFIG.verbose) console.warn('Can\'t find color ', name)
49
- return value
50
- }
51
-
52
- if (key) {
53
- if (val[key]) val = val[key]
54
- else if (CONFIG.verbose) console.warn(value, ' - does not have ', key)
55
- }
56
-
57
- // TODO: support variables
58
- // if (alpha) return `rgba(var(${val[shade || ''].var}), ${modifier})`
59
-
60
- let rgb = val.rgb
61
- if (!rgb) {
62
- return CONFIG.useVariable ? `var(${val.var})` : val.value
63
- }
64
- if (tone && !val[tone]) {
65
- rgb = getRgbTone(rgb, tone)
66
- val[tone] = { rgb, var: `${val.var}-${tone}` }
67
- }
68
- if (val[tone]) rgb = val[tone].rgb
69
-
70
- if (alpha) return `rgba(${rgb}, ${alpha})`
71
- if (tone) return `rgba(${rgb}, 1)`
72
- return CONFIG.useVariable ? `var(${val.var})` : `rgb(${rgb})`
73
- }
74
-
75
- export const getMediaColor = (value, globalTheme, config) => {
76
- const CONFIG = config || getActiveConfig()
77
- if (!globalTheme) globalTheme = CONFIG.globalTheme === 'auto' ? null : CONFIG.globalTheme
78
- if (!isString(value)) {
79
- if (CONFIG.verbose) console.warn(value, '- type for color is not valid')
80
- return
81
- }
82
-
83
- if (isCSSVar(value)) return `var(${value})`
84
-
85
- let name
86
- if (isArray(value)) {
87
- name = value[0]
88
- } else {
89
- const parsed = parseColorToken(value)
90
- if (!parsed) return value
91
- if (parsed.passthrough) return parsed.passthrough
92
- if (parsed.cssVar) return `var(${parsed.cssVar})`
93
- name = parsed.name
94
- }
95
-
96
- const { color: COLOR, gradient: GRADIENT } = CONFIG
97
- const val = COLOR[name] || GRADIENT[name]
98
- const isObj = isObject(val)
99
-
100
- if (isObj && val.value) return getColor(value, `@${globalTheme}`, config)
101
- else if (isObj) {
102
- // Prefer the adaptive var (--color-NAME) when available so runtime theme
103
- // switches via [data-theme] actually take effect. Without this, setting
104
- // `globalTheme: 'dark'` in config would lock every color to the dark
105
- // scheme-specific var (--color-NAME-dark), which never responds to
106
- // runtime theme toggles.
107
- if (CONFIG.useVariable && val.var) return `var(${val.var})`
108
- if (globalTheme) return getColor(value, `@${globalTheme}`, config)
109
- else {
110
- // Legacy fallback when adaptive var is unavailable (useVariable: false).
111
- // Only @-prefixed keys are scheme variants — skip metadata like `var`.
112
- const obj = {}
113
- for (const mediaName in val) {
114
- if (mediaName.charCodeAt(0) !== 64 /* @ */) continue
115
- const query = CONFIG.media[mediaName.slice(1)]
116
- if (!query) continue
117
- const media = '@media ' + (query === 'print' ? `${query}` : `screen and ${query}`)
118
- obj[media] = getColor(value, mediaName, config)
119
- }
120
- return obj
121
- }
122
- } else {
123
- if (CONFIG.verbose) console.warn('Can\'t find color', value)
124
- return value
125
- }
126
- }
127
-
128
- export const setColor = (val, key, suffix) => {
129
- const CONFIG = getActiveConfig()
130
-
131
- if (isString(val) && isCSSVar(val)) {
132
- const rawRef = val.slice(2)
133
- val = getColor(rawRef)
134
- if (!(
135
- val.includes('rgb') ||
136
- val.includes('var') ||
137
- val.includes('#')
138
- )) {
139
- // Handle space-separated format: '--colorName alpha' (e.g. '--gray1 1')
140
- const parts = rawRef.split(' ')
141
- let refColor = CONFIG.color[parts[0]]
142
- // Lazy resolution: composite colors (arrays/objects) often reference
143
- // primitives by name. If iteration order processed the composite first,
144
- // the primitive is still its raw input — process it now so this
145
- // dereference works regardless of key order in CONFIG.color. setEach()
146
- // also pre-sorts so primitives go first, but this is a safety net.
147
- if (refColor && !refColor.value && !refColor.rgb && (isString(refColor) || isArray(refColor) || isObject(refColor))) {
148
- CONFIG.color[parts[0]] = setColor(refColor, parts[0])
149
- refColor = CONFIG.color[parts[0]]
150
- }
151
- if (refColor && refColor.value) {
152
- let rgb = refColor.rgb
153
- const alpha = parts[1] !== undefined ? parts[1] : '1'
154
- const tone = parts[2]
155
- if (tone) {
156
- rgb = getRgbTone(rgb, tone)
157
- }
158
- val = `rgba(${rgb}, ${alpha})`
159
- } else {
160
- // Try to resolve as CSS keyword color with tone modifier
161
- const tone = parts[2]
162
- const alpha = parts[1] !== undefined ? parts[1] : '1'
163
- if (tone) {
164
- try {
165
- const rgb = colorStringToRgbaArray(parts[0])
166
- if (rgb && rgb.length >= 3) {
167
- const tonedRgb = getRgbTone(rgb.slice(0, 3).join(', '), tone)
168
- val = `rgba(${tonedRgb}, ${alpha})`
169
- } else {
170
- val = parts[0]
171
- }
172
- } catch (e) {
173
- val = parts[0]
174
- }
175
- } else {
176
- val = parts[0]
177
- }
178
- }
179
- }
180
- }
181
-
182
- // Normalize array shorthand `[lightVal, darkVal]` to scheme-keyed object
183
- // so the multi-scheme branch below handles both cases uniformly.
184
- if (isArray(val)) {
185
- val = { '@light': val[0], '@dark': val[1] }
186
- }
187
-
188
- if (isObject(val)) {
189
- const obj = {}
190
- for (const variant in val) {
191
- obj[variant] = setColor(
192
- val[variant],
193
- key,
194
- variant.slice(0, 1) === '@' ? variant.slice(1) : variant
195
- )
196
- }
197
-
198
- // Generate a single adaptive `--color-${key}` CSS var so consumers can
199
- // write `border-color: var(--color-${key})` and have it switch via
200
- // `@media (prefers-color-scheme)` and `[data-theme]` automatically.
201
- // Scheme-agnostic: works for @light/@dark plus arbitrary custom schemes
202
- // (@ocean, @sunset, etc). Mirrors generateAutoVars() in system/theme.js.
203
- //
204
- // Without this, getMediaColor() falls into the @media-keyed-object branch
205
- // (when globalTheme === 'auto'), which the CSS engine cannot serialize as
206
- // a single CSS property value — emitting broken rules like
207
- // `.classname borderrightcolor { }`.
208
- if (CONFIG.useVariable) {
209
- const vp = CONFIG.varPrefix ? CONFIG.varPrefix + '-' : ''
210
- const adaptiveVar = `--${vp}color-${key}`
211
- let fallbackValue
212
- const schemeValues = {}
213
-
214
- for (const variant in obj) {
215
- if (variant.charCodeAt(0) !== 64 /* @ */) continue
216
- const scheme = variant.slice(1)
217
- const entry = obj[variant]
218
- const value = entry && (entry.value || entry)
219
- if (!value || typeof value !== 'string') continue
220
- schemeValues[scheme] = value
221
- // Prefer 'light' as the :root fallback; otherwise first resolved scheme.
222
- if (scheme === 'light' || fallbackValue === undefined) fallbackValue = value
223
- }
224
-
225
- if (fallbackValue !== undefined) {
226
- CONFIG.cssVars[adaptiveVar] = fallbackValue
227
-
228
- if (!CONFIG.cssMediaVars) CONFIG.cssMediaVars = {}
229
- for (const scheme in schemeValues) {
230
- // [data-theme] selector — works for ANY scheme (including custom ones)
231
- const sel = `[data-theme="${scheme}"]`
232
- if (!CONFIG.cssMediaVars[sel]) CONFIG.cssMediaVars[sel] = {}
233
- CONFIG.cssMediaVars[sel][adaptiveVar] = schemeValues[scheme]
234
-
235
- // prefers-color-scheme media query — only browsers know light/dark
236
- if (scheme === 'light' || scheme === 'dark') {
237
- const mq = `@media (prefers-color-scheme: ${scheme})`
238
- if (!CONFIG.cssMediaVars[mq]) CONFIG.cssMediaVars[mq] = {}
239
- CONFIG.cssMediaVars[mq][adaptiveVar] = schemeValues[scheme]
240
- }
241
- }
242
-
243
- // Expose the adaptive var on the wrapper so getColor() returns it
244
- // directly when called without an explicit @scheme key.
245
- obj.var = adaptiveVar
246
- }
247
- }
248
-
249
- return obj
250
- }
251
-
252
- const vp = CONFIG.varPrefix ? CONFIG.varPrefix + '-' : ''
253
- const CSSVar = `--${vp}color-${key}` + (suffix ? `-${suffix}` : '')
254
-
255
- // Resolve dot notation: 'green.08' → take green color at 8% opacity
256
- let resolvedVal = val.value || val
257
- if (isString(resolvedVal) && resolvedVal.includes('.') && !resolvedVal.includes('(')) {
258
- const [colorRef, alphaStr] = resolvedVal.split('.')
259
- const refColor = CONFIG.color[colorRef]
260
- if (refColor && refColor.rgb) {
261
- resolvedVal = `rgba(${refColor.rgb}, ${parseFloat('0.' + alphaStr) || 1})`
262
- }
263
- }
264
-
265
- // Resolve token notation: 'gray-168' / 'gray+50' / 'gray=90' → apply tone to base
266
- if (isString(resolvedVal) && !resolvedVal.includes('(') && !resolvedVal.startsWith('#')) {
267
- const parsed = parseColorToken(resolvedVal)
268
- if (parsed && parsed.name && !parsed.passthrough && !parsed.cssVar) {
269
- const refColor = CONFIG.color[parsed.name]
270
- if (refColor && !refColor.value && !refColor.rgb && (isString(refColor) || isArray(refColor) || isObject(refColor))) {
271
- CONFIG.color[parsed.name] = setColor(refColor, parsed.name)
272
- }
273
- const baseColor = CONFIG.color[parsed.name]
274
- if (baseColor && baseColor.rgb) {
275
- let rgb = baseColor.rgb
276
- if (parsed.tone) rgb = getRgbTone(rgb, parsed.tone)
277
- const alphaVal = parsed.alpha ? parseFloat(parsed.alpha) : 1
278
- resolvedVal = `rgba(${rgb}, ${alphaVal})`
279
- }
280
- }
281
- }
282
-
283
- const colorArr = colorStringToRgbaArray(resolvedVal)
284
- const [r, g, b, a = 1] = colorArr
285
- const alpha = parseFloat(a.toFixed(2))
286
- const rgb = `${r}, ${g}, ${b}`
287
- const value = `rgba(${rgb}, ${alpha})`
288
-
289
- if (CONFIG.useVariable) { CONFIG.cssVars[CSSVar] = value }
290
-
291
- return {
292
- var: CSSVar,
293
- rgb,
294
- alpha,
295
- value
296
- }
297
- }
298
-
299
- export const setGradient = (val, key, suffix) => {
300
- const CONFIG = getActiveConfig()
301
- if (isString(val) && isCSSVar(val)) val = getColor(val.slice(2))
302
-
303
- if (isArray(val)) {
304
- return {
305
- '@light': setGradient(val[0], key, 'light'),
306
- '@dark': setGradient(val[0], key, 'dark')
307
- }
308
- }
309
-
310
- if (isObject(val)) {
311
- const obj = {}
312
- for (const variant in val) obj[variant] = setGradient(val[variant], key, variant.slice(0, 1) === '@' ? variant.slice(1) : variant)
313
- return obj
314
- }
315
-
316
- const vp = CONFIG.varPrefix ? CONFIG.varPrefix + '-' : ''
317
- const CSSVar = `--${vp}gradient-${key}` + (suffix ? `-${suffix}` : '')
318
-
319
- if (CONFIG.useVariable) {
320
- CONFIG.cssVars[CSSVar] = val.value || val
321
- }
322
-
323
- return {
324
- var: CSSVar,
325
- value: val.value || val
326
- }
327
- }
@@ -1,16 +0,0 @@
1
- 'use strict'
2
-
3
- import { merge } from '@symbo.ls/utils'
4
- import { getActiveConfig } from '../factory.js'
5
- import { getDefaultOrFirstKey } from '../utils/index.js'
6
-
7
- export const applyDocument = () => {
8
- const CONFIG = getActiveConfig()
9
- const { document: DOCUMENT, fontFamily: FONT_FAMILY, theme: THEME, typography: TYPOGRAPHY } = CONFIG
10
- return merge(DOCUMENT, {
11
- theme: THEME.document,
12
- fontFamily: getDefaultOrFirstKey(FONT_FAMILY),
13
- fontSize: TYPOGRAPHY.base,
14
- lineHeight: TYPOGRAPHY.lineHeight
15
- })
16
- }