zyzz 0.0.2 → 0.0.5

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 (252) hide show
  1. package/README.md +34 -19
  2. package/dist/Config.d.ts +16 -167
  3. package/dist/Config.d.ts.map +1 -1
  4. package/dist/Config.js +7 -246
  5. package/dist/Config.js.map +1 -1
  6. package/dist/Style.d.ts +19 -13
  7. package/dist/Style.d.ts.map +1 -1
  8. package/dist/Style.js +73 -7
  9. package/dist/Style.js.map +1 -1
  10. package/dist/Vars.d.ts +114 -0
  11. package/dist/Vars.d.ts.map +1 -0
  12. package/dist/Vars.js +42 -0
  13. package/dist/Vars.js.map +1 -0
  14. package/dist/compiler/Graph.js +11 -8
  15. package/dist/compiler/Graph.js.map +1 -1
  16. package/dist/compiler/Native.d.ts +5 -5
  17. package/dist/compiler/Native.d.ts.map +1 -1
  18. package/dist/compiler/Native.js +21 -21
  19. package/dist/compiler/Native.js.map +1 -1
  20. package/dist/compiler/Source.d.ts +10 -6
  21. package/dist/compiler/Source.d.ts.map +1 -1
  22. package/dist/compiler/Source.js +66 -11
  23. package/dist/compiler/Source.js.map +1 -1
  24. package/dist/compiler/Transform.d.ts +1 -1
  25. package/dist/compiler/Transform.d.ts.map +1 -1
  26. package/dist/compiler/Transform.js +59 -21
  27. package/dist/compiler/Transform.js.map +1 -1
  28. package/dist/compiler/internal/Compositions.d.ts +5 -0
  29. package/dist/compiler/internal/Compositions.d.ts.map +1 -1
  30. package/dist/compiler/internal/Compositions.js +22 -3
  31. package/dist/compiler/internal/Compositions.js.map +1 -1
  32. package/dist/compiler/internal/Configurations.d.ts.map +1 -1
  33. package/dist/compiler/internal/Configurations.js +73 -12
  34. package/dist/compiler/internal/Configurations.js.map +1 -1
  35. package/dist/compiler/internal/Contract.d.ts +1 -1
  36. package/dist/compiler/internal/Contract.d.ts.map +1 -1
  37. package/dist/compiler/internal/Contract.js +162 -13
  38. package/dist/compiler/internal/Contract.js.map +1 -1
  39. package/dist/compiler/internal/Contributions.js +1 -1
  40. package/dist/compiler/internal/Contributions.js.map +1 -1
  41. package/dist/compiler/internal/Dynamic.js +1 -1
  42. package/dist/compiler/internal/Dynamic.js.map +1 -1
  43. package/dist/compiler/internal/Expression.d.ts +3 -0
  44. package/dist/compiler/internal/Expression.d.ts.map +1 -1
  45. package/dist/compiler/internal/Expression.js +21 -0
  46. package/dist/compiler/internal/Expression.js.map +1 -1
  47. package/dist/compiler/internal/Identifiers.d.ts +1 -1
  48. package/dist/compiler/internal/Identifiers.d.ts.map +1 -1
  49. package/dist/compiler/internal/Identifiers.js +2 -2
  50. package/dist/compiler/internal/Identifiers.js.map +1 -1
  51. package/dist/compiler/internal/NativeBindings.d.ts +1 -1
  52. package/dist/compiler/internal/NativeBindings.d.ts.map +1 -1
  53. package/dist/compiler/internal/NativeBindings.js +3 -3
  54. package/dist/compiler/internal/NativeBindings.js.map +1 -1
  55. package/dist/compiler/internal/PackedStyles.d.ts +1 -1
  56. package/dist/compiler/internal/PackedStyles.d.ts.map +1 -1
  57. package/dist/compiler/internal/PackedStyles.js +15 -25
  58. package/dist/compiler/internal/PackedStyles.js.map +1 -1
  59. package/dist/compiler/internal/Recipes.js +1 -1
  60. package/dist/compiler/internal/Recipes.js.map +1 -1
  61. package/dist/compiler/internal/Selectors.js +1 -1
  62. package/dist/compiler/internal/Selectors.js.map +1 -1
  63. package/dist/compiler/internal/Themes.d.ts +12 -3
  64. package/dist/compiler/internal/Themes.d.ts.map +1 -1
  65. package/dist/compiler/internal/Themes.js +192 -52
  66. package/dist/compiler/internal/Themes.js.map +1 -1
  67. package/dist/compiler/internal/{Variables.d.ts → Vars.d.ts} +1 -1
  68. package/dist/compiler/internal/Vars.d.ts.map +1 -0
  69. package/dist/compiler/internal/{Variables.js → Vars.js} +3 -3
  70. package/dist/compiler/internal/Vars.js.map +1 -0
  71. package/dist/default.d.ts +451 -1649
  72. package/dist/default.d.ts.map +1 -1
  73. package/dist/default.js +389 -80
  74. package/dist/default.js.map +2 -2
  75. package/dist/default.js.zyzz.json +1 -1
  76. package/dist/esbuild/index.d.ts +3 -0
  77. package/dist/esbuild/index.d.ts.map +1 -0
  78. package/dist/esbuild/index.js +5 -0
  79. package/dist/esbuild/index.js.map +1 -0
  80. package/dist/index.d.ts +4 -4
  81. package/dist/index.d.ts.map +1 -1
  82. package/dist/index.js +4 -4
  83. package/dist/index.js.map +1 -1
  84. package/dist/internal/Authoring.d.ts +1 -1
  85. package/dist/internal/Authoring.d.ts.map +1 -1
  86. package/dist/internal/Authoring.js +4 -4
  87. package/dist/internal/Authoring.js.map +1 -1
  88. package/dist/internal/Binding.d.ts +1 -1
  89. package/dist/internal/Binding.d.ts.map +1 -1
  90. package/dist/internal/Completion.d.ts +19 -0
  91. package/dist/internal/Completion.d.ts.map +1 -0
  92. package/dist/internal/Completion.js +2 -0
  93. package/dist/internal/Completion.js.map +1 -0
  94. package/dist/internal/Condition.d.ts +3 -1
  95. package/dist/internal/Condition.d.ts.map +1 -1
  96. package/dist/internal/Condition.js.map +1 -1
  97. package/dist/internal/Configuration.d.ts +248 -0
  98. package/dist/internal/Configuration.d.ts.map +1 -0
  99. package/dist/internal/Configuration.js +343 -0
  100. package/dist/internal/Configuration.js.map +1 -0
  101. package/dist/internal/Shorthands.d.ts +1 -1
  102. package/dist/internal/Shorthands.d.ts.map +1 -1
  103. package/dist/internal/Shorthands.js +1 -0
  104. package/dist/internal/Shorthands.js.map +1 -1
  105. package/dist/{Theme.d.ts → internal/Theme.d.ts} +24 -19
  106. package/dist/internal/Theme.d.ts.map +1 -0
  107. package/dist/{Theme.js → internal/Theme.js} +57 -14
  108. package/dist/internal/Theme.js.map +1 -0
  109. package/dist/internal/Token.d.ts +46 -16
  110. package/dist/internal/Token.d.ts.map +1 -1
  111. package/dist/internal/Token.js +89 -5
  112. package/dist/internal/Token.js.map +1 -1
  113. package/dist/internal/Typography.d.ts +31 -0
  114. package/dist/internal/Typography.d.ts.map +1 -0
  115. package/dist/internal/Typography.js +54 -0
  116. package/dist/internal/Typography.js.map +1 -0
  117. package/dist/internal/Value.d.ts +6 -1
  118. package/dist/internal/Value.d.ts.map +1 -1
  119. package/dist/internal/Value.js.map +1 -1
  120. package/dist/internal/VariableSets.d.ts +20 -0
  121. package/dist/internal/VariableSets.d.ts.map +1 -0
  122. package/dist/internal/VariableSets.js +317 -0
  123. package/dist/internal/VariableSets.js.map +1 -0
  124. package/dist/next/loader.js +15 -5
  125. package/dist/next/loader.js.map +1 -1
  126. package/dist/node/Host.d.ts.map +1 -1
  127. package/dist/node/Host.js +1 -1
  128. package/dist/node/Host.js.map +1 -1
  129. package/dist/react-native/Host.d.ts +2 -2
  130. package/dist/react-native/Host.d.ts.map +1 -1
  131. package/dist/react-native/Host.js +4 -4
  132. package/dist/react-native/Host.js.map +1 -1
  133. package/dist/react-native/StyleSheet.d.ts +9 -8
  134. package/dist/react-native/StyleSheet.d.ts.map +1 -1
  135. package/dist/react-native/StyleSheet.js +29 -23
  136. package/dist/react-native/StyleSheet.js.map +1 -1
  137. package/dist/react-native/Variants.d.ts +3 -3
  138. package/dist/react-native/Variants.d.ts.map +1 -1
  139. package/dist/react-native/Variants.js +2 -2
  140. package/dist/react-native/react.d.ts +1 -1
  141. package/dist/react-native/react.d.ts.map +1 -1
  142. package/dist/react-native/react.js +5 -5
  143. package/dist/react-native/react.js.map +1 -1
  144. package/dist/rollup/index.d.ts +3 -0
  145. package/dist/rollup/index.d.ts.map +1 -0
  146. package/dist/rollup/index.js +5 -0
  147. package/dist/rollup/index.js.map +1 -0
  148. package/dist/runtime/Appearance.d.ts +7 -7
  149. package/dist/runtime/Appearance.d.ts.map +1 -1
  150. package/dist/runtime/Appearance.js +15 -15
  151. package/dist/runtime/Appearance.js.map +1 -1
  152. package/dist/runtime/Dynamic.d.ts.map +1 -1
  153. package/dist/runtime/Dynamic.js +4 -1
  154. package/dist/runtime/Dynamic.js.map +1 -1
  155. package/dist/runtime/NativeContext.d.ts +4 -4
  156. package/dist/runtime/NativeContext.d.ts.map +1 -1
  157. package/dist/runtime/NativeContext.js +5 -5
  158. package/dist/runtime/NativeContext.js.map +1 -1
  159. package/dist/runtime/NativeDynamic.d.ts +1 -1
  160. package/dist/runtime/NativeDynamic.d.ts.map +1 -1
  161. package/dist/runtime/NativeDynamic.js.map +1 -1
  162. package/dist/runtime/Props.d.ts +1 -1
  163. package/dist/runtime/Props.js +3 -3
  164. package/dist/runtime/Props.js.map +1 -1
  165. package/dist/runtime/Selection.d.ts +14 -10
  166. package/dist/runtime/Selection.d.ts.map +1 -1
  167. package/dist/runtime/Selection.js +19 -18
  168. package/dist/runtime/Selection.js.map +1 -1
  169. package/dist/styleFunction.d.ts +14 -14
  170. package/dist/styleFunction.d.ts.map +1 -1
  171. package/dist/styleFunction.js.map +1 -1
  172. package/dist/unplugin/index.d.ts +20 -0
  173. package/dist/unplugin/index.d.ts.map +1 -0
  174. package/dist/unplugin/index.js +498 -0
  175. package/dist/unplugin/index.js.map +1 -0
  176. package/dist/variants.d.ts +3 -3
  177. package/dist/variants.d.ts.map +1 -1
  178. package/dist/variants.js.map +1 -1
  179. package/dist/vite/index.d.ts.map +1 -1
  180. package/dist/vite/index.js +6 -3
  181. package/dist/vite/index.js.map +1 -1
  182. package/dist/web/Css.d.ts +4 -3
  183. package/dist/web/Css.d.ts.map +1 -1
  184. package/dist/web/Css.js +4 -4
  185. package/dist/web/Css.js.map +1 -1
  186. package/dist/web/internal/Themes.d.ts +3 -2
  187. package/dist/web/internal/Themes.d.ts.map +1 -1
  188. package/dist/web/internal/Themes.js +42 -6
  189. package/dist/web/internal/Themes.js.map +1 -1
  190. package/dist/webpack/index.d.ts +3 -0
  191. package/dist/webpack/index.d.ts.map +1 -0
  192. package/dist/webpack/index.js +5 -0
  193. package/dist/webpack/index.js.map +1 -0
  194. package/package.json +23 -2
  195. package/src/Config.ts +43 -684
  196. package/src/Style.ts +167 -40
  197. package/src/Vars.ts +299 -0
  198. package/src/compiler/Graph.ts +14 -9
  199. package/src/compiler/Native.ts +29 -32
  200. package/src/compiler/Source.ts +85 -18
  201. package/src/compiler/Transform.ts +78 -22
  202. package/src/compiler/internal/Compositions.ts +36 -3
  203. package/src/compiler/internal/Configurations.ts +83 -11
  204. package/src/compiler/internal/Contract.ts +223 -17
  205. package/src/compiler/internal/Contributions.ts +1 -1
  206. package/src/compiler/internal/Dynamic.ts +1 -1
  207. package/src/compiler/internal/Expression.ts +26 -0
  208. package/src/compiler/internal/Identifiers.ts +2 -1
  209. package/src/compiler/internal/NativeBindings.ts +3 -3
  210. package/src/compiler/internal/PackedStyles.ts +22 -26
  211. package/src/compiler/internal/Recipes.ts +1 -1
  212. package/src/compiler/internal/Selectors.ts +1 -1
  213. package/src/compiler/internal/Themes.ts +266 -57
  214. package/src/compiler/internal/{Variables.ts → Vars.ts} +2 -2
  215. package/src/default.ts +868 -156
  216. package/src/esbuild/index.ts +5 -0
  217. package/src/index.ts +4 -4
  218. package/src/internal/Authoring.ts +5 -7
  219. package/src/internal/Binding.ts +1 -1
  220. package/src/internal/Completion.ts +42 -0
  221. package/src/internal/Condition.ts +31 -1
  222. package/src/internal/Configuration.ts +970 -0
  223. package/src/internal/Shorthands.ts +2 -0
  224. package/src/{Theme.ts → internal/Theme.ts} +174 -48
  225. package/src/internal/Token.ts +258 -85
  226. package/src/internal/Typography.ts +106 -0
  227. package/src/internal/Value.ts +45 -26
  228. package/src/internal/VariableSets.ts +458 -0
  229. package/src/next/loader.ts +19 -5
  230. package/src/node/Host.ts +1 -2
  231. package/src/react-native/Host.ts +6 -6
  232. package/src/react-native/StyleSheet.ts +49 -42
  233. package/src/react-native/Variants.ts +3 -3
  234. package/src/react-native/react.ts +6 -6
  235. package/src/rollup/index.ts +5 -0
  236. package/src/runtime/Appearance.ts +20 -20
  237. package/src/runtime/Dynamic.ts +4 -1
  238. package/src/runtime/NativeContext.ts +8 -8
  239. package/src/runtime/NativeDynamic.ts +2 -4
  240. package/src/runtime/Props.ts +3 -3
  241. package/src/runtime/Selection.ts +44 -33
  242. package/src/styleFunction.ts +22 -19
  243. package/src/unplugin/index.ts +600 -0
  244. package/src/variants.ts +7 -5
  245. package/src/vite/index.ts +5 -4
  246. package/src/web/Css.ts +10 -7
  247. package/src/web/internal/Themes.ts +52 -9
  248. package/src/webpack/index.ts +5 -0
  249. package/dist/Theme.d.ts.map +0 -1
  250. package/dist/Theme.js.map +0 -1
  251. package/dist/compiler/internal/Variables.d.ts.map +0 -1
  252. package/dist/compiler/internal/Variables.js.map +0 -1
