zyzz 0.0.3 → 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 (247) 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 +15 -12
  7. package/dist/Style.d.ts.map +1 -1
  8. package/dist/Style.js +51 -26
  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 +8 -6
  21. package/dist/compiler/Source.d.ts.map +1 -1
  22. package/dist/compiler/Source.js +47 -14
  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 +58 -22
  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 +155 -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 +147 -2562
  72. package/dist/default.d.ts.map +1 -1
  73. package/dist/default.js +85 -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/{Theme.d.ts → internal/Theme.d.ts} +15 -13
  102. package/dist/internal/Theme.d.ts.map +1 -0
  103. package/dist/{Theme.js → internal/Theme.js} +39 -12
  104. package/dist/internal/Theme.js.map +1 -0
  105. package/dist/internal/Token.d.ts +46 -16
  106. package/dist/internal/Token.d.ts.map +1 -1
  107. package/dist/internal/Token.js +89 -5
  108. package/dist/internal/Token.js.map +1 -1
  109. package/dist/internal/Typography.d.ts +13 -5
  110. package/dist/internal/Typography.d.ts.map +1 -1
  111. package/dist/internal/Typography.js +42 -6
  112. package/dist/internal/Typography.js.map +1 -1
  113. package/dist/internal/Value.d.ts +6 -1
  114. package/dist/internal/Value.d.ts.map +1 -1
  115. package/dist/internal/Value.js.map +1 -1
  116. package/dist/internal/VariableSets.d.ts +20 -0
  117. package/dist/internal/VariableSets.d.ts.map +1 -0
  118. package/dist/internal/VariableSets.js +317 -0
  119. package/dist/internal/VariableSets.js.map +1 -0
  120. package/dist/next/loader.js +15 -5
  121. package/dist/next/loader.js.map +1 -1
  122. package/dist/node/Host.d.ts.map +1 -1
  123. package/dist/node/Host.js +1 -1
  124. package/dist/node/Host.js.map +1 -1
  125. package/dist/react-native/Host.d.ts +2 -2
  126. package/dist/react-native/Host.d.ts.map +1 -1
  127. package/dist/react-native/Host.js +4 -4
  128. package/dist/react-native/Host.js.map +1 -1
  129. package/dist/react-native/StyleSheet.d.ts +9 -8
  130. package/dist/react-native/StyleSheet.d.ts.map +1 -1
  131. package/dist/react-native/StyleSheet.js +29 -23
  132. package/dist/react-native/StyleSheet.js.map +1 -1
  133. package/dist/react-native/Variants.d.ts +3 -3
  134. package/dist/react-native/Variants.d.ts.map +1 -1
  135. package/dist/react-native/Variants.js +2 -2
  136. package/dist/react-native/react.d.ts +1 -1
  137. package/dist/react-native/react.d.ts.map +1 -1
  138. package/dist/react-native/react.js +5 -5
  139. package/dist/react-native/react.js.map +1 -1
  140. package/dist/rollup/index.d.ts +3 -0
  141. package/dist/rollup/index.d.ts.map +1 -0
  142. package/dist/rollup/index.js +5 -0
  143. package/dist/rollup/index.js.map +1 -0
  144. package/dist/runtime/Appearance.d.ts +7 -7
  145. package/dist/runtime/Appearance.d.ts.map +1 -1
  146. package/dist/runtime/Appearance.js +15 -15
  147. package/dist/runtime/Appearance.js.map +1 -1
  148. package/dist/runtime/Dynamic.d.ts.map +1 -1
  149. package/dist/runtime/Dynamic.js +4 -1
  150. package/dist/runtime/Dynamic.js.map +1 -1
  151. package/dist/runtime/NativeContext.d.ts +4 -4
  152. package/dist/runtime/NativeContext.d.ts.map +1 -1
  153. package/dist/runtime/NativeContext.js +5 -5
  154. package/dist/runtime/NativeContext.js.map +1 -1
  155. package/dist/runtime/NativeDynamic.d.ts +1 -1
  156. package/dist/runtime/NativeDynamic.d.ts.map +1 -1
  157. package/dist/runtime/NativeDynamic.js.map +1 -1
  158. package/dist/runtime/Props.d.ts +1 -1
  159. package/dist/runtime/Props.js +3 -3
  160. package/dist/runtime/Props.js.map +1 -1
  161. package/dist/runtime/Selection.d.ts +14 -10
  162. package/dist/runtime/Selection.d.ts.map +1 -1
  163. package/dist/runtime/Selection.js +19 -18
  164. package/dist/runtime/Selection.js.map +1 -1
  165. package/dist/styleFunction.d.ts +14 -14
  166. package/dist/styleFunction.d.ts.map +1 -1
  167. package/dist/styleFunction.js.map +1 -1
  168. package/dist/unplugin/index.d.ts +20 -0
  169. package/dist/unplugin/index.d.ts.map +1 -0
  170. package/dist/unplugin/index.js +498 -0
  171. package/dist/unplugin/index.js.map +1 -0
  172. package/dist/variants.d.ts +3 -3
  173. package/dist/variants.d.ts.map +1 -1
  174. package/dist/variants.js.map +1 -1
  175. package/dist/vite/index.d.ts.map +1 -1
  176. package/dist/vite/index.js +6 -3
  177. package/dist/vite/index.js.map +1 -1
  178. package/dist/web/Css.d.ts +4 -3
  179. package/dist/web/Css.d.ts.map +1 -1
  180. package/dist/web/Css.js +4 -4
  181. package/dist/web/Css.js.map +1 -1
  182. package/dist/web/internal/Themes.d.ts +3 -2
  183. package/dist/web/internal/Themes.d.ts.map +1 -1
  184. package/dist/web/internal/Themes.js +42 -6
  185. package/dist/web/internal/Themes.js.map +1 -1
  186. package/dist/webpack/index.d.ts +3 -0
  187. package/dist/webpack/index.d.ts.map +1 -0
  188. package/dist/webpack/index.js +5 -0
  189. package/dist/webpack/index.js.map +1 -0
  190. package/package.json +23 -2
  191. package/src/Config.ts +43 -685
  192. package/src/Style.ts +97 -34
  193. package/src/Vars.ts +299 -0
  194. package/src/compiler/Graph.ts +14 -9
  195. package/src/compiler/Native.ts +29 -32
  196. package/src/compiler/Source.ts +59 -21
  197. package/src/compiler/Transform.ts +73 -23
  198. package/src/compiler/internal/Compositions.ts +36 -3
  199. package/src/compiler/internal/Configurations.ts +83 -11
  200. package/src/compiler/internal/Contract.ts +207 -15
  201. package/src/compiler/internal/Contributions.ts +1 -1
  202. package/src/compiler/internal/Dynamic.ts +1 -1
  203. package/src/compiler/internal/Expression.ts +26 -0
  204. package/src/compiler/internal/Identifiers.ts +2 -1
  205. package/src/compiler/internal/NativeBindings.ts +3 -3
  206. package/src/compiler/internal/PackedStyles.ts +22 -26
  207. package/src/compiler/internal/Recipes.ts +1 -1
  208. package/src/compiler/internal/Selectors.ts +1 -1
  209. package/src/compiler/internal/Themes.ts +266 -57
  210. package/src/compiler/internal/{Variables.ts → Vars.ts} +2 -2
  211. package/src/default.ts +173 -154
  212. package/src/esbuild/index.ts +5 -0
  213. package/src/index.ts +4 -4
  214. package/src/internal/Authoring.ts +5 -7
  215. package/src/internal/Binding.ts +1 -1
  216. package/src/internal/Completion.ts +42 -0
  217. package/src/internal/Condition.ts +31 -1
  218. package/src/internal/Configuration.ts +970 -0
  219. package/src/{Theme.ts → internal/Theme.ts} +75 -20
  220. package/src/internal/Token.ts +258 -85
  221. package/src/internal/Typography.ts +71 -16
  222. package/src/internal/Value.ts +45 -26
  223. package/src/internal/VariableSets.ts +458 -0
  224. package/src/next/loader.ts +19 -5
  225. package/src/node/Host.ts +1 -2
  226. package/src/react-native/Host.ts +6 -6
  227. package/src/react-native/StyleSheet.ts +49 -42
  228. package/src/react-native/Variants.ts +3 -3
  229. package/src/react-native/react.ts +6 -6
  230. package/src/rollup/index.ts +5 -0
  231. package/src/runtime/Appearance.ts +20 -20
  232. package/src/runtime/Dynamic.ts +4 -1
  233. package/src/runtime/NativeContext.ts +8 -8
  234. package/src/runtime/NativeDynamic.ts +2 -4
  235. package/src/runtime/Props.ts +3 -3
  236. package/src/runtime/Selection.ts +44 -33
  237. package/src/styleFunction.ts +22 -19
  238. package/src/unplugin/index.ts +600 -0
  239. package/src/variants.ts +7 -5
  240. package/src/vite/index.ts +5 -4
  241. package/src/web/Css.ts +10 -7
  242. package/src/web/internal/Themes.ts +52 -9
  243. package/src/webpack/index.ts +5 -0
  244. package/dist/Theme.d.ts.map +0 -1
  245. package/dist/Theme.js.map +0 -1
  246. package/dist/compiler/internal/Variables.d.ts.map +0 -1
  247. package/dist/compiler/internal/Variables.js.map +0 -1
