@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.
Files changed (119) hide show
  1. package/README.md +2 -2
  2. package/dist/cjs/defaultConfig/animation.js +4 -2
  3. package/dist/cjs/defaultConfig/cases.js +4 -2
  4. package/dist/cjs/defaultConfig/class.js +4 -2
  5. package/dist/cjs/defaultConfig/color.js +6 -9
  6. package/dist/cjs/defaultConfig/document.js +4 -2
  7. package/dist/cjs/defaultConfig/font-family.js +8 -12
  8. package/dist/cjs/defaultConfig/font.js +4 -11
  9. package/dist/cjs/defaultConfig/grid.js +5 -3
  10. package/dist/cjs/defaultConfig/icons.js +6 -4
  11. package/dist/cjs/defaultConfig/index.js +200 -24
  12. package/dist/cjs/defaultConfig/media.js +4 -2
  13. package/dist/cjs/defaultConfig/responsive.js +6 -4
  14. package/dist/cjs/defaultConfig/sequence.js +4 -2
  15. package/dist/cjs/defaultConfig/shadow.js +4 -2
  16. package/dist/cjs/defaultConfig/spacing.js +56 -7
  17. package/dist/cjs/defaultConfig/svg.js +6 -4
  18. package/dist/cjs/defaultConfig/templates.js +4 -2
  19. package/dist/cjs/defaultConfig/theme.js +4 -14
  20. package/dist/cjs/defaultConfig/timing.js +36 -5
  21. package/dist/cjs/defaultConfig/typography.js +36 -5
  22. package/dist/cjs/defaultConfig/unit.js +4 -2
  23. package/dist/cjs/factory.js +341 -40
  24. package/dist/cjs/index.js +6333 -11
  25. package/dist/cjs/package.json +4 -0
  26. package/dist/cjs/set.js +5614 -158
  27. package/dist/cjs/system/color.js +4481 -104
  28. package/dist/cjs/system/document.js +4371 -11
  29. package/dist/cjs/system/font.js +4401 -28
  30. package/dist/cjs/system/index.js +5748 -11
  31. package/dist/cjs/system/reset.js +4445 -21
  32. package/dist/cjs/system/shadow.js +4832 -41
  33. package/dist/cjs/system/spacing.js +4752 -39
  34. package/dist/cjs/system/svg.js +4437 -47
  35. package/dist/cjs/system/theme.js +4526 -335
  36. package/dist/cjs/system/timing.js +4695 -19
  37. package/dist/cjs/system/typography.js +4755 -33
  38. package/dist/cjs/tests/index.js +4 -2
  39. package/dist/cjs/transforms/index.js +5019 -134
  40. package/dist/cjs/utils/color.js +47 -66
  41. package/dist/cjs/utils/font.js +25 -46
  42. package/dist/cjs/utils/index.js +5068 -8
  43. package/dist/cjs/utils/sequence.js +4423 -35
  44. package/dist/cjs/utils/sprite.js +353 -12
  45. package/dist/cjs/utils/theme.js +3 -1
  46. package/dist/cjs/utils/unit.js +4 -2
  47. package/dist/cjs/utils/var.js +4390 -42
  48. package/index.js +1 -0
  49. package/package.json +11 -14
  50. package/src/defaultConfig/class.js +2 -1
  51. package/src/defaultConfig/font-family.js +3 -3
  52. package/src/defaultConfig/icons.js +1 -1
  53. package/src/defaultConfig/svg.js +1 -1
  54. package/src/defaultConfig/timing.js +1 -1
  55. package/src/factory.js +85 -13
  56. package/src/index.js +16 -5
  57. package/src/set.js +156 -63
  58. package/src/system/color.js +113 -12
  59. package/src/system/document.js +3 -3
  60. package/src/system/font.js +5 -5
  61. package/src/system/reset.js +41 -8
  62. package/src/system/shadow.js +4 -3
  63. package/src/system/spacing.js +3 -3
  64. package/src/system/svg.js +42 -5
  65. package/src/system/theme.js +87 -64
  66. package/src/system/timing.js +2 -2
  67. package/src/system/typography.js +12 -3
  68. package/src/transforms/index.js +4 -4
  69. package/src/utils/color.js +2 -1
  70. package/src/utils/font.js +7 -1
  71. package/src/utils/sequence.js +46 -29
  72. package/src/utils/sprite.js +44 -16
  73. package/src/utils/var.js +27 -9
  74. package/dist/esm/defaultConfig/animation.js +0 -4
  75. package/dist/esm/defaultConfig/cases.js +0 -4
  76. package/dist/esm/defaultConfig/class.js +0 -5
  77. package/dist/esm/defaultConfig/color.js +0 -11
  78. package/dist/esm/defaultConfig/document.js +0 -4
  79. package/dist/esm/defaultConfig/font-family.js +0 -18
  80. package/dist/esm/defaultConfig/font.js +0 -13
  81. package/dist/esm/defaultConfig/grid.js +0 -5
  82. package/dist/esm/defaultConfig/icons.js +0 -6
  83. package/dist/esm/defaultConfig/index.js +0 -25
  84. package/dist/esm/defaultConfig/media.js +0 -9
  85. package/dist/esm/defaultConfig/responsive.js +0 -30
  86. package/dist/esm/defaultConfig/sequence.js +0 -29
  87. package/dist/esm/defaultConfig/shadow.js +0 -4
  88. package/dist/esm/defaultConfig/spacing.js +0 -18
  89. package/dist/esm/defaultConfig/svg.js +0 -6
  90. package/dist/esm/defaultConfig/templates.js +0 -4
  91. package/dist/esm/defaultConfig/theme.js +0 -16
  92. package/dist/esm/defaultConfig/timing.js +0 -17
  93. package/dist/esm/defaultConfig/typography.js +0 -21
  94. package/dist/esm/defaultConfig/unit.js +0 -6
  95. package/dist/esm/factory.js +0 -60
  96. package/dist/esm/index.js +0 -12
  97. package/dist/esm/set.js +0 -219
  98. package/dist/esm/system/color.js +0 -193
  99. package/dist/esm/system/document.js +0 -16
  100. package/dist/esm/system/font.js +0 -58
  101. package/dist/esm/system/index.js +0 -10
  102. package/dist/esm/system/reset.js +0 -67
  103. package/dist/esm/system/shadow.js +0 -91
  104. package/dist/esm/system/spacing.js +0 -121
  105. package/dist/esm/system/svg.js +0 -86
  106. package/dist/esm/system/theme.js +0 -480
  107. package/dist/esm/system/timing.js +0 -32
  108. package/dist/esm/system/typography.js +0 -85
  109. package/dist/esm/tests/index.js +0 -8
  110. package/dist/esm/transforms/index.js +0 -216
  111. package/dist/esm/utils/color.js +0 -192
  112. package/dist/esm/utils/font.js +0 -92
  113. package/dist/esm/utils/index.js +0 -7
  114. package/dist/esm/utils/sequence.js +0 -303
  115. package/dist/esm/utils/sprite.js +0 -65
  116. package/dist/esm/utils/theme.js +0 -9
  117. package/dist/esm/utils/unit.js +0 -59
  118. package/dist/esm/utils/var.js +0 -82
  119. package/dist/iife/index.js +0 -3204