@@ -43,7 +43,7 @@ export function compile(options: compile.Options): compile.ReturnType {
43
43
  )
44
44
  portableNames[call.name] = call.portable
45
45
  }
46
- for (const [name, theme] of Object.entries(extracted.themes))
46
+ for (const [name, theme] of Object.entries(extracted.vars))
47
47
  if (
48
48
  (Object.keys(theme[Token.definition].values).length ||
49
49
  extracted.themeCalls.some(
@@ -57,7 +57,7 @@ export function compile(options: compile.Options): compile.ReturnType {
57
57
  !theme[Token.definition].contract[Token.identity]?.startsWith('id-')
58
58
  )
59
59
  throw new Error(
60
- 'CSS-only themes require an explicit id on Config.create or Theme.define.',
60
+ 'CSS-only themes require an explicit id on Config.create or Vars.define.',
61
61
  )
62
62
  for (const call of extracted.contributionCalls ?? [])
63
63
  if (
@@ -138,7 +138,9 @@ export function compile(options: compile.Options): compile.ReturnType {
138
138
  ? property.key.name
139
139
  : property.key.type === 'Literal'
140
140
  ? property.key.value
141
- : undefined) !== 'themes',
141
+ : undefined) !== 'themes' &&
142
+ (property.key.type !== 'Identifier' ||
143
+ property.key.name !== 'variables'),
142
144
  )
143
145
  )
144
146
  unusedSelections.add(Expression.unwrap(node.init).start)
@@ -215,7 +217,7 @@ export function compile(options: compile.Options): compile.ReturnType {
215
217
  }
216
218
  })(),