@@ -2,17 +2,18 @@
2
2
  * Defines typed token contracts and compatible immutable theme overrides.
3
3
  * @module
4
4
  */
5
- import { MissingTransformError, style } from './styleFunction.js'
6
- import * as Authoring from './internal/Authoring.js'
7
- import * as Identity from './internal/Identity.js'
8
- import { variants } from './variants.js'
9
- import type * as Binding from './internal/Binding.js'
10
- import type * as Literal from './internal/Literal.js'
11
- import * as Query from './internal/Query.js'
12
- import * as Token from './internal/Token.js'
13
- import * as Typography from './internal/Typography.js'
14
- import type * as Value from './internal/Value.js'
15
- import type * as Style from './Style.js'
5
+ import { MissingTransformError, style } from '../styleFunction.js'
6
+ import * as Authoring from './Authoring.js'
7
+ import * as Identity from './Identity.js'
8
+ import { variants } from '../variants.js'
9
+ import type * as Binding from './Binding.js'
10
+ import type * as Literal from './Literal.js'
11
+ import * as Query from './Query.js'
12
+ import * as Token from './Token.js'
13
+ import * as Typography from './Typography.js'
14
+ import type * as Value from './Value.js'
15
+ import type * as Style from '../Style.js'
16
+ import * as Condition from './Condition.js'
16
17
 
