@symbo.ls/scratch 3.5.1 → 3.6.3

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.
@@ -140,25 +140,136 @@ const keySetters = { // eslint-disable-line
140
140
  '.': (theme, value) => setHelpers(theme, value)
141
141
  }
142
142
 
143
+ /**
144
+ * Recursively generates auto-switching CSS vars from all @-scheme content.
145
+ * - @dark/@light: uses prefers-color-scheme media queries + [data-theme] selectors
146
+ * - Custom @schemes (@ocean, @sunset, etc.): uses [data-theme] selectors only
147
+ * - globalTheme forced: sets non-suffixed vars directly in CSS_VARS
148
+ *
149
+ * @param {Object} schemes - { dark: {...}, light: {...}, ocean: {...} }
150
+ * @param {string} varPrefix - var name prefix (e.g. 'document', 'primary-child')
151
+ * @param {Object} CONFIG - active config
152
+ */
153
+ const generateAutoVars = (schemes, varPrefix, CONFIG) => {
154
+ const { CSS_VARS } = CONFIG
155
+ if (!CONFIG.CSS_MEDIA_VARS) CONFIG.CSS_MEDIA_VARS = {}
156
+ const MEDIA_VARS = CONFIG.CSS_MEDIA_VARS
157
+ const globalTheme = CONFIG.globalTheme !== undefined ? CONFIG.globalTheme : 'auto'
158
+
159
+ const result = {}
160
+
161
+ // Collect union of all keys across all schemes
162
+ const allKeys = new Set()
163
+ for (const scheme in schemes) {
164
+ if (schemes[scheme]) for (const k of Object.keys(schemes[scheme])) allKeys.add(k)
165
+ }
166
+
167
+ for (const param of allKeys) {
168
+ const symb = param.slice(0, 1)
169
+
170
+ // Check if any scheme has an object value for this param
171
+ const hasObject = Object.values(schemes).some(s => isObjectLike(s?.[param]))
172
+
173
+ if (symb === '.' && hasObject) {
174
+ // Dot helper (.color-only, .child, etc.) — recurse
175
+ const helperName = param.slice(1)
176
+ const subSchemes = {}
177
+ for (const scheme in schemes) {
178
+ if (isObjectLike(schemes[scheme]?.[param])) subSchemes[scheme] = schemes[scheme][param]
179
+ }
180
+ result[param] = generateAutoVars(subSchemes, `${varPrefix}-${helperName}`, CONFIG)
181
+ } else if (symb === ':' && hasObject) {
182
+ // Pseudo selector (:hover, ::placeholder) — recurse
183
+ const pseudoName = param.replace(/^:+/, '')
184
+ const subSchemes = {}
185
+ for (const scheme in schemes) {
186
+ if (isObjectLike(schemes[scheme]?.[param])) subSchemes[scheme] = schemes[scheme][param]
187
+ }
188
+ result[param] = generateAutoVars(subSchemes, `${varPrefix}-${pseudoName}`, CONFIG)
189
+ } else if (symb !== '@' && symb !== '.' && symb !== ':') {
190
+ // Regular CSS param — generate auto-switching var
191
+ const autoVar = `--theme-${varPrefix}-${param}`
192
+
193
+ if (globalTheme === 'auto') {
194
+ for (const scheme in schemes) {
195
+ const val = schemes[scheme]?.[param]
196
+ if (val === undefined) continue
197
+ const color = getColor(val, `@${scheme}`)
198
+ if (color === undefined) continue
199
+
200
+ // [data-theme] selector for ALL schemes (custom + standard)
201
+ const selector = `[data-theme="${scheme}"]`
202
+ if (!MEDIA_VARS[selector]) MEDIA_VARS[selector] = {}
203
+ MEDIA_VARS[selector][autoVar] = color
204
+
205
+ // prefers-color-scheme media query only for dark/light
206
+ if (scheme === 'dark' || scheme === 'light') {
207
+ const mq = `@media (prefers-color-scheme: ${scheme})`
208
+ if (!MEDIA_VARS[mq]) MEDIA_VARS[mq] = {}
209
+ MEDIA_VARS[mq][autoVar] = color
210
+ }
211
+ }
212
+ } else {
213
+ // Force specific theme — set non-suffixed var directly
214
+ const forced = String(globalTheme).replace(/^'|'$/g, '')
215
+ const source = schemes[forced]?.[param]
216
+ if (source !== undefined) {
217
+ const color = getColor(source, `@${forced}`)
218
+ if (color !== undefined) CSS_VARS[autoVar] = color
219
+ }
220
+ }
221
+
222
+ result[param] = `var(${autoVar})`
223
+ result[`.${param}`] = { [param]: result[param] }
224
+ }
225
+ }
226
+
227
+ if (result.background || result.color || result.backgroundColor) {
228
+ result['.inversed'] = {
229
+ color: result.background || result.backgroundColor,
230
+ background: result.color
231
+ }
232
+ }
233
+
234
+ return result
235
+ }
236
+
143
237
  export const setMediaTheme = (val, key, suffix, prefers) => {
144
238
  const CONFIG = getActiveConfig()
145
239
  const { CSS_VARS } = CONFIG
146
240
  const theme = { value: val }
241
+ const isTopLevel = !suffix && !prefers
147
242
 
148
243
  if (isObjectLike(val)) {
244
+ // At top level: collect all @-schemes and generate auto-switching vars
245
+ if (isTopLevel && CONFIG.useVariable) {
246
+ const schemes = {}
247
+ for (const param in val) {
248
+ if (param.startsWith('@') && isObjectLike(val[param])) {
249
+ schemes[param.slice(1)] = val[param]
250
+ }
251
+ }
252
+
253
+ if (Object.keys(schemes).length) {
254
+ const autoResult = generateAutoVars(schemes, key, CONFIG)
255
+ Object.assign(theme, autoResult)
256
+ }
257
+ }
258
+
149
259
  for (const param in val) {
150
260
  const symb = param.slice(0, 1)
151
261
  const value = val[param]
152
262
  if (symb === '@' || symb === ':' || symb === '.') {
153
263
  const hasPrefers = symb === '@' && param
154
264
  theme[param] = setMediaTheme(value, key, param, prefers || hasPrefers)
155
- } else {
265
+ } else if (!isTopLevel) {
156
266
  const color = getColor(value, prefers)
157
267
  const metaSuffixes = [...new Set([prefers, suffix].filter(v => v).map(v => v.slice(1)))]
158
268
  const varmetaSuffixName = metaSuffixes.length ? '-' + metaSuffixes.join('-') : ''
159
269
  const CSSVar = `--theme-${key}${varmetaSuffixName}-${param}`
160
270
  if (CONFIG.useVariable) {
161
- CSS_VARS[CSSVar] = color
271
+ // Suffixed vars (--theme-key-dark-param) only when opted in
272
+ if (CONFIG.useThemeSuffixedVars) CSS_VARS[CSSVar] = color
162
273
  theme[param] = `var(${CSSVar})`
163
274
  } else {
164
275
  theme[param] = color
@@ -167,7 +278,8 @@ export const setMediaTheme = (val, key, suffix, prefers) => {
167
278
  }
168
279
  }
169
280
 
170
- if (theme.background || theme.color || theme.backgroundColor) {
281
+ // Only add .inversed if not already set by generateAutoVars
282
+ if (!theme['.inversed'] && (theme.background || theme.color || theme.backgroundColor)) {
171
283
  theme['.inversed'] = {
172
284
  color: theme.background || theme.backgroundColor,
173
285
  background: theme.color
@@ -192,9 +304,8 @@ const recursiveTheme = val => {
192
304
  const symb = param.slice(0, 1)
193
305
  if (isObjectLike(val[param])) {
194
306
  if (symb === '@') {
195
- const query = CONFIG.MEDIA[param.slice(1)]
196
- const media = '@media ' + (query === 'print' ? `${query}` : `screen and ${query}`)
197
- obj[media] = recursiveTheme(val[param])
307
+ // Skip all @-schemes — CSS vars + data-theme handle switching
308
+ continue
198
309
  } else if (symb === ':') {
199
310
  obj[`&${param}`] = recursiveTheme(val[param])
200
311
  }
@@ -239,8 +350,10 @@ export const getMediaTheme = (value, modifier) => {
239
350
  const [themeName, ...themeModifiers] = isArray(value) ? value : value.split(' ')
240
351
  let themeValue = activeConfig.THEME[themeName]
241
352
 
242
- if (themeValue && (themeModifiers || modifier)) {
243
- themeValue = findModifier(themeValue, themeModifiers.length ? themeModifiers : modifier)
353
+ if (themeValue && themeModifiers.length) {
354
+ themeValue = findModifier(themeValue, themeModifiers)
355
+ } else if (themeValue && modifier) {
356
+ themeValue = findModifier(themeValue, modifier)
244
357
  }
245
358
 
246
359
  const resolvedTheme = recursiveTheme(themeValue)
@@ -246,7 +246,7 @@ export function transformSize(propertyName, val, props = {}, opts = {}) {
246
246
 
247
247
  const shouldScaleBoxSize = props.scaleBoxSize
248
248
  const isBoxSize = checkIfBoxSize(propertyName)
249
- if (!shouldScaleBoxSize && isBoxSize) {
249
+ if (!shouldScaleBoxSize && isBoxSize && !opts.ratio) {
250
250
  value = splitSpacedValue(value)
251
251
  }
252
252
  }
package/src/utils/font.js CHANGED
@@ -1,5 +1,13 @@
1
1
  'use strict'
2
2
 
3
+ export const resolveFileUrl = (url, files) => {
4
+ if (!url || !files) return null
5
+ try { new URL(url); return null } catch (e) { }
6
+ const file = files[url]
7
+ if (file) return file.content && file.content.src
8
+ return null
9
+ }
10
+
3
11
  export const getDefaultOrFirstKey = (LIBRARY, key) => {
4
12
  if (LIBRARY[key]) return LIBRARY[key].value
5
13
  if (LIBRARY.default) return LIBRARY[LIBRARY.default].value
@@ -53,11 +61,12 @@ export const setCustomFontMedia = (
53
61
  ) => `@font-face {${setCustomFont(name, url, weight, options)}
54
62
  }`
55
63
 
56
- export const getFontFaceEach = (name, weights) => {
64
+ export const getFontFaceEach = (name, weights, files) => {
57
65
  const keys = Object.keys(weights)
58
66
  return keys.map((key) => {
59
67
  const { url, fontWeight } = weights[key]
60
- return setCustomFont(name, url, fontWeight)
68
+ const resolvedUrl = resolveFileUrl(url, files) || url
69
+ return setCustomFont(name, resolvedUrl, fontWeight)
61
70
  })
62
71
  }
63
72
 
@@ -66,22 +75,24 @@ export const getFontFace = (LIBRARY) => {
66
75
  return keys.map((key) => getFontFaceEach(key, LIBRARY[key].value))
67
76
  }
68
77
 
69
- export const getFontFaceEachString = (name, weights) => {
78
+ export const getFontFaceEachString = (name, weights, files) => {
70
79
  if (weights && weights.isVariable) {
71
- if (isGoogleFontsUrl(weights.url)) {
72
- return setFontImport(weights.url)
80
+ const url = resolveFileUrl(weights.url, files) || weights.url
81
+ if (isGoogleFontsUrl(url)) {
82
+ return setFontImport(url)
73
83
  }
74
- return setCustomFontMedia(name, weights.url, weights.fontWeight, {
84
+ return setCustomFontMedia(name, url, weights.fontWeight, {
75
85
  fontStretch: weights.fontStretch,
76
86
  fontDisplay: weights.fontDisplay || 'swap'
77
87
  })
78
88
  }
79
89
  const isArr = weights[0]
80
- if (isArr) return getFontFaceEach(name, weights).map(setInCustomFontMedia)
81
- return setCustomFontMedia(name, weights.url)
90
+ if (isArr) return getFontFaceEach(name, weights, files).map(setInCustomFontMedia)
91
+ const url = resolveFileUrl(weights.url, files) || weights.url
92
+ return setCustomFontMedia(name, url)
82
93
  }
83
94
 
84
- export const getFontFaceString = (LIBRARY) => {
95
+ export const getFontFaceString = (LIBRARY, files) => {
85
96
  const keys = Object.keys(LIBRARY)
86
- return keys.map((key) => getFontFaceEachString(key, LIBRARY[key].value))
97
+ return keys.map((key) => getFontFaceEachString(key, LIBRARY[key].value, files))
87
98
  }