217
219
  contributions: extracted.contributions,
218
- themes: Object.keys(extracted.themes).length ? extracted.themes : undefined,
220
+ vars: Object.keys(extracted.vars).length ? extracted.vars : undefined,
219
221
  })
220
222
 
221
223
  let runtime = '__zyzzProps'
@@ -461,7 +463,7 @@ export function compile(options: compile.Options): compile.ReturnType {
461
463
  .join(',')
462
464
 
463
465
  const className = JSON.stringify(classes[call.name])
464
- const value = `(input${typed ? `:Parameters<${type}>[0]` : ''})=>{${reads}const external=input.className;const style=input.style;return {className:external?${className}+" "+external:${className},style:{...input.variables,...style,${assignments}}}}`
466
+ const value = `(input${typed ? `:Parameters<${type}>[0]` : ''})=>{${reads}const external=input.className;const style=input.style;return {className:external?${className}+" "+external:${className},style:{...input.vars,...style,${assignments}}}}`
465
467
  const result = composeHtml
466
468
  ? `${compositionHtml}.bind(${value})`
467
469
  : call.output === 'html'
@@ -540,11 +542,12 @@ export function compile(options: compile.Options): compile.ReturnType {
540
542
  }
541
543
 
542
544
  for (const call of extracted.themeCalls) {
543
- const scope = (name: string) => ({ className: emitted.themes[name] })
545
+ const scope = (name: string) => ({ className: emitted.vars[name] })
544
546
 
545
547
  const props = (() => {
548
+ if (call.variableSet) return '{}'
546
549
  if (!call.members)
547
- return `{className:${JSON.stringify(emitted.themes[call.name])}}`
550
+ return `{className:${JSON.stringify(emitted.vars[call.name])}}`
548
551
 
549
552
  const script = extracted.themeScripts?.includes(call.name)
550
553
  const root = extracted.themeAppearances?.includes(call.name)
@@ -559,19 +562,48 @@ export function compile(options: compile.Options): compile.ReturnType {
559
562
  : ''
560
563
  const rootOptions =
561
564
  storage || defaultTheme
562
- ? `,${JSON.stringify({ ...(defaultTheme ? { defaultTheme } : {}), ...(storage ? { storageKey: storage } : {}) })}`
565
+ ? `,${JSON.stringify({ ...(defaultTheme ? { defaultVars: defaultTheme } : {}), ...(storage ? { storageKey: storage } : {}) })}`
563
566
  : ''
564
567
 
565
568
  return `${root ? `appearance:${appearance}.root(${entries}${rootOptions}),` : ''}${script ? `script:${appearance}.create(${entries}${scriptOptions}),` : ''}`
566
569
  }
567
570
 
571
+ if (call.variableConfig) {
572
+ const selected =
573
+ extracted.themeSelections?.includes(call.name) ||
574
+ Object.values(extracted.themeExports ?? {}).some(
575
+ (link) =>
576
+ link.call.name === call.name &&
577
+ link.kind === 'config' &&
578
+ !link.call.initialization &&
579
+ !link.call.root,
580
+ )
581
+ const entries = call.options?.themes
582
+ ? Object.entries(call.members)
583
+ .filter(([key]) => {
584
+ const path = JSON.parse(key) as string[]
585
+ return path[0] === 'themes' && path.length === 2
586
+ })
587
+ .map(([key, name]) => [
588
+ (JSON.parse(key) as string[])[1],
589
+ emitted.vars[name],
590
+ ])
591
+ : [['default', emitted.vars[call.name]]]
592
+ const fallback = call.options?.defaultTheme ?? 'default'
593
+ if (!selected)
594
+ return `{${helpers(JSON.stringify(call.options?.themes ? entries : []), call.options?.themes ? String(fallback) : undefined).replace(/,$/, '')}}`
595
+ usesSelection = true
596
+ const selector = `${selection}.create(${JSON.stringify(entries)},${call.options?.output === 'html'},'set',${JSON.stringify(fallback)})`
597
+ return `{${helpers(JSON.stringify(call.options?.themes ? entries : []), call.options?.themes ? String(fallback) : undefined)}vars:/*#__PURE__*/${selector}}`
598
+ }
599
+
568
600
  if (call.options?.themes) {
569
601
  const catalog = Object.fromEntries(
570
602
  Object.entries(call.members)
571
603
  .filter(([key]) => (JSON.parse(key) as string[]).length === 2)
572
604
  .map(([key, name]) => [
573
605
  (JSON.parse(key) as string[])[1]!,
574
- emitted.themes[name],
606
+ emitted.vars[name],
575
607
  ]),
576
608
  )
577
609
 
@@ -595,7 +627,7 @@ export function compile(options: compile.Options): compile.ReturnType {
595
627
  })()
596
628
 
597
629
  const assertion = /\.[cm]?tsx?$/.test(options.moduleId)
598
- ? ` as ${call.type ?? `import('zyzz').Theme.Definition<${call.tokenType}>`}`
630
+ ? ` as ${call.type ?? `import('zyzz').Vars.Definition<${call.tokenType}>`}`
599
631
  : ''
600
632
 
601
633
  module.overwrite(call.start, call.end, `(${props}${assertion})`)
@@ -607,7 +639,7 @@ export function compile(options: compile.Options): compile.ReturnType {
607
639
  module.overwrite(
608
640
  alias.start,
609
641
  alias.end,
610
- `(${options.source.slice(alias.start, alias.end)} as ${alias.type ?? `import('zyzz').Theme.Definition<${alias.tokenType}>`})`,
642
+ `(${options.source.slice(alias.start, alias.end)} as ${alias.type ?? `import('zyzz').Vars.Definition<${alias.tokenType}>`})`,
611
643
  )
612
644
 
613
645
  continue
@@ -619,7 +651,7 @@ export function compile(options: compile.Options): compile.ReturnType {
619
651
  ? `{${members.map((member) => `${member}:undefined`).join(',')}}`
620
652
  : 'undefined'
621
653
  const type =
622
- alias.type ?? `import('zyzz').Theme.Definition<${alias.tokenType}>`
654
+ alias.type ?? `import('zyzz').Vars.Definition<${alias.tokenType}>`
623
655
  const assertion = /\.[cm]?tsx?$/.test(options.moduleId)
624
656
  ? ` as unknown as ${alias.destructured ? `{${members.map((member) => `readonly ${member}:${type}['${member}']`).join(';')}}` : `${type}['${member}']`}`
625
657
  : ''
@@ -667,7 +699,7 @@ export function compile(options: compile.Options): compile.ReturnType {
667
699
  module.overwrite(
668
700
  reference.start,
669
701
  reference.end,
670
- JSON.stringify(emitted.themes[reference.name]),
702
+ JSON.stringify(emitted.vars[reference.name]),
671
703
  )
672
704
 
673
705
  replacements.push(
@@ -692,7 +724,7 @@ export function compile(options: compile.Options): compile.ReturnType {
692
724
  specifier.importKind === 'type' ||
693
725
  !(
694
726
  node.source.value === 'zyzz'
695
- ? ['Config', 'cx', 'style', 'Theme', 'variable', 'variants']
727
+ ? ['Config', 'cx', 'style', 'Vars', 'variable', 'variants']
696
728
  : [
697
729
  'Css',
698
730
  'cssFunction',
@@ -830,14 +862,14 @@ export function compile(options: compile.Options): compile.ReturnType {
830
862
  const themeOwners = new Map(
831
863
  extracted.themeCalls.flatMap((call) =>
832
864
  [...new Set([call.name, ...Object.values(call.members ?? {})])].map(
833
- (name) => [emitted.themes[name], { ...call, name }] as const,
865
+ (name) => [emitted.vars[name], { ...call, name }] as const,
834
866
  ),
835
867
  ),
836
868
  )
837
869
 
838
870
  const linkedOwners = new Map(
839
871
  Object.entries(options[Themes.context]?.owners ?? {}).map(
840
- ([key, owner]) => [emitted.themes[key], owner],
872
+ ([key, owner]) => [emitted.vars[key], owner],
841
873
  ),
842
874
  )
843
875
 
@@ -873,9 +905,7 @@ export function compile(options: compile.Options): compile.ReturnType {
873
905
  Css.compile({
874
906
  styles: { styles: [] },
875
907
  contributions: [contribution.definition],
876
- themes: Object.keys(extracted.themes).length
877
- ? extracted.themes
878
- : undefined,
908
+ vars: Object.keys(extracted.vars).length ? extracted.vars : undefined,
879
909
  }).contributionCss ?? ''
880
910
  if (!rendered) continue
881
911
  for (const _ of rendered.split('\n')) {
@@ -933,7 +963,7 @@ export function compile(options: compile.Options): compile.ReturnType {
933
963
  return rule
934
964
  }
935
965
 
936
- if (Object.values(emitted.themes).includes(name)) {
966
+ if (Object.values(emitted.vars).includes(name)) {
937
967
  // Packed theme declarations have no authored source in this graph.
938
968
  Mapping.addMapping(cssMap, { generated: { column: 0, line } })
939
969
 
@@ -947,6 +977,19 @@ export function compile(options: compile.Options): compile.ReturnType {
947
977
  return rule
948
978
  }
949
979
 
980
+ if (
981
+ rule.startsWith(':root{') ||
982
+ rule.startsWith(':where(*){--z-f') ||
983
+ (rule.startsWith('@media ') &&
984
+ (rule.includes(':root{') ||
985
+ Object.values(emitted.vars).some((name) =>
986
+ rule.includes(`.${name}{`),
987
+ )))
988
+ ) {
989
+ Mapping.addMapping(cssMap, { generated: { column: 0, line } })
990
+ return rule
991
+ }
992
+
950
993
  const selector = `.${names.get(name)!}`
951
994
  const call = owners.get(name)!
952
995
 
@@ -995,6 +1038,19 @@ export function compile(options: compile.Options): compile.ReturnType {
995
1038
  ? String(property.key.value)
996
1039
  : ''
997
1040
 
1041
+ if (key === 'typography' && call.typography?.has(property.start)) {
1042
+ conditionNodes.push(
1043
+ ...Array.from(
1044
+ { length: call.typographyQueries?.get(property.start) ?? 0 },
1045
+ () => property,
1046
+ ),
1047
+ )
1048
+ return Array.from(
1049
+ { length: call.typography.get(property.start)! },
1050
+ () => property,
1051
+ )
1052
+ }
1053
+
998
1054
  const authoredLocations: readonly Ast.Node[] =
999
1055
  value.type === 'ArrayExpression'
1000
1056
  ? value.elements.filter(
@@ -1105,7 +1161,7 @@ export function compile(options: compile.Options): compile.ReturnType {
1105
1161
  sourcesContent: map.sourcesContent!,
1106
1162
  version: 3 as const,
1107
1163
  },
1108
- themes: emitted.themes,
1164
+ vars: emitted.vars,
1109
1165
  })
1110
1166
  }
1111
1167
 
@@ -1145,7 +1201,7 @@ export declare namespace compile {
1145
1201
  /** Standard rewritten-module map, including original source content. */
1146
1202
  readonly map: Mapping.EncodedSourceMap
1147
1203
  /** Stable scope classes keyed by local module/binding identity. */
1148
- readonly themes: Readonly<Record<string, string>>
1204
+ readonly vars: Readonly<Record<string, string>>
1149
1205
  }
1150
1206
  }
1151
1207
 
@@ -193,6 +193,7 @@ export function collect(options: collect.Options) {
193
193
  )) {
194
194
  const selected: Source.Call[] = []
195
195
  const inputs: NonNullable<Source.Call['runtimeComposition']>[number][] = []
196
+ const outputs = new Set<'html' | undefined>()
196
197
  let runtime = false
197
198
  let conditions = 0
198
199
  const attributes = new Map<string, string>()
@@ -256,7 +257,6 @@ export function collect(options: collect.Options) {
256
257
  const resolved = bindings.resolve(expression, nodes)
257
258
  const conditional =
258
259
  resolved.type === 'LogicalExpression' && resolved.operator === '&&'
259
- const condition = conditional ? conditions++ : undefined
260
260
  const value = conditional
261
261
  ? bindings.resolve(resolved.right, nodes)
262
262
  : resolved
@@ -305,6 +305,19 @@ export function collect(options: collect.Options) {
305
305
  'Composition requires statically known local style applications.',
306
306
  value,
307
307
  )
308
+ const scope = options.scopes?.get(value.start)
309
+ if (scope?.end === value.end) {
310
+ outputs.add(scope.output)
311
+ inputs.push({
312
+ start: expression.start,
313
+ end: expression.end,
314
+ name: '',
315
+ owners: [],
316
+ })
317
+ runtime = true
318
+ continue
319
+ }
320
+ const condition = conditional ? conditions++ : undefined
308
321
  const nested = calls.get(value.start)
309
322
  if (nested?.compositionCases && resolved !== expression)
310
323
  throw new Themes.InvalidError(
@@ -379,7 +392,8 @@ export function collect(options: collect.Options) {
379
392
  options.source.slice(application.start, application.calleeEnd),
380
393
  )
381
394
  }
382
- if (selected.some((call) => call.output !== selected[0]?.output))
395
+ for (const call of selected) outputs.add(call.output)
396
+ if (outputs.size > 1)
383
397
  throw new Themes.InvalidError(
384
398
  'Composition cannot mix HTML and React props.',
385
399
  node,
@@ -497,6 +511,12 @@ export function collect(options: collect.Options) {
497
511
  ),
498
512
  }
499
513
  : {}),
514
+ typography: new Map(
515
+ selected.flatMap((call) => [...(call.typography ?? [])]),
516
+ ),
517
+ typographyQueries: new Map(
518
+ selected.flatMap((call) => [...(call.typographyQueries ?? [])]),
519
+ ),
500
520
  name,
501
521
  start: node.start,
502
522
  end: node.end,
@@ -507,7 +527,7 @@ export function collect(options: collect.Options) {
507
527
  end: node.end,
508
528
  properties: selected.flatMap((call) => properties(call)),
509
529
  },
510
- ...(selected[0]?.output ? { output: selected[0].output } : {}),
530
+ ...(outputs.has('html') ? { output: 'html' as const } : {}),
511
531
  ...(identities.length ? { identity: identities.join(' ') } : {}),
512
532
  }
513
533
  calls.set(node.start, call)
@@ -538,6 +558,12 @@ export function collect(options: collect.Options) {
538
558
  start: call.start,
539
559
  end: call.end,
540
560
  compositionCase: true,
561
+ typography: new Map(
562
+ included.flatMap((call) => [...(call.typography ?? [])]),
563
+ ),
564
+ typographyQueries: new Map(
565
+ included.flatMap((call) => [...(call.typographyQueries ?? [])]),
566
+ ),
541
567
  body: {
542
568
  ...call.body!,
543
569
  properties: included.flatMap((call) => properties(call)),
@@ -604,6 +630,13 @@ export declare namespace collect {
604
630
  readonly links?: Readonly<Record<string, Themes.Link>> | undefined
605
631
  /** Parsed module with lexical binding scopes. */
606
632
  readonly program: Ast.Program
633
+ /** Validated variable scope applications preserved as runtime props. */
634
+ readonly scopes?:
635
+ | ReadonlyMap<
636
+ number,
637
+ { readonly end: number; readonly output: 'html' | undefined }
638
+ >
639
+ | undefined
607
640
  /** Source used to preserve initialization-sensitive binding reads. */
608
641
  readonly source: string
609
642
  /** Validated ordered style bodies. */
@@ -4,21 +4,32 @@
4
4
  */
5
5
  import * as Identity from '../../internal/Identity.js'
6
6
  import type * as Ast from '@oxc-project/types'
7
- import * as Config from '../../Config.js'
7
+ import * as Config from '../../internal/Configuration.js'
8
+ import * as PublicConfig from '../../Config.js'
8
9
  import * as Shorthands from '../../internal/Shorthands.js'
9
10
  import * as Token from '../../internal/Token.js'
10
- import * as Theme from '../../Theme.js'
11
+ import * as Theme from '../../internal/Theme.js'
12
+ import * as VariableSets from '../../internal/VariableSets.js'
13
+ import * as Vars from '../../Vars.js'
11
14
  import type * as Themes from './Themes.js'
12
15
 
13
16
  /** Reads a direct configuration factory without executing authored expressions. */
14
17
  export function collect(options: collect.Options): Themes.Link {
15
18
  function data(node: Ast.Node): unknown {
16
19
  const linked = options.resolve(node)
17
- if (linked?.kind === 'theme') return linked.definition
20
+ if (
21
+ linked?.kind === 'theme' ||
22
+ (linked?.call.variableConfig && linked.call.selection)
23
+ )
24
+ return linked.definition[Token.definition].contract.variableSet
25
+ ? VariableSets.from(linked.definition[Token.definition])
26
+ : linked.definition
18
27
 
19
28
  if (node.type === 'TSAsExpression' || node.type === 'TSSatisfiesExpression')
20
29
  return data(node.expression)
21
30
 
31
+ if (node.type === 'Literal' && node.value === false) return false
32
+
22
33
  if (node.type === 'ArrayExpression')
23
34
  return node.elements.map((node) => {
24
35
  if (!node || node.type === 'SpreadElement')
@@ -77,12 +88,40 @@ export function collect(options: collect.Options): Themes.Link {
77
88
  'Config.create accepts one literal options object.',
78
89
  )
79
90
 
80
- const input = (
91
+ let input = (
81
92
  options.expression.arguments.length
82
93
  ? data(options.expression.arguments[0]!)
83
94
  : {}
84
95
  ) as Config.create.Options
85
96
 
97
+ PublicConfig.create(input as unknown as PublicConfig.create.Options)
98
+
99
+ const authored = input as unknown as Config.VariableOptions
100
+ const variableMode = authored.vars !== undefined
101
+ if (variableMode) {
102
+ Config.create(authored)
103
+ const wrap = (value: Vars.Definition | Vars.Values) =>
104
+ VariableSets.theme(
105
+ Token.definition in value
106
+ ? (value as Vars.Definition)
107
+ : Vars.define(value as Vars.Values),
108
+ authored.mappings,
109
+ )
110
+ const { vars, defaultVars, mappings: _mappings, ...rest } = authored
111
+ input =
112
+ defaultVars === undefined
113
+ ? { ...rest, theme: wrap(vars as Vars.Definition) }
114
+ : {
115
+ ...rest,
116
+ defaultTheme: defaultVars,
117
+ themes: Object.fromEntries(
118
+ Object.entries(vars).map(([key, value]) => [
119
+ key,
120
+ wrap(value as Vars.Definition),
121
+ ]),
122
+ ),
123
+ }
124
+ }
86
125
  const config = Config.create(input)
87
126
 
88
127
  const catalog = (() => {
@@ -102,8 +141,17 @@ export function collect(options: collect.Options): Themes.Link {
102
141
  ? options.name
103
142
  : Identity.requireId(input.id, 'Config.create')
104
143
  const contract = Object.freeze({
144
+ ...(variableMode
145
+ ? {
146
+ variableSet: true,
147
+ mappings: VariableSets.mappings(authored.mappings),
148
+ }
149
+ : {}),
105
150
  [Token.identity]: identity,
106
151
  cssOutput: input.cssOutput ?? 'atomic',
152
+ ...(input.defaultLayer !== undefined
153
+ ? { defaultLayer: input.defaultLayer }
154
+ : {}),
107
155
  ...(input.shorthands
108
156
  ? { shorthands: Shorthands.read(input.shorthands) }
109
157
  : {}),
@@ -151,6 +199,16 @@ export function collect(options: collect.Options): Themes.Link {
151
199
  if ('themes' in config && selected)
152
200
  members[JSON.stringify(['theme'])] = selected
153
201
 
202
+ if (variableMode && selected)
203
+ members[JSON.stringify(['vars'])] = {
204
+ ...selected,
205
+ call: {
206
+ ...selected.call,
207
+ directVariables: true,
208
+ type: `import('zyzz').Vars.References<${selected.call.tokenType}>`,
209
+ },
210
+ }
211
+
154
212
  const definition =
155
213
  selected?.definition ?? Token.bind(Theme.define({}), contract)
156
214
 
@@ -160,10 +218,14 @@ export function collect(options: collect.Options): Themes.Link {
160
218
  return {
161
219
  defaultTheme: input.defaultTheme,
162
220
  themes: Object.fromEntries(
163
- Object.entries(catalog).map(([name, theme]) => [
164
- name,
165
- { ...values(theme.tokens), ...theme[Token.definition].queries },
166
- ]),
221
+ Object.keys(catalog).map((name) => {
222
+ const theme =
223
+ members[JSON.stringify(['themes', name])]!.definition!
224
+ return [
225
+ name,
226
+ { ...values(theme.tokens), ...theme[Token.definition].queries },
227
+ ]
228
+ }),
167
229
  ),
168
230
  }
169
231
  }
@@ -171,8 +233,8 @@ export function collect(options: collect.Options): Themes.Link {
171
233
  if ('theme' in config) {
172
234
  return {
173
235
  theme: {
174
- ...values(config.theme.tokens),
175
- ...config.theme[Token.definition].queries,
236
+ ...values(definition.tokens),
237
+ ...definition[Token.definition].queries,
176
238
  },
177
239
  }
178
240
  }
@@ -180,6 +242,9 @@ export function collect(options: collect.Options): Themes.Link {
180
242
  return {}
181
243
  })(),
182
244
  ...(input.cssOutput ? { cssOutput: input.cssOutput } : {}),
245
+ ...(input.defaultLayer !== undefined
246
+ ? { defaultLayer: input.defaultLayer }
247
+ : {}),
183
248
  ...(input.shorthands ? { shorthands: input.shorthands } : {}),
184
249
  ...(input.output ? { output: input.output } : {}),
185
250
  ...(input.layers ? { layers: input.layers } : {}),
@@ -189,6 +254,9 @@ export function collect(options: collect.Options): Themes.Link {
189
254
  return {
190
255
  binding: options.name,
191
256
  call: {
257
+ ...(variableMode
258
+ ? { variableConfig: true, variableMappings: authored.mappings }
259
+ : {}),
192
260
  appearance: true,
193
261
  script: true,
194
262
  end: options.expression.end,
@@ -199,7 +267,9 @@ export function collect(options: collect.Options): Themes.Link {
199
267
  options: normalized,
200
268
  start: options.expression.start,
201
269
  tokenType: selected?.call.tokenType ?? '{}',
202
- type: `import('zyzz').Config.create.ReturnType<${type(normalized)}>`,
270
+ type: variableMode
271
+ ? `import('zyzz').Config.VariableConfig<${type({ ...normalized, theme: undefined, themes: undefined, defaultTheme: undefined, vars: 'themes' in normalized ? normalized.themes : normalized.theme, ...('themes' in normalized ? { defaultVars: normalized.defaultTheme } : {}), ...(authored.mappings !== undefined ? { mappings: VariableSets.mappings(authored.mappings) } : {}) })}>`
272
+ : `import('zyzz').Config.create.ReturnType<${type(normalized)}>`,
203
273
  },
204
274
  definition,
205
275
  kind: 'config',
@@ -224,10 +294,12 @@ export declare namespace collect {
224
294
 
225
295
  /** Encodes validated literal options as a declaration type, never executable text. */
226
296
  export function type(value: unknown): string {
297
+ if (Token.is(value)) return `import('zyzz').Vars.Reference<'${value.group}'>`
227
298
  if (Array.isArray(value)) return `readonly [${value.map(type).join(',')}]`
228
299
  if (!value || typeof value !== 'object') return JSON.stringify(value)
229
300
 
230
301
  return `{${Object.entries(value)
302
+ .filter(([, value]) => value !== undefined)
231
303
  .map(([key, value]) => `readonly ${JSON.stringify(key)}:${type(value)}`)
232
304
  .join(';')}}`
233
305
  }