package/index.js ADDED
@@ -0,0 +1 @@
1
+ export * from './src/index.js'
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.9",
5
+ "version": "3.14.1",
6
6
  "files": [
7
7
  "dist",
8
8
  "*.js",
@@ -10,15 +10,13 @@
10
10
  ],
11
11
  "repository": "https://github.com/symbo-ls/smbls",
12
12
  "type": "module",
13
- "module": "./dist/esm/index.js",
13
+ "module": "./index.js",
14
14
  "unpkg": "./dist/iife/index.js",
15
15
  "jsdelivr": "./dist/iife/index.js",
16
- "main": "./dist/cjs/index.js",
16
+ "main": "./index.js",
17
17
  "exports": {
18
- ".": {
19
- "import": "./dist/esm/index.js",
20
- "require": "./dist/cjs/index.js"
21
- }
18
+ ".": "./index.js",
19
+ "./package.json": "./package.json"
22
20
  },
23
21
  "source": "src/index.js",
24
22
  "publishConfig": {
@@ -26,18 +24,17 @@
26
24
  },
27
25
  "scripts": {
28
26
  "copy:package:cjs": "cp ../../build/package-cjs.json dist/cjs/package.json",
29
- "build:esm": "cross-env NODE_ENV=$NODE_ENV esbuild $(find src -name '*.js') --target=es2020 --format=esm --outdir=dist/esm --define:process.env.NODE_ENV=process.env.NODE_ENV",
30
- "build:cjs": "cross-env NODE_ENV=$NODE_ENV esbuild $(find src -name '*.js') --target=node18 --format=cjs --outdir=dist/cjs --define:process.env.NODE_ENV=process.env.NODE_ENV",
31
- "build:iife": "cross-env NODE_ENV=$NODE_ENV esbuild src/index.js --bundle --target=es2020 --format=iife --global-name=SmblsScratch --outfile=dist/iife/index.js --define:process.env.NODE_ENV=process.env.NODE_ENV",
27
+ "build:esm": "NODE_ENV=$NODE_ENV esbuild $(find src -name '*.js') --target=es2020 --format=esm --outdir=dist/esm --define:process.env.NODE_ENV=process.env.NODE_ENV",
28
+ "build:cjs": "NODE_ENV=$NODE_ENV esbuild $(find src -name '*.js') --target=node18 --format=cjs --outdir=dist/cjs --define:process.env.NODE_ENV=process.env.NODE_ENV",
29
+ "build:iife": "NODE_ENV=$NODE_ENV esbuild src/index.js --bundle --target=es2020 --format=iife --global-name=SmblsScratch --outfile=dist/iife/index.js --define:process.env.NODE_ENV=process.env.NODE_ENV",
32
30
  "build": "node ../../build/build.js",
33
31
  "prepublish": "npm run build && npm run copy:package:cjs"
34
32
  },