17
18
  /** Complete color-scheme pair or a shared color. */
18
19
  export type Color =
@@ -96,7 +97,7 @@ export type Definition<
96
97
  /** Token-aware single-element recipe authoring. */
97
98
  readonly variants: boundVariants
98
99
  /** Web variable references; source templates retain their identity and fallback. */
99
- readonly vars: Token.Variables<References<tokens>>
100
+ readonly vars: Token.Vars<References<tokens>>
100
101
  }
101
102
 
102
103
  /**
@@ -128,6 +129,8 @@ export function extend<
128
129
  data.contract,
129
130
  data.values,
130
131
  data.queries,
132
+ undefined,
133
+ data.paths,
131
134
  ) as unknown as Definition<tokens, boundStyle, boundVariants>
132
135
  }
133
136
 
@@ -218,6 +221,7 @@ type ReferenceTree<
218
221
  type Scalar<group> = group extends
219
222
  | 'spacing'
220
223
  | 'borderRadius'
224
+ | 'borderWidth'
221
225
  | 'margin'
222
226
  | 'padding'
223
227
  ? Literal.Length
@@ -242,6 +246,10 @@ export type Tokens = {
242
246
  readonly borderColor?: Palette<Color> | undefined
243
247
  /** Nonnegative corner radii. */
244
248
  readonly borderRadius?: Palette<Literal.Length> | undefined
249
+ /** Nonnegative widths for physical and logical border declarations. */
250
+ readonly borderWidth?:
251
+ | Palette<Exclude<Literal.Length, `${number}%`>>
252
+ | undefined
245
253
  /** Compile-time viewport width thresholds. */
246
254
  readonly breakpoints?: Readonly<Record<string, Query.Length>> | undefined
247
255
  /** Shared colors available to every supported color property. */
