@symbo.ls/scratch 3.8.9 → 3.14.1
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/README.md +2 -2
- package/dist/cjs/defaultConfig/animation.js +4 -2
- package/dist/cjs/defaultConfig/cases.js +4 -2
- package/dist/cjs/defaultConfig/class.js +4 -2
- package/dist/cjs/defaultConfig/color.js +6 -9
- package/dist/cjs/defaultConfig/document.js +4 -2
- package/dist/cjs/defaultConfig/font-family.js +8 -12
- package/dist/cjs/defaultConfig/font.js +4 -11
- package/dist/cjs/defaultConfig/grid.js +5 -3
- package/dist/cjs/defaultConfig/icons.js +6 -4
- package/dist/cjs/defaultConfig/index.js +200 -24
- package/dist/cjs/defaultConfig/media.js +4 -2
- package/dist/cjs/defaultConfig/responsive.js +6 -4
- package/dist/cjs/defaultConfig/sequence.js +4 -2
- package/dist/cjs/defaultConfig/shadow.js +4 -2
- package/dist/cjs/defaultConfig/spacing.js +56 -7
- package/dist/cjs/defaultConfig/svg.js +6 -4
- package/dist/cjs/defaultConfig/templates.js +4 -2
- package/dist/cjs/defaultConfig/theme.js +4 -14
- package/dist/cjs/defaultConfig/timing.js +36 -5
- package/dist/cjs/defaultConfig/typography.js +36 -5
- package/dist/cjs/defaultConfig/unit.js +4 -2
- package/dist/cjs/factory.js +341 -40
- package/dist/cjs/index.js +6333 -11
- package/dist/cjs/package.json +4 -0
- package/dist/cjs/set.js +5614 -158
- package/dist/cjs/system/color.js +4481 -104
- package/dist/cjs/system/document.js +4371 -11
- package/dist/cjs/system/font.js +4401 -28
- package/dist/cjs/system/index.js +5748 -11
- package/dist/cjs/system/reset.js +4445 -21
- package/dist/cjs/system/shadow.js +4832 -41
- package/dist/cjs/system/spacing.js +4752 -39
- package/dist/cjs/system/svg.js +4437 -47
- package/dist/cjs/system/theme.js +4526 -335
- package/dist/cjs/system/timing.js +4695 -19
- package/dist/cjs/system/typography.js +4755 -33
- package/dist/cjs/tests/index.js +4 -2
- package/dist/cjs/transforms/index.js +5019 -134
- package/dist/cjs/utils/color.js +47 -66
- package/dist/cjs/utils/font.js +25 -46
- package/dist/cjs/utils/index.js +5068 -8
- package/dist/cjs/utils/sequence.js +4423 -35
- package/dist/cjs/utils/sprite.js +353 -12
- package/dist/cjs/utils/theme.js +3 -1
- package/dist/cjs/utils/unit.js +4 -2
- package/dist/cjs/utils/var.js +4390 -42
- package/index.js +1 -0
- package/package.json +11 -14
- package/src/defaultConfig/class.js +2 -1
- package/src/defaultConfig/font-family.js +3 -3
- package/src/defaultConfig/icons.js +1 -1
- package/src/defaultConfig/svg.js +1 -1
- package/src/defaultConfig/timing.js +1 -1
- package/src/factory.js +85 -13
- package/src/index.js +16 -5
- package/src/set.js +156 -63
- package/src/system/color.js +113 -12
- package/src/system/document.js +3 -3
- package/src/system/font.js +5 -5
- package/src/system/reset.js +41 -8
- package/src/system/shadow.js +4 -3
- package/src/system/spacing.js +3 -3
- package/src/system/svg.js +42 -5
- package/src/system/theme.js +87 -64
- package/src/system/timing.js +2 -2
- package/src/system/typography.js +12 -3
- package/src/transforms/index.js +4 -4
- package/src/utils/color.js +2 -1
- package/src/utils/font.js +7 -1
- package/src/utils/sequence.js +46 -29
- package/src/utils/sprite.js +44 -16
- package/src/utils/var.js +27 -9
- package/dist/esm/defaultConfig/animation.js +0 -4
- package/dist/esm/defaultConfig/cases.js +0 -4
- package/dist/esm/defaultConfig/class.js +0 -5
- package/dist/esm/defaultConfig/color.js +0 -11
- package/dist/esm/defaultConfig/document.js +0 -4
- package/dist/esm/defaultConfig/font-family.js +0 -18
- package/dist/esm/defaultConfig/font.js +0 -13
- package/dist/esm/defaultConfig/grid.js +0 -5
- package/dist/esm/defaultConfig/icons.js +0 -6
- package/dist/esm/defaultConfig/index.js +0 -25
- package/dist/esm/defaultConfig/media.js +0 -9
- package/dist/esm/defaultConfig/responsive.js +0 -30
- package/dist/esm/defaultConfig/sequence.js +0 -29
- package/dist/esm/defaultConfig/shadow.js +0 -4
- package/dist/esm/defaultConfig/spacing.js +0 -18
- package/dist/esm/defaultConfig/svg.js +0 -6
- package/dist/esm/defaultConfig/templates.js +0 -4
- package/dist/esm/defaultConfig/theme.js +0 -16
- package/dist/esm/defaultConfig/timing.js +0 -17
- package/dist/esm/defaultConfig/typography.js +0 -21
- package/dist/esm/defaultConfig/unit.js +0 -6
- package/dist/esm/factory.js +0 -60
- package/dist/esm/index.js +0 -12
- package/dist/esm/set.js +0 -219
- package/dist/esm/system/color.js +0 -193
- package/dist/esm/system/document.js +0 -16
- package/dist/esm/system/font.js +0 -58
- package/dist/esm/system/index.js +0 -10
- package/dist/esm/system/reset.js +0 -67
- package/dist/esm/system/shadow.js +0 -91
- package/dist/esm/system/spacing.js +0 -121
- package/dist/esm/system/svg.js +0 -86
- package/dist/esm/system/theme.js +0 -480
- package/dist/esm/system/timing.js +0 -32
- package/dist/esm/system/typography.js +0 -85
- package/dist/esm/tests/index.js +0 -8
- package/dist/esm/transforms/index.js +0 -216
- package/dist/esm/utils/color.js +0 -192
- package/dist/esm/utils/font.js +0 -92
- package/dist/esm/utils/index.js +0 -7
- package/dist/esm/utils/sequence.js +0 -303
- package/dist/esm/utils/sprite.js +0 -65
- package/dist/esm/utils/theme.js +0 -9
- package/dist/esm/utils/unit.js +0 -59
- package/dist/esm/utils/var.js +0 -82
- package/dist/iife/index.js +0 -3204
package/src/system/color.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
import { isObject, isArray, isString } from '@
|
|
3
|
+
import { isObject, isArray, isString } from '@symbo.ls/utils'
|
|
4
4
|
import { getActiveConfig } from '../factory.js'
|
|
5
5
|
|
|
6
6
|
import {
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
getRgbTone,
|
|
9
9
|
isCSSVar,
|
|
10
10
|
parseColorToken
|
|
11
|
-
} from '../utils'
|
|
11
|
+
} from '../utils/index.js'
|
|
12
12
|
|
|
13
13
|
export const getColor = (value, key, config) => {
|
|
14
14
|
const CONFIG = config || getActiveConfig()
|
|
@@ -99,11 +99,21 @@ export const getMediaColor = (value, globalTheme, config) => {
|
|
|
99
99
|
|
|
100
100
|
if (isObj && val.value) return getColor(value, `@${globalTheme}`, config)
|
|
101
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})`
|
|
102
108
|
if (globalTheme) return getColor(value, `@${globalTheme}`, config)
|
|
103
109
|
else {
|
|
110
|
+
// Legacy fallback when adaptive var is unavailable (useVariable: false).
|
|
111
|
+
// Only @-prefixed keys are scheme variants — skip metadata like `var`.
|
|
104
112
|
const obj = {}
|
|
105
113
|
for (const mediaName in val) {
|
|
114
|
+
if (mediaName.charCodeAt(0) !== 64 /* @ */) continue
|
|
106
115
|
const query = CONFIG.media[mediaName.slice(1)]
|
|
116
|
+
if (!query) continue
|
|
107
117
|
const media = '@media ' + (query === 'print' ? `${query}` : `screen and ${query}`)
|
|
108
118
|
obj[media] = getColor(value, mediaName, config)
|
|
109
119
|
}
|
|
@@ -128,7 +138,16 @@ export const setColor = (val, key, suffix) => {
|
|
|
128
138
|
)) {
|
|
129
139
|
// Handle space-separated format: '--colorName alpha' (e.g. '--gray1 1')
|
|
130
140
|
const parts = rawRef.split(' ')
|
|
131
|
-
|
|
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
|
+
}
|
|
132
151
|
if (refColor && refColor.value) {
|
|
133
152
|
let rgb = refColor.rgb
|
|
134
153
|
const alpha = parts[1] !== undefined ? parts[1] : '1'
|
|
@@ -160,11 +179,10 @@ export const setColor = (val, key, suffix) => {
|
|
|
160
179
|
}
|
|
161
180
|
}
|
|
162
181
|
|
|
182
|
+
// Normalize array shorthand `[lightVal, darkVal]` to scheme-keyed object
|
|
183
|
+
// so the multi-scheme branch below handles both cases uniformly.
|
|
163
184
|
if (isArray(val)) {
|
|
164
|
-
|
|
165
|
-
'@light': setColor(val[0], key, 'light'),
|
|
166
|
-
'@dark': setColor(val[1], key, 'dark')
|
|
167
|
-
}
|
|
185
|
+
val = { '@light': val[0], '@dark': val[1] }
|
|
168
186
|
}
|
|
169
187
|
|
|
170
188
|
if (isObject(val)) {
|
|
@@ -176,17 +194,99 @@ export const setColor = (val, key, suffix) => {
|
|
|
176
194
|
variant.slice(0, 1) === '@' ? variant.slice(1) : variant
|
|
177
195
|
)
|
|
178
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
|
+
|
|
179
249
|
return obj
|
|
180
250
|
}
|
|
181
251
|
|
|
182
|
-
const
|
|
183
|
-
const
|
|
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)
|
|
184
284
|
const [r, g, b, a = 1] = colorArr
|
|
185
285
|
const alpha = parseFloat(a.toFixed(2))
|
|
186
286
|
const rgb = `${r}, ${g}, ${b}`
|
|
187
287
|
const value = `rgba(${rgb}, ${alpha})`
|
|
188
288
|
|
|
189
|
-
if (CONFIG.useVariable) { CONFIG.
|
|
289
|
+
if (CONFIG.useVariable) { CONFIG.cssVars[CSSVar] = value }
|
|
190
290
|
|
|
191
291
|
return {
|
|
192
292
|
var: CSSVar,
|
|
@@ -213,10 +313,11 @@ export const setGradient = (val, key, suffix) => {
|
|
|
213
313
|
return obj
|
|
214
314
|
}
|
|
215
315
|
|
|
216
|
-
const
|
|
316
|
+
const vp = CONFIG.varPrefix ? CONFIG.varPrefix + '-' : ''
|
|
317
|
+
const CSSVar = `--${vp}gradient-${key}` + (suffix ? `-${suffix}` : '')
|
|
217
318
|
|
|
218
319
|
if (CONFIG.useVariable) {
|
|
219
|
-
CONFIG.
|
|
320
|
+
CONFIG.cssVars[CSSVar] = val.value || val
|
|
220
321
|
}
|
|
221
322
|
|
|
222
323
|
return {
|
package/src/system/document.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
import { merge } from '@
|
|
3
|
+
import { merge } from '@symbo.ls/utils'
|
|
4
4
|
import { getActiveConfig } from '../factory.js'
|
|
5
|
-
import { getDefaultOrFirstKey } from '../utils'
|
|
5
|
+
import { getDefaultOrFirstKey } from '../utils/index.js'
|
|
6
6
|
|
|
7
7
|
export const applyDocument = () => {
|
|
8
8
|
const CONFIG = getActiveConfig()
|
|
9
|
-
const { document: DOCUMENT,
|
|
9
|
+
const { document: DOCUMENT, fontFamily: FONT_FAMILY, theme: THEME, typography: TYPOGRAPHY } = CONFIG
|
|
10
10
|
return merge(DOCUMENT, {
|
|
11
11
|
theme: THEME.document,
|
|
12
12
|
fontFamily: getDefaultOrFirstKey(FONT_FAMILY),
|
package/src/system/font.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
import { isObject, isArray } from '@
|
|
4
|
-
import { arrayzeValue } from '@symbo.ls/
|
|
3
|
+
import { isObject, isArray } from '@symbo.ls/utils'
|
|
4
|
+
import { arrayzeValue } from '@symbo.ls/utils'
|
|
5
5
|
import { getActiveConfig } from '../factory.js'
|
|
6
6
|
|
|
7
7
|
import {
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
setCustomFontMedia,
|
|
12
12
|
setFontImport,
|
|
13
13
|
resolveFileUrl
|
|
14
|
-
} from '../utils'
|
|
14
|
+
} from '../utils/index.js'
|
|
15
15
|
|
|
16
16
|
export const setFont = (val, key) => {
|
|
17
17
|
const CONFIG = getActiveConfig()
|
|
@@ -48,13 +48,13 @@ export const setFont = (val, key) => {
|
|
|
48
48
|
|
|
49
49
|
export const getFontFamily = (key, factory) => {
|
|
50
50
|
const CONFIG = getActiveConfig()
|
|
51
|
-
const {
|
|
51
|
+
const { fontFamily: FONT_FAMILY } = CONFIG
|
|
52
52
|
return getDefaultOrFirstKey(factory || FONT_FAMILY, key)
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
export const setFontFamily = (val, key) => {
|
|
56
56
|
const CONFIG = getActiveConfig()
|
|
57
|
-
const {
|
|
57
|
+
const { fontFamily: FONT_FAMILY, fontFamilyTypes: FONT_FAMILY_TYPES } = CONFIG
|
|
58
58
|
let { value, type } = val
|
|
59
59
|
if (val.isDefault) FONT_FAMILY.default = key
|
|
60
60
|
|
package/src/system/reset.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
import { deepMerge, merge, overwriteDeep } from '@
|
|
3
|
+
import { deepMerge, merge, overwriteDeep } from '@symbo.ls/utils'
|
|
4
4
|
import { getActiveConfig } from '../factory.js'
|
|
5
5
|
import { getMediaTheme } from './theme.js'
|
|
6
6
|
|
|
@@ -34,10 +34,31 @@ export const applyReset = (reset = {}) => {
|
|
|
34
34
|
: {}
|
|
35
35
|
if (RESET.html) overwriteDeep(RESET.html, globalTheme)
|
|
36
36
|
|
|
37
|
+
// Generate html fontSize overrides for typography media breakpoints
|
|
38
|
+
if (TYPOGRAPHY.unit) {
|
|
39
|
+
const { media: MEDIA } = CONFIG
|
|
40
|
+
for (const key in TYPOGRAPHY) {
|
|
41
|
+
if (key.charAt(0) !== '@') continue
|
|
42
|
+
const mediaTypo = TYPOGRAPHY[key]
|
|
43
|
+
if (!mediaTypo) continue
|
|
44
|
+
// Only override html fontSize if this media explicitly changes the unit
|
|
45
|
+
if (mediaTypo.unit === TYPOGRAPHY.unit || !mediaTypo.unit) continue
|
|
46
|
+
const mediaUnit = mediaTypo.unit
|
|
47
|
+
const mediaBase = mediaTypo.base || TYPOGRAPHY.base
|
|
48
|
+
const mediaBrowserDefault = mediaTypo.browserDefault || TYPOGRAPHY.browserDefault
|
|
49
|
+
const mediaName = key.slice(1)
|
|
50
|
+
const query = MEDIA[mediaName]
|
|
51
|
+
if (!query) continue
|
|
52
|
+
const mediaKey = '@media ' + (query === 'print' ? query : 'screen and ' + query)
|
|
53
|
+
if (!RESET[mediaKey]) RESET[mediaKey] = {}
|
|
54
|
+
if (!RESET[mediaKey].html) RESET[mediaKey].html = {}
|
|
55
|
+
RESET[mediaKey].html.fontSize = (mediaBase / mediaBrowserDefault) + mediaUnit
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
37
59
|
return deepMerge(merge(RESET, reset), {
|
|
38
60
|
html: {
|
|
39
61
|
position: 'absolute',
|
|
40
|
-
// overflow: 'hidden',
|
|
41
62
|
width: '100%',
|
|
42
63
|
height: '100%',
|
|
43
64
|
top: '0',
|
|
@@ -48,7 +69,9 @@ export const applyReset = (reset = {}) => {
|
|
|
48
69
|
|
|
49
70
|
...globalTheme,
|
|
50
71
|
|
|
51
|
-
fontSize: TYPOGRAPHY.
|
|
72
|
+
fontSize: TYPOGRAPHY.unit
|
|
73
|
+
? (TYPOGRAPHY.base / TYPOGRAPHY.browserDefault) + TYPOGRAPHY.unit
|
|
74
|
+
: TYPOGRAPHY.browserDefault + 'px',
|
|
52
75
|
|
|
53
76
|
fontFamily: DOCUMENT.fontFamily,
|
|
54
77
|
lineHeight: DOCUMENT.lineHeight
|
|
@@ -71,15 +94,25 @@ export const applyReset = (reset = {}) => {
|
|
|
71
94
|
color: 'currentColor'
|
|
72
95
|
},
|
|
73
96
|
|
|
74
|
-
|
|
97
|
+
button: {
|
|
98
|
+
color: 'inherit',
|
|
99
|
+
font: 'inherit',
|
|
100
|
+
background: 'transparent',
|
|
101
|
+
border: 'none',
|
|
102
|
+
cursor: 'pointer',
|
|
103
|
+
appearance: 'none',
|
|
104
|
+
WebkitAppearance: 'none'
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
'input, select, textarea': {
|
|
108
|
+
color: 'inherit',
|
|
109
|
+
font: 'inherit'
|
|
110
|
+
},
|
|
111
|
+
|
|
75
112
|
fieldset: {
|
|
76
113
|
border: 0,
|
|
77
114
|
padding: 0,
|
|
78
115
|
margin: 0
|
|
79
|
-
},
|
|
80
|
-
|
|
81
|
-
'select, input': {
|
|
82
|
-
fontFamily: DOCUMENT.fontFamily
|
|
83
116
|
}
|
|
84
117
|
})
|
|
85
118
|
}
|
package/src/system/shadow.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
isObject,
|
|
10
10
|
isString,
|
|
11
11
|
isArray
|
|
12
|
-
} from '@
|
|
12
|
+
} from '@symbo.ls/utils'
|
|
13
13
|
|
|
14
14
|
export const setShadow = (value, key, suffix, prefers) => {
|
|
15
15
|
const CONFIG = getActiveConfig()
|
|
@@ -53,9 +53,10 @@ export const setShadow = (value, key, suffix, prefers) => {
|
|
|
53
53
|
}).join(', ')
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
const
|
|
56
|
+
const vp = CONFIG.varPrefix ? CONFIG.varPrefix + '-' : ''
|
|
57
|
+
const CSSVar = `--${vp}shadow-${key}` + (suffix ? `-${suffix}` : '')
|
|
57
58
|
|
|
58
|
-
if (CONFIG.useVariable) { CONFIG.
|
|
59
|
+
if (CONFIG.useVariable) { CONFIG.cssVars[CSSVar] = value }
|
|
59
60
|
|
|
60
61
|
return {
|
|
61
62
|
var: CSSVar,
|
package/src/system/spacing.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
import { arrayzeValue } from '@symbo.ls/
|
|
4
|
-
import { isArray, isString, merge } from '@
|
|
3
|
+
import { arrayzeValue } from '@symbo.ls/utils'
|
|
4
|
+
import { isArray, isString, merge } from '@symbo.ls/utils'
|
|
5
5
|
|
|
6
6
|
import { getActiveConfig } from '../factory.js'
|
|
7
7
|
import {
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
generateSequence,
|
|
12
12
|
getFnPrefixAndValue,
|
|
13
13
|
getSequenceValuePropertyPair
|
|
14
|
-
} from '../utils'
|
|
14
|
+
} from '../utils/index.js'
|
|
15
15
|
|
|
16
16
|
const runThroughMedia = (FACTORY) => {
|
|
17
17
|
for (const prop in FACTORY) {
|
package/src/system/svg.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
import { document } from '@
|
|
4
|
-
import { generateSprite, convertSvgToSymbol } from '../utils'
|
|
3
|
+
import { document } from '@symbo.ls/utils'
|
|
4
|
+
import { generateSprite, convertSvgToSymbol } from '../utils/index.js'
|
|
5
5
|
import { getActiveConfig } from '../factory.js'
|
|
6
6
|
|
|
7
7
|
const DEF_OPTIONS = {
|
|
@@ -30,7 +30,7 @@ export const appendSVGSprite = (LIBRARY, options = DEF_OPTIONS) => {
|
|
|
30
30
|
|
|
31
31
|
export const setSvgIcon = (val, key) => {
|
|
32
32
|
const CONFIG = getActiveConfig()
|
|
33
|
-
if (CONFIG.useIconSprite && !CONFIG.
|
|
33
|
+
if (CONFIG.useIconSprite && !CONFIG.semanticIcons?.[key]) {
|
|
34
34
|
return setSVG(val, key)
|
|
35
35
|
} return val
|
|
36
36
|
}
|
|
@@ -57,6 +57,32 @@ const createSVGSpriteElement = (doc, options = { isRoot: true }) => {
|
|
|
57
57
|
return svgElem
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
// Parse SVG sprite string using XML parser to correctly handle self-closing tags.
|
|
61
|
+
// HTML parser treats <line .../> as <line ...> (ignores /), nesting siblings.
|
|
62
|
+
const parseSVGSprite = (doc, svgString) => {
|
|
63
|
+
const DOMParserCtor = (typeof DOMParser !== 'undefined') ? DOMParser : null
|
|
64
|
+
if (DOMParserCtor) {
|
|
65
|
+
const wrapped = `<svg xmlns="http://www.w3.org/2000/svg">${svgString}</svg>`
|
|
66
|
+
const parser = new DOMParserCtor()
|
|
67
|
+
const parsed = parser.parseFromString(wrapped, 'image/svg+xml')
|
|
68
|
+
// Check for parse errors — if the SVG is malformed, fall back to innerHTML
|
|
69
|
+
if (parsed.querySelector('parsererror')) return null
|
|
70
|
+
return parsed.documentElement
|
|
71
|
+
}
|
|
72
|
+
return null
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Move all children from a parsed SVG element into a target element.
|
|
76
|
+
// Uses importNode to cross document boundaries, then removes the source child
|
|
77
|
+
// so the loop terminates.
|
|
78
|
+
const moveChildren = (doc, from, to) => {
|
|
79
|
+
while (from.firstChild) {
|
|
80
|
+
const child = from.firstChild
|
|
81
|
+
to.appendChild(doc.importNode(child, true))
|
|
82
|
+
child.remove()
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
60
86
|
const appendSVG = (lib, options = DEF_OPTIONS) => {
|
|
61
87
|
const CONFIG = getActiveConfig()
|
|
62
88
|
const doc = options.document || document
|
|
@@ -73,8 +99,12 @@ const appendSVG = (lib, options = DEF_OPTIONS) => {
|
|
|
73
99
|
|
|
74
100
|
const spriteHtml = `<svg aria-hidden="true" width="0" height="0" style="position:absolute" id="svgSprite">${SVGsprite}</svg>`
|
|
75
101
|
|
|
102
|
+
const parsed = parseSVGSprite(doc, SVGsprite)
|
|
103
|
+
|
|
76
104
|
if (exists) {
|
|
77
|
-
if (
|
|
105
|
+
if (parsed) {
|
|
106
|
+
moveChildren(doc, parsed, exists)
|
|
107
|
+
} else if (doc.body.insertAdjacentHTML) {
|
|
78
108
|
exists.insertAdjacentHTML('beforeend', SVGsprite)
|
|
79
109
|
} else {
|
|
80
110
|
const tempSVG = createSVGSpriteElement(doc, { isRoot: false })
|
|
@@ -82,7 +112,13 @@ const appendSVG = (lib, options = DEF_OPTIONS) => {
|
|
|
82
112
|
exists.append(...tempSVG.children)
|
|
83
113
|
}
|
|
84
114
|
} else {
|
|
85
|
-
if (
|
|
115
|
+
if (parsed) {
|
|
116
|
+
const svgSpriteDOM = createSVGSpriteElement(doc)
|
|
117
|
+
if (svgSpriteDOM && svgSpriteDOM.nodeType) {
|
|
118
|
+
moveChildren(doc, parsed, svgSpriteDOM)
|
|
119
|
+
doc.body.prepend(svgSpriteDOM)
|
|
120
|
+
}
|
|
121
|
+
} else if (doc.body.insertAdjacentHTML) {
|
|
86
122
|
doc.body.insertAdjacentHTML('afterbegin', spriteHtml)
|
|
87
123
|
} else {
|
|
88
124
|
const svgSpriteDOM = createSVGSpriteElement(doc)
|
|
@@ -92,4 +128,5 @@ const appendSVG = (lib, options = DEF_OPTIONS) => {
|
|
|
92
128
|
}
|
|
93
129
|
}
|
|
94
130
|
}
|
|
131
|
+
|
|
95
132
|
}
|
package/src/system/theme.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
import { getColor } from './color'
|
|
3
|
+
import { getColor } from './color.js'
|
|
4
4
|
import { getActiveConfig } from '../factory.js'
|
|
5
5
|
import { isCSSVar } from '../utils/color.js'
|
|
6
6
|
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
isString,
|
|
10
10
|
isObjectLike,
|
|
11
11
|
isArray
|
|
12
|
-
} from '@
|
|
12
|
+
} from '@symbo.ls/utils'
|
|
13
13
|
|
|
14
14
|
// Common CSS named colors — used to distinguish valid CSS color names
|
|
15
15
|
// from unresolved design-system tokens (e.g. 'blackRussian', 'grayMid')
|
|
@@ -43,6 +43,20 @@ const CSS_NAMED_COLORS = new Set([
|
|
|
43
43
|
'whitesmoke', 'yellowgreen', 'rebeccapurple'
|
|
44
44
|
])
|
|
45
45
|
|
|
46
|
+
// A theme param is treated as a color-producing property when its name
|
|
47
|
+
// contains one of these substrings. Mirrors the check in `setThemeValue`
|
|
48
|
+
// so the broken-scheme pre-scan skips non-color props like `borderStyle`,
|
|
49
|
+
// `boxShadow`, `backdropFilter`, `cursor`, etc. that would otherwise trip
|
|
50
|
+
// the `/^[a-z][a-zA-Z]+$/` heuristic with values like 'solid' or 'pointer'.
|
|
51
|
+
const COLOR_PARAM_TOKENS = ['color', 'Color', 'background', 'Background', 'fill', 'Fill', 'stroke', 'Stroke']
|
|
52
|
+
|
|
53
|
+
const isColorParam = (param) => {
|
|
54
|
+
for (let i = 0; i < COLOR_PARAM_TOKENS.length; i++) {
|
|
55
|
+
if (param.includes(COLOR_PARAM_TOKENS[i])) return true
|
|
56
|
+
}
|
|
57
|
+
return false
|
|
58
|
+
}
|
|
59
|
+
|
|
46
60
|
const setThemeValue = theme => {
|
|
47
61
|
const value = {}
|
|
48
62
|
const { state, media, helpers, ...rest } = theme
|
|
@@ -159,7 +173,8 @@ export const setTheme = (val, key) => {
|
|
|
159
173
|
|
|
160
174
|
const { state, media, helpers } = val
|
|
161
175
|
const value = setThemeValue(val, key)
|
|
162
|
-
const
|
|
176
|
+
const vp = CONFIG.varPrefix ? CONFIG.varPrefix + '-' : ''
|
|
177
|
+
const CSSvar = `--${vp}theme-${key}`
|
|
163
178
|
|
|
164
179
|
setSelectors(val, value)
|
|
165
180
|
setMedia(val, value)
|
|
@@ -185,9 +200,9 @@ const keySetters = { // eslint-disable-line
|
|
|
185
200
|
* @param {Object} CONFIG - active config
|
|
186
201
|
*/
|
|
187
202
|
const generateAutoVars = (schemes, varPrefix, CONFIG) => {
|
|
188
|
-
const { CSS_VARS } = CONFIG
|
|
189
|
-
if (!CONFIG.
|
|
190
|
-
const MEDIA_VARS = CONFIG.
|
|
203
|
+
const { cssVars: CSS_VARS } = CONFIG
|
|
204
|
+
if (!CONFIG.cssMediaVars) CONFIG.cssMediaVars = {}
|
|
205
|
+
const MEDIA_VARS = CONFIG.cssMediaVars
|
|
191
206
|
const globalTheme = CONFIG.globalTheme !== undefined ? CONFIG.globalTheme : 'auto'
|
|
192
207
|
|
|
193
208
|
const result = {}
|
|
@@ -198,23 +213,28 @@ const generateAutoVars = (schemes, varPrefix, CONFIG) => {
|
|
|
198
213
|
if (schemes[scheme]) for (const k of Object.keys(schemes[scheme])) allKeys.add(k)
|
|
199
214
|
}
|
|
200
215
|
|
|
201
|
-
// Pre-scan: detect schemes with unresolvable color values
|
|
202
|
-
//
|
|
203
|
-
//
|
|
216
|
+
// Pre-scan: detect schemes with unresolvable color values. If any color in
|
|
217
|
+
// a scheme fails to resolve, skip the ENTIRE scheme to avoid partial theme
|
|
218
|
+
// application (e.g. white text without a dark background). Runs regardless
|
|
219
|
+
// of `globalTheme` because every scheme is always emitted now.
|
|
220
|
+
//
|
|
221
|
+
// Only check params whose names indicate colors — otherwise legitimate
|
|
222
|
+
// non-color values like `borderStyle: 'solid'` or `cursor: 'pointer'`
|
|
223
|
+
// match the `/^[a-z][a-zA-Z]+$/` heuristic and falsely break the scheme,
|
|
224
|
+
// which would silently drop the entire theme's CSS vars.
|
|
204
225
|
const brokenSchemes = new Set()
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
}
|
|
226
|
+
for (const param of allKeys) {
|
|
227
|
+
const symb = param.slice(0, 1)
|
|
228
|
+
if (symb === '@' || symb === '.' || symb === ':') continue
|
|
229
|
+
if (!isColorParam(param)) continue
|
|
230
|
+
for (const scheme in schemes) {
|
|
231
|
+
if (brokenSchemes.has(scheme)) continue
|
|
232
|
+
const val = schemes[scheme]?.[param]
|
|
233
|
+
if (val === undefined) continue
|
|
234
|
+
const color = getColor(val, `@${scheme}`)
|
|
235
|
+
if (color === undefined) continue
|
|
236
|
+
if (isString(color) && /^[a-z][a-zA-Z]+$/.test(color) && !CSS_NAMED_COLORS.has(color)) {
|
|
237
|
+
brokenSchemes.add(scheme)
|
|
218
238
|
}
|
|
219
239
|
}
|
|
220
240
|
}
|
|
@@ -242,51 +262,53 @@ const generateAutoVars = (schemes, varPrefix, CONFIG) => {
|
|
|
242
262
|
}
|
|
243
263
|
result[param] = generateAutoVars(subSchemes, `${varPrefix}-${pseudoName}`, CONFIG)
|
|
244
264
|
} else if (symb !== '@' && symb !== '.' && symb !== ':') {
|
|
245
|
-
// Regular CSS param — generate auto-switching var
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
// prefers-color-scheme media query only for dark/light
|
|
268
|
-
if (scheme === 'dark' || scheme === 'light') {
|
|
269
|
-
const mq = `@media (prefers-color-scheme: ${scheme})`
|
|
270
|
-
if (!MEDIA_VARS[mq]) MEDIA_VARS[mq] = {}
|
|
271
|
-
MEDIA_VARS[mq][autoVar] = color
|
|
272
|
-
}
|
|
273
|
-
}
|
|
265
|
+
// Regular CSS param — generate auto-switching var.
|
|
266
|
+
//
|
|
267
|
+
// We always emit the FULL switch table — `:root` fallback + one
|
|
268
|
+
// rule per scheme on `[data-theme="X"]` + `@media
|
|
269
|
+
// (prefers-color-scheme: dark|light)` — regardless of whether the
|
|
270
|
+
// user provided an initial `globalTheme`. A forced initial theme
|
|
271
|
+
// only decides which value lands in the `:root` fallback; runtime
|
|
272
|
+
// switching (via `changeGlobalTheme` → `data-theme` attribute)
|
|
273
|
+
// must always work, and OS preference must still drive auto mode.
|
|
274
|
+
const vp = CONFIG.varPrefix ? CONFIG.varPrefix + '-' : ''
|
|
275
|
+
const autoVar = `--${vp}theme-${varPrefix}-${param}`
|
|
276
|
+
const forced = globalTheme && globalTheme !== 'auto'
|
|
277
|
+
? String(globalTheme).replace(/^'|'$/g, '')
|
|
278
|
+
: null
|
|
279
|
+
|
|
280
|
+
let fallbackColor
|
|
281
|
+
for (const scheme in schemes) {
|
|
282
|
+
if (brokenSchemes.has(scheme)) continue
|
|
283
|
+
const val = schemes[scheme]?.[param]
|
|
284
|
+
if (val === undefined) continue
|
|
285
|
+
const color = getColor(val, `@${scheme}`)
|
|
286
|
+
if (color === undefined) continue
|
|
274
287
|
|
|
275
|
-
//
|
|
276
|
-
//
|
|
277
|
-
if (
|
|
278
|
-
|
|
288
|
+
// Pick the `:root` fallback: prefer the user's forced scheme if
|
|
289
|
+
// any, else `light`, else the first resolved scheme.
|
|
290
|
+
if ((forced && scheme === forced) ||
|
|
291
|
+
(!forced && (scheme === 'light' || fallbackColor === undefined))) {
|
|
292
|
+
fallbackColor = color
|
|
279
293
|
}
|
|
280
|
-
|
|
281
|
-
//
|
|
282
|
-
const
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
294
|
+
|
|
295
|
+
// `[data-theme="X"]` selector — works for any scheme incl. custom.
|
|
296
|
+
const selector = `[data-theme="${scheme}"]`
|
|
297
|
+
if (!MEDIA_VARS[selector]) MEDIA_VARS[selector] = {}
|
|
298
|
+
MEDIA_VARS[selector][autoVar] = color
|
|
299
|
+
|
|
300
|
+
// `prefers-color-scheme` media query only for standard light/dark.
|
|
301
|
+
if (scheme === 'dark' || scheme === 'light') {
|
|
302
|
+
const mq = `@media (prefers-color-scheme: ${scheme})`
|
|
303
|
+
if (!MEDIA_VARS[mq]) MEDIA_VARS[mq] = {}
|
|
304
|
+
MEDIA_VARS[mq][autoVar] = color
|
|
287
305
|
}
|
|
288
306
|
}
|
|
289
307
|
|
|
308
|
+
if (fallbackColor !== undefined) {
|
|
309
|
+
CSS_VARS[autoVar] = fallbackColor
|
|
310
|
+
}
|
|
311
|
+
|
|
290
312
|
result[param] = `var(${autoVar})`
|
|
291
313
|
result[`.${param}`] = { [param]: result[param] }
|
|
292
314
|
}
|
|
@@ -304,7 +326,7 @@ const generateAutoVars = (schemes, varPrefix, CONFIG) => {
|
|
|
304
326
|
|
|
305
327
|
export const setMediaTheme = (val, key, suffix, prefers) => {
|
|
306
328
|
const CONFIG = getActiveConfig()
|
|
307
|
-
const { CSS_VARS } = CONFIG
|
|
329
|
+
const { cssVars: CSS_VARS } = CONFIG
|
|
308
330
|
const theme = { value: val }
|
|
309
331
|
const isTopLevel = !suffix && !prefers
|
|
310
332
|
|
|
@@ -334,7 +356,8 @@ export const setMediaTheme = (val, key, suffix, prefers) => {
|
|
|
334
356
|
const color = getColor(value, prefers)
|
|
335
357
|
const metaSuffixes = [...new Set([prefers, suffix].filter(v => v).map(v => v.slice(1)))]
|
|
336
358
|
const varmetaSuffixName = metaSuffixes.length ? '-' + metaSuffixes.join('-') : ''
|
|
337
|
-
const
|
|
359
|
+
const vp = CONFIG.varPrefix ? CONFIG.varPrefix + '-' : ''
|
|
360
|
+
const CSSVar = `--${vp}theme-${key}${varmetaSuffixName}-${param}`
|
|
338
361
|
if (CONFIG.useVariable) {
|
|
339
362
|
// Suffixed vars (--theme-key-dark-param) only when opted in
|
|
340
363
|
if (CONFIG.useThemeSuffixedVars) CSS_VARS[CSSVar] = color
|