35
33
  "dependencies": {
36
- "@domql/utils": "^3.8.9",
37
- "@symbo.ls/smbls-utils": "^3.8.9",
34
+ "@symbo.ls/utils": "^3.14.0",
38
35
  "color-contrast-checker": "^1.5.0"
39
36
  },
40
37
  "gitHead": "9fc1b79b41cdc725ca6b24aec64920a599634681",
41
- "browser": "./dist/esm/index.js",
42
- "sideEffects": false
38
+ "browser": "./index.js",
39
+ "sideEffects": true
43
40
  }
@@ -2,4 +2,5 @@
2
2
 
3
3
  const defaultProps = {}
4
4
 
5
- export const CLASS = defaultProps
5
+ // 'class' is a reserved word — export as named alias
6
+ export { defaultProps as class }
@@ -6,10 +6,10 @@ var defaultFont = { // eslint-disable-line
6
6
  type: ''
7
7
  }
8
8
 
9
- export const font_family = {}
10
- export const font_family_types = {
9
+ export const fontFamily = {}
10
+ export const fontFamilyTypes = {
11
11
  'sans-serif': 'Helvetica, Arial, sans-serif, --system-default',
12
12
  serif: 'Times New Roman, Georgia, serif, --system-default',
13
13
  monospace: 'Courier New, monospace, --system-default'
14
14
  }
15
- export const font_face = {}
15
+ export const fontFace = {}
@@ -1,4 +1,4 @@
1
1
  'use strict'
2
2
 
3
3
  export const icons = {}
4
- export const semantic_icons = {}
4
+ export const semanticIcons = {}
@@ -1,4 +1,4 @@
1
1
  'use strict'
2
2
 
3
3
  export const svg = {}
4
- export const svg_data = {}
4
+ export const svgData = {}
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- import { sequence } from './sequence'
3
+ import { sequence } from './sequence.js'
4
4
 
5
5
  const defaultProps = {
6
6
  default: 150,
package/src/factory.js CHANGED
@@ -5,28 +5,24 @@ import {
5
5
  deepMerge,
6
6
  isDefined,
7
7
  isObject
8
- } from '@domql/utils'
9
- import * as CONF from './defaultConfig'
8
+ } from '@symbo.ls/utils'
9
+ import * as CONF from './defaultConfig/index.js'
10
10
 
11
- export const CSS_VARS = {}
12
- export const CSS_MEDIA_VARS = {}
11
+ export const cssVars = {}
12
+ export const cssMediaVars = {}
13
13
 
14
- // Build CONFIG with lowercase/camelCase keys as canonical + UPPERCASE backward compat aliases
14
+ // Build CONFIG with lowercase/camelCase keys as canonical
15
15
  const _CONF = CONF
16
16
  const _confLower = {}
17
- const toCamel = (s) => s.replace(/_([a-z])/g, (_, c) => c.toUpperCase())
18
- const toUpper = (s) => s.replace(/([A-Z])/g, '_$1').toUpperCase()
17
+ export const toCamel = (s) => s.replace(/_([a-z])/g, (_, c) => c.toUpperCase())
19
18
  for (const key in _CONF) {
20
19
  const lower = key.toLowerCase()
21
20
  _confLower[lower] = _CONF[key]
22
- // camelCase alias (e.g. font_family -> fontFamily, semantic_icons -> semanticIcons)
21
+ // camelCase alias (e.g. font_family -> fontFamily, semanticIcons -> semanticIcons)
23
22
  const camel = toCamel(lower)
24
23
  if (camel !== lower) _confLower[camel] = _CONF[key]
25
24
  // keep original key if different from lower
26
25
  if (lower !== key) _confLower[key] = _CONF[key]
27
- // backward compat: add UPPERCASE alias (e.g. typography -> TYPOGRAPHY, fontFamily -> FONT_FAMILY)
28
- const upper = toUpper(key)
29
- if (upper !== key) _confLower[upper] = _CONF[key]
30
26
  }
31
27
 
32
28
  export const CONFIG = {
@@ -34,8 +30,11 @@ export const CONFIG = {
34
30
  useVariable: true,
35
31
  useReset: true,
36
32
  globalTheme: 'auto',
37
- CSS_VARS,
38
- CSS_MEDIA_VARS,
33
+ cssVars,
34
+ cssMediaVars,
35
+ CSS_VARS: cssVars,
36
+ CSS_MEDIA_VARS: cssMediaVars,
37
+ _scratchConfig: true,
39
38
  ..._confLower
40
39
  }
41
40
 
@@ -51,7 +50,21 @@ export const activateConfig = (def) => {
51
50
  return FACTORY[def || FACTORY.active]
52
51
  }
53
52
 
53
+ // Config stack for context-local resolution
54
+ // When processing an element, its context.designSystem is pushed
55
+ // so all getActiveConfig() calls resolve to the right config
56
+ const configStack = []
57
+
58
+ export const pushConfig = (config) => {
59
+ if (config && config._scratchConfig) configStack.push(config)
60
+ }
61
+
62
+ export const popConfig = () => {
63
+ configStack.pop()
64
+ }
65
+
54
66
  export const getActiveConfig = (def) => {
67
+ if (configStack.length) return configStack[configStack.length - 1]
55
68
  return FACTORY[def || FACTORY.active] || CONFIG
56
69
  }
57
70
 
@@ -61,3 +74,62 @@ export const setActiveConfig = (newConfig) => {
61
74
  FACTORY['1'] = deepMerge(newConfig, deepClone(cachedConfig))
62
75
  return newConfig
63
76
  }
77
+
78
+ // App-level flags that should NOT be inherited by isolated configs
79
+ const APP_FLAGS = [
80
+ 'useReset', 'useVariable', 'useFontImport', 'useIconSprite',
81
+ 'useSvgSprite', 'useDocumentTheme', 'useDefaultIcons', 'useDefaultConfig',
82
+ 'verbose', 'globalTheme'
83
+ ]
84
+
85
+ // Keep case-variant aliases pointing to the same object. `factory.js`
86
+ // bootstrap wires these as shared refs, but `deepClone` in `createConfig`
87
+ // breaks the sharing and a later `deepMerge` only writes into one spelling
88
+ // — downstream code that destructures a different spelling then reads
89
+ // stale data (e.g. `applyDocument` reads `fontFamily` while `setValue`
90
+ // writes to `fontfamily`, leaving `DOCUMENT.fontFamily` unset).
91
+ const ALIAS_GROUPS = [
92
+ ['fontfamily', 'fontFamily', 'font_family'],
93
+ ['fontfamilytypes', 'fontFamilyTypes'],
94
+ ['semanticicons', 'semanticIcons'],
95
+ ['svgdata', 'svgData']
96
+ ]
97
+ const aliasCaseVariants = (cfg) => {
98
+ for (const vs of ALIAS_GROUPS) {
99
+ let merged
100
+ for (const v of vs) {
101
+ const val = cfg[v]
102
+ if (!isObject(val)) continue
103
+ merged = merged ? deepMerge(val, merged) : val
104
+ }
105
+ if (!merged) continue
106
+ for (const v of vs) if (isObject(cfg[v])) cfg[v] = merged
107
+ }
108
+ }
109
+
110
+ export const createConfig = (name, overrides, { cleanBase = false } = {}) => {
111
+ // Secondary apps inheriting the primary app's processed config used to
112
+ // drag primary-specific tokens (e.g. an editor's brand themes like
113
+ // `sepia`, `canvas-card`) into the secondary's design system — breaking
114
+ // iframe isolation. When `cleanBase: true` is passed, clone the initial
115
+ // framework cache instead so the secondary starts from the same blank
116
+ // slate as the primary did. Legacy callers (no option) keep the old
117
+ // active-inherit behavior for backward compatibility.
118
+ const activeBase = cleanBase ? null : getActiveConfig()
119
+ const base = deepClone(
120
+ !cleanBase && activeBase && activeBase._scratchConfig ? activeBase : cachedConfig
121
+ )
122
+ // Remove app-level flags — they must come from overrides explicitly
123
+ for (const flag of APP_FLAGS) {
124
+ delete base[flag]
125
+ }
126
+ const cfg = deepMerge(overrides || {}, base)
127
+ cfg.cssVars = {}
128
+ cfg.cssMediaVars = {}
129
+ cfg.CSS_VARS = cfg.cssVars
130
+ cfg.CSS_MEDIA_VARS = cfg.cssMediaVars
131
+ cfg._scratchConfig = true
132
+ aliasCaseVariants(cfg)
133
+ if (name) FACTORY[name] = cfg
134
+ return cfg
135
+ }
package/src/index.js CHANGED
@@ -1,13 +1,24 @@
1
1
  'use strict'
2
2
 
3
+ // Note on the dual-form imports below: each `./X/index.js` is referenced
4
+ // twice in this file — once as a namespace import, once as a barrel
5
+ // re-export. That dual reference is load-bearing for parcel's optimizer:
6
+ // if a directory is only seen via barrel `export *` here AND via
7
+ // `import * as` in a different file (as factory.js does for
8
+ // ./defaultConfig), parcel collapses the dep-map entry to the bare
9
+ // directory form and marks it `false`, breaking runtime `require` for
10
+ // the explicit `/index.js` path. Keeping the namespace import in this
11
+ // file alongside the barrel forces parcel to track both forms.
12
+
3
13
  import * as scratchUtils from './utils/index.js'
4
14
  import * as scratchSystem from './system/index.js'
15
+ import * as scratchDefaultConfig from './defaultConfig/index.js'
5
16
 
6
17
  export * from './factory.js'
7
- export * from './defaultConfig'
8
- export * from './system'
9
- export * from './utils'
10
- export * from './transforms'
18
+ export * from './defaultConfig/index.js'
19
+ export * from './system/index.js'
20
+ export * from './utils/index.js'
21
+ export * from './transforms/index.js'
11
22
  export * from './set.js'
12
23
 
13
- export { scratchUtils, scratchSystem }
24
+ export { scratchUtils, scratchSystem, scratchDefaultConfig }
package/src/set.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- import { FACTORY, getActiveConfig, setActiveConfig } from './factory.js' // eslint-disable-line no-unused-vars
3
+ import { FACTORY, getActiveConfig, setActiveConfig, toCamel } from './factory.js' // eslint-disable-line no-unused-vars
4
4
  import {
5
5
  setColor,
6
6
  setGradient,
@@ -15,32 +15,36 @@ import {
15
15
  applyTimingSequence,
16
16
  applyDocument,
17
17
  setShadow
18
- } from './system'
18
+ } from './system/index.js'
19
19
 
20
- import { deepMerge } from '@domql/utils'
20
+ import { deepMerge, isObject, isArray } from '@symbo.ls/utils'
21
21
 
22
22
  const setVars = (val, key) => {
23
23
  const CONFIG = getActiveConfig()
24
- const { CSS_VARS } = CONFIG
24
+ const { cssVars } = CONFIG
25
25
  const varName = key.startsWith('--') ? key : `--${key}`
26
- CSS_VARS[varName] = val
26
+ cssVars[varName] = val
27
27
  return val
28
28
  }
29
29
 
30
30
  const asIs = (val) => val
31
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
+
32
36
  export const VALUE_TRANSFORMERS = {
33
37
  color: setColor,
34
38
  gradient: setGradient,
35
39
  font: setFont,
36
- font_family: setFontFamily,
40
+ fontFamily: setFontFamily,
37
41
  fontfamily: setFontFamily,
38
42
  theme: setTheme,
39
43
  icons: setSvgIcon,
40
- semantic_icons: asIs,
44
+ semanticIcons: asIs,
41
45
  semanticicons: asIs,
42
46
  svg: setSVG,
43
- svg_data: asIs,
47
+ svgData: asIs,
44
48
  typography: asIs,
45
49
  shadow: setShadow,
46
50
  spacing: asIs,
@@ -63,13 +67,15 @@ export const VALUE_TRANSFORMERS = {
63
67
  */
64
68
  export const setValue = (factoryName, value, key) => {
65
69
  const CONFIG = getActiveConfig()
66
- const lowerName = factoryName.toLowerCase()
67
- const FACTORY = CONFIG[lowerName] || CONFIG[factoryName]
70
+ const camelName = toCamel(factoryName)
71
+ const lowerName = camelName.toLowerCase()
72
+ const FACTORY = CONFIG[camelName] || CONFIG[lowerName] || CONFIG[factoryName]
68
73
 
69
- if (VALUE_TRANSFORMERS[lowerName]) {
74
+ const transformer = VALUE_TRANSFORMERS[camelName] || VALUE_TRANSFORMERS[lowerName]
75
+ if (transformer) {
70
76
  try {
71
- const result = VALUE_TRANSFORMERS[lowerName](value, key)
72
- FACTORY[key] = result
77
+ const result = transformer(value, key)
78
+ if (FACTORY) FACTORY[key] = result
73
79
  return FACTORY
74
80
  } catch (error) {
75
81
  if (CONFIG.verbose)
@@ -84,7 +90,16 @@ export const setValue = (factoryName, value, key) => {
84
90
  export const setEach = (factoryName, props) => {
85
91
  const CONFIG = getActiveConfig()
86
92
  const lowerName = factoryName.toLowerCase()
87
- const keys = Object.keys(props)
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
+ })
88
103
 
89
104
  keys.forEach((key) => {
90
105
  try {
@@ -105,49 +120,90 @@ export const setEach = (factoryName, props) => {
105
120
  return CONFIG[lowerName] || CONFIG[factoryName]
106
121
  }
107
122
 
108
- export const changeGlobalTheme = (newTheme) => {
109
- const CONFIG = getActiveConfig()
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()
110
127
  CONFIG.globalTheme = newTheme
111
128
 
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
-
117
- // Clear theme-related CSS vars
118
- for (const key in CONFIG.CSS_VARS) {
119
- if (key.startsWith('--theme-')) delete CONFIG.CSS_VARS[key]
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
+ }
120
175
  }
121
176
 
122
- // Clear all CSS_MEDIA_VARS (media queries + data-theme selectors)
123
- for (const key in CONFIG.CSS_MEDIA_VARS) {
124
- delete CONFIG.CSS_MEDIA_VARS[key]
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]
125
181
  }
126
182
 
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)
183
+ // Clear all cssMediaVars (media queries + data-theme selectors)
184
+ for (const key in CONFIG.cssMediaVars) {
185
+ delete CONFIG.cssMediaVars[key]
134
186
  }
135
187
 
136
- // Apply updated CSS vars to the existing :root stylesheet rule
137
- if (typeof document !== 'undefined' && CONFIG.CSS_VARS) {
138
- const sheets = document.styleSheets
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
139
193
  for (let i = 0; i < sheets.length; i++) {
140
194
  try {
141
195
  const rules = sheets[i].cssRules
142
196
  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])
197
+ if (rules[j].selectorText === targetSelector) {
198
+ for (const key in CONFIG.cssVars) {
199
+ rules[j].style.setProperty(key, CONFIG.cssVars[key])
146
200
  }
147
201
  return CONFIG
148
202
  }
149
203
  }
150
- } catch (e) { /* cross-origin stylesheet */ }
204
+ } catch (e) {
205
+ /* cross-origin stylesheet */
206
+ }
151
207
  }
152
208
  }
153
209
 
@@ -157,7 +213,10 @@ export const changeGlobalTheme = (newTheme) => {
157
213
  const SET_OPTIONS = {}
158
214
 
159
215
  export const set = (recivedConfig, options = SET_OPTIONS) => {
160
- let CONFIG = getActiveConfig()
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()
161
220
 
162
221
  const {
163
222
  version,
@@ -168,12 +227,12 @@ export const set = (recivedConfig, options = SET_OPTIONS) => {
168
227
  useFontImport,
169
228
  useIconSprite,
170
229
  globalTheme,
230
+ themeRoot,
171
231
  useDocumentTheme,
172
232
  useDefaultConfig,
173
233
  semanticIcons,
174
- SEMANTIC_ICONS, // deprecated
175
- semantic_icons,
176
234
  files,
235
+ assets,
177
236
  ...config
178
237
  } = recivedConfig
179
238
 
@@ -182,6 +241,7 @@ export const set = (recivedConfig, options = SET_OPTIONS) => {
182
241
  }
183
242
 
184
243
  if (files !== undefined) CONFIG.files = files
244
+ if (assets !== undefined) CONFIG.assets = assets
185
245
  if (verbose !== undefined) CONFIG.verbose = verbose
186
246
  if (useVariable !== undefined) CONFIG.useVariable = useVariable
187
247
  if (useReset !== undefined) CONFIG.useReset = useReset
@@ -190,23 +250,51 @@ export const set = (recivedConfig, options = SET_OPTIONS) => {
190
250
  if (useIconSprite !== undefined) CONFIG.useIconSprite = useIconSprite
191
251
  if (useDocumentTheme !== undefined) CONFIG.useDocumentTheme = useDocumentTheme
192
252
  if (globalTheme !== undefined) CONFIG.globalTheme = globalTheme
253
+ if (themeRoot !== undefined) CONFIG.themeRoot = themeRoot
193
254
  if (recivedConfig.useThemeSuffixedVars !== undefined)
194
255
  CONFIG.useThemeSuffixedVars = recivedConfig.useThemeSuffixedVars
195
256
  if (useDefaultConfig !== undefined) CONFIG.useDefaultConfig = useDefaultConfig
196
- const _semanticIcons = semanticIcons || SEMANTIC_ICONS || semantic_icons
197
- if (_semanticIcons !== undefined) {
198
- CONFIG.semantic_icons = _semanticIcons
199
- CONFIG.semanticIcons = CONFIG.semantic_icons
200
- CONFIG.SEMANTIC_ICONS = CONFIG.semantic_icons // backward compat alias
201
- }
257
+ if (semanticIcons !== undefined) CONFIG.semanticIcons = semanticIcons
202
258
  if (CONFIG.verbose) console.log(CONFIG)
203
259
 
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)
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
+ }
207
295
  }
208
296
 
209
- if (!CONFIG.__svg_cache) CONFIG.__svg_cache = {}
297
+ if (!CONFIG.__svgCache) CONFIG.__svgCache = {}
210
298
 
211
299
  const keys = Object.keys(config)
212
300
  const keySet = new Set(keys)
@@ -219,11 +307,6 @@ export const set = (recivedConfig, options = SET_OPTIONS) => {
219
307
  }
220
308
  })
221
309
 
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
-
227
310
  // Process only lowercase keys (skip UPPERCASE when lowercase equivalent exists)
228
311
  keys.map((key) => {
229
312
  const lower = key.toLowerCase()
@@ -231,22 +314,29 @@ export const set = (recivedConfig, options = SET_OPTIONS) => {
231
314
  return setEach(key, config[key])
232
315
  })
233
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
+
234
324
  // apply generic configs
235
- if (config.typography || config.TYPOGRAPHY) { // UPPERCASE deprecated
325
+ if (config.typography) {
236
326
  try {
237
327
  applyTypographySequence()
238
328
  } catch (e) {
239
329
  if (CONFIG.verbose) console.warn('Error applying typography sequence', e)
240
330
  }
241
331
  }
242
- if (config.spacing || config.SPACING) { // UPPERCASE deprecated
332
+ if (config.spacing) {
243
333
  try {
244
334
  applySpacingSequence()
245
335
  } catch (e) {
246
336
  if (CONFIG.verbose) console.warn('Error applying spacing sequence', e)
247
337
  }
248
338
  }
249
- if (config.timing || config.TIMING) { // UPPERCASE deprecated
339
+ if (config.timing) {
250
340
  try {
251
341
  applyTimingSequence()
252
342
  } catch (e) {
@@ -254,7 +344,10 @@ export const set = (recivedConfig, options = SET_OPTIONS) => {
254
344
  }
255
345
  }
256
346
  applyDocument()
257
- applyReset()
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
258
351
 
259
352
  return CONFIG
260
353
  }