@@ -288,6 +296,7 @@ function build(
288
296
  base?: Readonly<Record<string, Token.Value>>,
289
297
  baseQueries?: Query.Metadata,
290
298
  scope?: string,
299
+ basePaths?: Readonly<Record<string, readonly string[]>>,
291
300
  ) {
292
301
  const values: Record<string, Token.Value> = Object.assign(
293
302
  Object.create(null),
@@ -306,6 +315,9 @@ function build(
306
315
  ) as Record<string, string>,
307
316
  }
308
317
 
318
+ const paths: Record<string, readonly string[]> = { ...basePaths }
319
+ const conditions: (readonly string[])[] = []
320
+
309
321
  let hasQueries = !!baseQueries
310
322
  const active = new Set<object>()
311
323
 
@@ -315,6 +327,8 @@ function build(
315
327
  path: readonly string[],
316
328
  ) {
317
329
  const key = path.join('.')
330
+ if (path.some((part) => part.startsWith('@')))
331
+ paths[key] = Object.freeze([...path])
318
332
  const scalar = typeof value === 'string' || typeof value === 'number'
319
333
  if (group === 'typography' && scalar)
320
334
  throw new InvalidError(
@@ -374,6 +388,24 @@ function build(
374
388
 
375
389
  for (const [name, child] of entries!) {
376
390
  const next = [...path, name]
391
+ if (group === 'typography' && name.startsWith('@')) {
392
+ if (!Typography.condition(name))
393
+ throw new InvalidError(
394
+ next,
395
+ 'Typography conditions must be media or container queries.',
396
+ )
397
+ conditions.push(next)
398
+ }
399
+ if (
400
+ group === 'typography' &&
401
+ path.some((part) => part.startsWith('@')) &&
402
+ !Typography.condition(name) &&
403
+ !Typography.properties.includes(name as Typography.Property)
404
+ )
405
+ throw new InvalidError(
406
+ next,
407
+ 'Typography query blocks accept only typography fields and queries.',
408
+ )
377
409
  if (
378
410
  base &&
379
411
  !Object.keys(base).some(
@@ -482,6 +514,7 @@ function build(
482
514
  'backgroundColor',
483
515
  'borderColor',
484
516
  'borderRadius',
517
+ 'borderWidth',
485
518
  'color',
486
519
  'margin',
487
520
  'padding',
@@ -510,7 +543,8 @@ function build(
510
543
  for (const [name, value] of entries) {
511
544
  if (
512
545
  group === 'typography' &&
513
- Typography.properties.includes(name as Typography.Property)
546
+ (Typography.properties.includes(name as Typography.Property) ||
547
+ name.startsWith('@'))
514
548
  )
515
549
  throw new InvalidError(
516
550
  [group, name],
@@ -520,12 +554,20 @@ function build(
520
554
  }
521
555
  }
522
556
 
557
+ for (const path of conditions) {
558
+ try {
559
+ Condition.normalize(Query.resolve(path.at(-1)!, queries))
560
+ } catch (error) {
561
+ throw new InvalidError(path, (error as Error).message)
562
+ }
563
+ }
564
+
523
565
  type Tree = { [key: string]: Tree | Token.Reference }
524
566
 
525
567
  const tokens: Tree = Object.create(null)
526
568
 
527
569
  for (const [path, value] of Object.entries(values)) {
528
- const parts = path.split('.')
570
+ const parts = paths[path] ?? path.split('.')
529
571
  let tree = tokens
530
572
 
531
573
  for (const part of parts.slice(0, -1))
@@ -577,6 +619,7 @@ function build(
577
619
  {
578
620
  value: Object.freeze({
579
621
  contract,
622
+ ...(Object.keys(paths).length ? { paths: Object.freeze(paths) } : {}),
580
623
  values: Object.freeze(values),
581
624
  ...(hasQueries
582
625
  ? {
@@ -627,7 +670,8 @@ function record(
627
670
  if (
628
671
  typeof key !== 'string' ||
629
672
  !key ||
630
- key.includes('.') ||
673
+ (key.includes('.') &&
674
+ !(path[0] === 'typography' && Typography.condition(key))) ||
631
675
  !descriptor.enumerable ||
632
676
  !('value' in descriptor)
633
677
  )
@@ -694,7 +738,7 @@ type ValidPalette<palette, group> = palette extends undefined
694
738
  [key in keyof palette]: key extends `${string}!${string}`
695
739
  ? never
696
740
  : group extends 'typography'
697
- ? key extends Typography.Property
741
+ ? key extends Typography.Property | `@${string}`
698
742
  ? never
699
743
  : ValidTree<palette[key], group>
700
744
  : ValidTree<palette[key], group>
@@ -767,11 +811,22 @@ type ValidTree<tree, group> = tree extends string | number
767
811
  [key in keyof tree]: key extends `${string}!${string}`
768
812
  ? never
769
813
  : group extends 'typography'
770
- ? key extends Typography.Property
771
- ? tree[key] extends string | number
772
- ? ValidTree<tree[key], key>
814
+ ? key extends `@${string}`
815
+ ? key extends Typography.Condition
816
+ ? ValidTree<tree[key], group> &
817
+ Record<
818
+ Exclude<
819
+ keyof tree[key],
820
+ Typography.Property | Typography.Condition
821
+ >,
822
+ never
823
+ >
773
824
  : never
774
- : ValidTree<tree[key], group>
825
+ : key extends Typography.Property
826
+ ? tree[key] extends string | number
827
+ ? ValidTree<tree[key], key>
828
+ : never
829
+ : ValidTree<tree[key], group>
775
830
  : ValidTree<tree[key], group>
776
831
  }
777
832
  : group extends 'color' | 'backgroundColor' | 'borderColor' | 'textColor'
@@ -2,19 +2,28 @@
2
2
  * Carries portable theme references and enforces their property domains.
3
3
  * @module
4
4
  */
5
- import type * as Binding from './Binding.js'
5
+ import * as Binding from './Binding.js'
6
6
  import type * as Query from './Query.js'
7
7
  import type * as Shorthands from './Shorthands.js'
8
- import type * as Theme from '../Theme.js'
8
+ import type * as Theme from './Theme.js'
9
+ import type * as ValueSyntax from './Value.js'
10
+ import type * as VariableSets from '../Vars.js'
9
11
  import * as Literal from './Literal.js'
12
+ import * as VariableData from './VariableSets.js'
10
13
 
11
14
  /** Checks a reference's property domain. */
12
15
  export function accepts(
13
16
  group: Group,
14
17
  property: keyof Literal.Properties,
15
18
  ): boolean {
19
+ if (property.startsWith('--')) return true
20
+ if (group === 'number' || group === 'string') return false
16
21
  if (group === 'color') return Literal.rule(property)?.kind === 'color'
17
22
  if (group === 'borderColor') return /^border.*Color$/.test(property)
23
+ if (group === 'borderWidth')
24
+ return /^border(?:Block|Inline)?(?:Start|End|Top|Right|Bottom|Left)?Width$/.test(
25
+ property,
26
+ )
18
27
  if (group === 'borderRadius') return /^border.*Radius$/.test(property)
19
28
  if (group === 'textColor') return property === 'color'
20
29
 
@@ -57,6 +66,50 @@ export function accepts(
57
66
  return group === property
58
67
  }
59
68
 
69
+ /** Checks explicit variable values independently of shorthand category mappings. */
70
+ export function acceptsReference(
71
+ reference: Reference,
72
+ property: keyof Literal.Properties,
73
+ ): boolean {
74
+ if (!reference.contract.variableSet) return accepts(reference.group, property)
75
+ if (property.startsWith('--')) return true
76
+ function check(value: Value): boolean {
77
+ if (is(value)) return acceptsReference(value, property)
78
+ if (typeof value === 'object') return Object.values(value).every(check)
79
+ if (typeof value === 'number') {
80
+ const rule = Literal.rule(property)
81
+ if (rule?.kind === 'number')
82
+ return (
83
+ value >= rule.min &&
84
+ value <= rule.max &&
85
+ (!rule.integer || Number.isInteger(value))
86
+ )
87
+ return value === 0 && Binding.accepts('length', property)
88
+ }
89
+ if (reference.group === 'color') return Binding.accepts('color', property)
90
+ if (reference.group === 'spacing')
91
+ return (
92
+ (Literal.rule(property)?.kind === 'compound' &&
93
+ accepts('spacing', property)) ||
94
+ Binding.accepts(
95
+ value.endsWith('%')
96
+ ? value.startsWith('-')
97
+ ? 'signedPercentage'
98
+ : 'percentage'
99
+ : value.startsWith('-')
100
+ ? 'signedLength'
101
+ : 'length',
102
+ property,
103
+ )
104
+ )
105
+ return (
106
+ Literal.isLiteral(property, value) ||
107
+ Literal.rule(property)?.kind === 'compound'
108
+ )
109
+ }
110
+ return check(reference.value)
111
+ }
112
+
60
113
  /** Rebinds validated scalar references to a explicitly owned contract identity. */
61
114
  export function bind<tokens extends Theme.Tokens>(
62
115
  original: Theme.Definition<tokens>,
@@ -64,6 +117,9 @@ export function bind<tokens extends Theme.Tokens>(
64
117
  ): Theme.Definition<tokens> {
65
118
  type Tree = { [key: string]: Reference | Tree }
66
119
 
120
+ const metadata = original[definition]
121
+ const values = VariableData.rebind(metadata, contract)
122
+
67
123
  function rebind(tree: Theme.References<Theme.Tokens>): Tree {
68
124
  return Object.freeze(
69
125
  Object.fromEntries(
@@ -74,7 +130,7 @@ export function bind<tokens extends Theme.Tokens>(
74
130
  contract,
75
131
  group: value.group,
76
132
  path: value.path,
77
- value: value.value,
133
+ value: values[value.path]!,
78
134
  })
79
135
  : rebind(value as Theme.References<Theme.Tokens>),
80
136
  ]),
@@ -96,7 +152,7 @@ export function bind<tokens extends Theme.Tokens>(
96
152
  vars: variables(tokens),
97
153
  },
98
154
  definition,
99
- { value: Object.freeze({ ...original[definition], contract }) },
155
+ { value: Object.freeze({ ...metadata, contract, values }) },
100
156
  ),
101
157
  ) as Theme.Definition<tokens>
102
158
  }
@@ -106,8 +162,14 @@ export const complete = Symbol('zyzz.contract.complete')
106
162
 
107
163
  /** Opaque data shared by a definition and its compatible extensions. */
108
164
  export type Contract = {
165
+ /** Whether values belong to independent variables rather than fixed theme categories. */
166
+ readonly variableSet?: boolean | undefined
167
+ /** Configuration-local category-to-property mappings. */
168
+ readonly mappings?: VariableSets.Mappings | false | undefined
109
169
  /** Web emission mode retained by configuration-bound theme handles. */
110
170
  readonly cssOutput?: 'atomic' | 'grouped' | undefined
171
+ /** Fallback layer for declarations without an explicit layer. */
172
+ readonly defaultLayer?: string | undefined
111
173
  /** Configuration-local property aliases, inherited by bound handles. */
112
174
  readonly shorthands?: Shorthands.Map | undefined
113
175
  readonly [complete]?: boolean | undefined
@@ -153,23 +215,24 @@ export function compose(
153
215
  export const web = Symbol('zyzz.web.variable')
154
216
 
155
217
  /** Web reference with the original token domain. */
156
- export type Variable<group extends Group = Group> = Reference<group> & {
157
- readonly [web]: true
158
- }
218
+ export type Variable<group extends Group = Group> = `var(--${string})` &
219
+ Reference<group> & {
220
+ readonly [web]: true
221
+ }
159
222
 
160
223
  /** Replaces portable scalar leaves with web-only references. */
161
- export type Variables<tree> =
224
+ export type Vars<tree> =
162
225
  tree extends Reference<infer group>
163
226
  ? Variable<group>
164
- : { readonly [key in keyof tree]: Variables<tree[key]> }
227
+ : { readonly [key in keyof tree]: Vars<tree[key]> }
165
228
 
166
229
  /** Creates web-only reference leaves without changing contract identities. */
167
- export function variables<tree>(tree: tree): Variables<tree> {
230
+ export function variables<tree>(tree: tree): Vars<tree> {
168
231
  if (is(tree))
169
232
  return Object.freeze({
170
233
  ...tree,
171
234
  [web]: true as const,
172
- }) as unknown as Variables<tree>
235
+ }) as unknown as Vars<tree>
173
236
 
174
237
  return Object.freeze(
175
238
  Object.fromEntries(
@@ -178,7 +241,7 @@ export function variables<tree>(tree: tree): Variables<tree> {
178
241
  variables(value),
179
242
  ]),
180
243
  ),
181
- ) as Variables<tree>
244
+ ) as Vars<tree>
182
245
  }
183
246
 
184
247
  /** Internal definition metadata; never enumerable consumer output. */
@@ -186,9 +249,12 @@ export const definition = Symbol('zyzz.theme')
186
249
 
187
250
  /** Supported scalar token groups. */
188
251
  export type Group =
252
+ | 'number'
253
+ | 'string'
189
254
  | 'backgroundColor'
190
255
  | 'borderColor'
191
256
  | 'borderRadius'
257
+ | 'borderWidth'
192
258
  | 'color'
193
259
  | 'margin'
194
260
  | 'padding'
@@ -223,19 +289,53 @@ export function is(value: unknown): value is Reference {
223
289
 
224
290
  /** Immutable theme data carried directly by each definition. */
225
291
  export type Metadata = {
292
+ /** Original segments for typography paths containing query punctuation. */
293
+ readonly paths?: Readonly<Record<string, readonly string[]>> | undefined
226
294
  readonly queries?: Query.Metadata | undefined
227
295
  readonly contract: Contract
228
296
  readonly values: Readonly<Record<string, Value>>
229
297
  }
230
298
 
231
- /** Inferred shorthand names whose leaves belong to a property's domain. */
232
- export type Names<tokens, property extends keyof Literal.Properties> = {
233
- [group in Extract<keyof tokens, Group>]: property extends Properties<group>
234
- ? Paths<NonNullable<tokens[group]>>
235
- : never
236
- }[Extract<keyof tokens, Group>]
299
+ /** Literal domain carried by explicit variable references during type checking. */
300
+ export const scalar = Symbol('zyzz.variable.scalar')
237
301
 
238
- type Paths<tree> = [tree] extends [never]
302
+ /** Inferred shorthand names whose leaves belong to a property domain. */
303
+ export type Names<
304
+ tokens,
305
+ property extends keyof Literal.Properties,
306
+ > = tokens extends {
307
+ readonly '~vars': { values: infer values; mappings: infer mappings }
308
+ }
309
+ ? mappings extends false
310
+ ? Paths<
311
+ Omit<values, 'breakpoints' | 'containers' | 'containerNames'>,
312
+ property
313
+ >
314
+ : {
315
+ [category in keyof values]: category extends keyof mappings
316
+ ? mappings[category] extends readonly unknown[]
317
+ ? property extends mappings[category][number]
318
+ ? Paths<values[category], property>
319
+ : never
320
+ : never
321
+ : category extends Group
322
+ ? property extends Properties<category>
323
+ ? Paths<values[category], property>
324
+ : never
325
+ : never
326
+ }[keyof values]
327
+ : {
328
+ [group in Extract<
329
+ keyof tokens,
330
+ Group
331
+ >]: property extends Properties<group>
332
+ ? Paths<NonNullable<tokens[group]>>
333
+ : never
334
+ }[Extract<keyof tokens, Group>]
335
+
336
+ type Paths<tree, property extends keyof Literal.Properties = never> = [
337
+ tree,
338
+ ] extends [never]
239
339
  ? never
240
340
  : string extends keyof tree
241
341
  ? string
@@ -243,72 +343,95 @@ type Paths<tree> = [tree] extends [never]
243
343
  [key in Extract<keyof tree, number | string>]: NonNullable<
244
344
  tree[key]
245
345
  > extends Value
246
- ?
247
- | key
248
- | `${key}`
249
- | (key extends `${infer numericKey extends number}`
250
- ? `${numericKey}` extends key
251
- ? numericKey
252
- : never
253
- : never)
254
- : `${key}.${Paths<NonNullable<tree[key]>>}`
346
+ ? [property] extends [never]
347
+ ? PathKey<key>
348
+ : VariableSets.Scalar<
349
+ NonNullable<tree[key]>
350
+ > extends Literal.Properties[property] &
351
+ ValueSyntax.Checked<
352
+ Record<
353
+ property,
354
+ VariableSets.Scalar<NonNullable<tree[key]>>
355
+ >
356
+ >[property]
357
+ ? PathKey<key>
358
+ : never
359
+ : `${key}.${Paths<NonNullable<tree[key]>, property>}`
255
360
  }[Extract<keyof tree, number | string>]
256
361
 
362
+ type PathKey<key extends number | string> =
363
+ | key
364
+ | `${key}`
365
+ | (key extends `${infer numericKey extends number}`
366
+ ? `${numericKey}` extends key
367
+ ? numericKey
368
+ : never
369
+ : never)
370
+
257
371
  /** Property domains accepted by each token group. */
258
- export type Properties<group extends Group> = group extends 'margin' | 'padding'
259
- ? Exclude<
260
- Extract<keyof Literal.Properties, `${group}${string}`>,
261
- 'marginTrim'
262
- >
263
- : group extends 'spacing'
264
- ? Extract<
265
- keyof Literal.Properties,
266
- | `blockSize`
267
- | `bottom`
268
- | `columnGap`
269
- | `flexBasis`
270
- | `gap`
271
- | `height`
272
- | `inlineSize`
273
- | `inset${string}`
274
- | `left`
275
- | Exclude<
276
- Extract<keyof Literal.Properties, `margin${string}`>,
277
- 'marginTrim'
278
- >
279
- | `maxBlockSize`
280
- | `maxHeight`
281
- | `maxInlineSize`
282
- | `maxWidth`
283
- | `minBlockSize`
284
- | `minHeight`
285
- | `minInlineSize`
286
- | `minWidth`
287
- | `padding${string}`
288
- | `right`
289
- | `rowGap`
290
- | `scrollPadding${string}`
291
- | `textDecorationThickness`
292
- | `textIndent`
293
- | `textUnderlineOffset`
294
- | `top`
295
- | `width`
372
+ export type Properties<group extends Group> = `--${string}` | Property<group>
373
+
374
+ type Property<group extends Group> = group extends 'number' | 'string'
375
+ ? never
376
+ : group extends 'margin' | 'padding'
377
+ ? Exclude<
378
+ Extract<keyof Literal.Properties, `${group}${string}`>,
379
+ 'marginTrim'
296
380
  >
297
- : group extends 'textColor'
298
- ? 'color'
299
- : group extends 'color'
300
- ? {
301
- [property in keyof typeof Literal.rules]: (typeof Literal.rules)[property] extends {
302
- kind: 'color'
303
- }
304
- ? property
305
- : never
306
- }[keyof typeof Literal.rules]
307
- : group extends 'borderColor'
308
- ? Extract<keyof Literal.Properties, `border${string}Color`>
309
- : group extends 'borderRadius'
310
- ? Extract<keyof Literal.Properties, `border${string}Radius`>
311
- : group
381
+ : group extends 'spacing'
382
+ ? Extract<
383
+ keyof Literal.Properties,
384
+ | `blockSize`
385
+ | `bottom`
386
+ | `columnGap`
387
+ | `flexBasis`
388
+ | `gap`
389
+ | `height`
390
+ | `inlineSize`
391
+ | `inset${string}`
392
+ | `left`
393
+ | Exclude<
394
+ Extract<keyof Literal.Properties, `margin${string}`>,
395
+ 'marginTrim'
396
+ >
397
+ | `maxBlockSize`
398
+ | `maxHeight`
399
+ | `maxInlineSize`
400
+ | `maxWidth`
401
+ | `minBlockSize`
402
+ | `minHeight`
403
+ | `minInlineSize`
404
+ | `minWidth`
405
+ | `padding${string}`
406
+ | `right`
407
+ | `rowGap`
408
+ | `scrollPadding${string}`
409
+ | `textDecorationThickness`
410
+ | `textIndent`
411
+ | `textUnderlineOffset`
412
+ | `top`
413
+ | `width`
414
+ >
415
+ : group extends 'textColor'
416
+ ? 'color'
417
+ : group extends 'color'
418
+ ? {
419
+ [property in keyof typeof Literal.rules]: (typeof Literal.rules)[property] extends {
420
+ kind: 'color'
421
+ }
422
+ ? property
423
+ : never
424
+ }[keyof typeof Literal.rules]
425
+ : group extends 'borderColor'
426
+ ? Extract<keyof Literal.Properties, `border${string}Color`>
427
+ : group extends 'borderWidth'
428
+ ? Exclude<
429
+ Extract<keyof Literal.Properties, `border${string}Width`>,
430
+ 'borderImageWidth'
431
+ >
432
+ : group extends 'borderRadius'
433
+ ? Extract<keyof Literal.Properties, `border${string}Radius`>
434
+ : group
312
435
 
313
436
  /** Immutable portable reference retaining its defining fallback. */
314
437
  export type Reference<group extends Group = Group> = {
@@ -333,11 +456,11 @@ export function resolve(value: unknown, options: resolve.Options): unknown {
333
456
  ?.value as Metadata | undefined
334
457
  if (!data) throw new Error('Expected a theme definition.')
335
458
 
336
- // Specific groups precede shared colors regardless of authored group order.
337
- for (const group of [
459
+ const groups = [
338
460
  'backgroundColor',
339
461
  'borderColor',
340
462
  'borderRadius',
463
+ 'borderWidth',
341
464
  'margin',
342
465
  'padding',
343
466
  'spacing',
@@ -348,7 +471,49 @@ export function resolve(value: unknown, options: resolve.Options): unknown {
348
471
  'lineHeight',
349
472
  'textColor',
350
473
  'color',
351
- ] as const) {
474
+ ] as const
475
+
476
+ if (data.contract.variableSet) {
477
+ for (const [path, entry] of Object.entries(data.values).sort(
478
+ ([left], [right]) =>
479
+ groups.indexOf(left.split('.')[0] as (typeof groups)[number]) -
480
+ groups.indexOf(right.split('.')[0] as (typeof groups)[number]),
481
+ )) {
482
+ const [category, ...parts] = path.split('.')
483
+ const mappings = data.contract.mappings
484
+ const mapped = mappings === false ? undefined : mappings?.[category!]
485
+ if (
486
+ mappings === false
487
+ ? path !== String(value)
488
+ : parts.join('.') !== String(value) ||
489
+ !(mapped
490
+ ? mapped.includes(options.property)
491
+ : accepts(category as Group, options.property))
492
+ )
493
+ continue
494
+ return create({
495
+ contract: data.contract,
496
+ group: (() => {
497
+ let leaf: unknown = Object.getOwnPropertyDescriptor(
498
+ options.theme,
499
+ 'tokens',
500
+ )?.value
501
+ for (const key of path.split('.'))
502
+ leaf =
503
+ leaf && typeof leaf === 'object'
504
+ ? Object.getOwnPropertyDescriptor(leaf, key)?.value
505
+ : undefined
506
+ return is(leaf) ? leaf.group : (category as Group)
507
+ })(),
508
+ path,
509
+ value: entry,
510
+ })
511
+ }
512
+ return value
513
+ }
514
+
515
+ // Specific groups precede shared colors regardless of authored group order.
516
+ for (const group of groups) {
352
517
  if (!accepts(group, options.property)) continue
353
518
 
354
519
  const path = `${group}.${value}`
@@ -377,4 +542,12 @@ export declare namespace resolve {
377
542
  export type Value =
378
543
  | number
379
544
  | string
380
- | { readonly dark: string; readonly light: string }
545
+ | Reference
546
+ | { readonly dark: Value; readonly light: Value }
547
+ | Conditions
548
+
549
+ /** Ordered conditional values with an unconditional fallback. */
550
+ export type Conditions = {
551
+ readonly default: Value
552
+ readonly [query: `@media ${string}`]: Value
553
+ }