@ui0/theme 0.0.0-stage → 0.1.0

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 (148) hide show
  1. package/README.md +113 -2
  2. package/dist/backdrop.module.css +1 -0
  3. package/dist/backdrop.scss +7 -0
  4. package/dist/color.inject.module.css +7 -0
  5. package/dist/color.module.css +3 -0
  6. package/dist/color.scss +84 -0
  7. package/dist/components/index.d.ts +4 -0
  8. package/dist/components/index.d.ts.map +1 -0
  9. package/dist/components/index.js +53 -0
  10. package/dist/components/style.css +1 -0
  11. package/dist/components/theme-provider.keys.d.ts +4 -0
  12. package/dist/components/theme-provider.keys.d.ts.map +1 -0
  13. package/dist/components/theme-provider.module.css +1 -0
  14. package/dist/components/theme-provider.types.d.ts +18 -0
  15. package/dist/components/theme-provider.types.d.ts.map +1 -0
  16. package/dist/components/theme-provider.vue.d.ts +19 -0
  17. package/dist/config.default.d.mts +4 -0
  18. package/dist/config.default.d.mts.map +1 -0
  19. package/dist/config.default.mjs +73 -0
  20. package/dist/config.default.mjs.map +1 -0
  21. package/dist/disabled.inject.module.css +5 -0
  22. package/dist/disabled.module.css +1 -0
  23. package/dist/disabled.scss +3 -0
  24. package/dist/display.module.css +2 -0
  25. package/dist/display.scss +63 -0
  26. package/dist/duration.scss +17 -0
  27. package/dist/elevation.module.css +33 -0
  28. package/dist/elevation.scss +46 -0
  29. package/dist/generate-BPYE5ALG.mjs +1037 -0
  30. package/dist/generate-BPYE5ALG.mjs.map +1 -0
  31. package/dist/generator/cli.d.mts +1 -0
  32. package/dist/generator/cli.mjs +103 -0
  33. package/dist/generator/cli.mjs.map +1 -0
  34. package/dist/generator/vite.d.mts +18 -0
  35. package/dist/generator/vite.d.mts.map +1 -0
  36. package/dist/generator/vite.mjs +117 -0
  37. package/dist/generator/vite.mjs.map +1 -0
  38. package/dist/index.d.mts +339 -0
  39. package/dist/index.d.mts.map +1 -0
  40. package/dist/index.mjs +909 -0
  41. package/dist/index.mjs.map +1 -0
  42. package/dist/is.scss +185 -0
  43. package/dist/load-config-64q6nzFy.mjs +48 -0
  44. package/dist/load-config-64q6nzFy.mjs.map +1 -0
  45. package/dist/ripple.scss +57 -0
  46. package/dist/round.inject.module.css +92 -0
  47. package/dist/round.module.css +14 -0
  48. package/dist/round.scss +90 -0
  49. package/dist/size.inject.module.css +50 -0
  50. package/dist/size.module.css +13 -0
  51. package/dist/size.scss +28 -0
  52. package/dist/theme.scss +16 -0
  53. package/dist/typography.inject.module.css +81 -0
  54. package/dist/typography.module.css +32 -0
  55. package/dist/typography.scss +21 -0
  56. package/dist/util.scss +78 -0
  57. package/dist/vars.scss +0 -0
  58. package/package.json +96 -4
  59. package/src/contracts/backdrop/backdrop.contract.ts +44 -0
  60. package/src/contracts/backdrop/backdrop.generator.ts +82 -0
  61. package/src/contracts/backdrop/backdrop.keys.ts +7 -0
  62. package/src/contracts/backdrop/backdrop.module.css +3 -0
  63. package/src/contracts/backdrop/backdrop.scss +7 -0
  64. package/src/contracts/backdrop/backdrop.types.ts +45 -0
  65. package/src/contracts/backdrop/index.ts +21 -0
  66. package/src/contracts/color/color.contract.spec.ts +75 -0
  67. package/src/contracts/color/color.contract.ts +97 -0
  68. package/src/contracts/color/color.generator.spec.ts +73 -0
  69. package/src/contracts/color/color.generator.ts +122 -0
  70. package/src/contracts/color/color.gradient.spec.ts +32 -0
  71. package/src/contracts/color/color.gradient.ts +52 -0
  72. package/src/contracts/color/color.inject.module.css +5 -0
  73. package/src/contracts/color/color.keys.ts +7 -0
  74. package/src/contracts/color/color.module.css +5 -0
  75. package/src/contracts/color/color.scss +84 -0
  76. package/src/contracts/color/color.serialize.spec.ts +57 -0
  77. package/src/contracts/color/color.serialize.ts +35 -0
  78. package/src/contracts/color/color.token.spec.ts +57 -0
  79. package/src/contracts/color/color.token.ts +89 -0
  80. package/src/contracts/color/color.types.ts +82 -0
  81. package/src/contracts/color/index.md +87 -0
  82. package/src/contracts/color/index.ts +25 -0
  83. package/src/contracts/contract.ts +115 -0
  84. package/src/contracts/disabled/disabled.contract.ts +32 -0
  85. package/src/contracts/disabled/disabled.generator.ts +32 -0
  86. package/src/contracts/disabled/disabled.inject.module.css +5 -0
  87. package/src/contracts/disabled/disabled.keys.ts +6 -0
  88. package/src/contracts/disabled/disabled.module.css +3 -0
  89. package/src/contracts/disabled/disabled.scss +3 -0
  90. package/src/contracts/disabled/disabled.types.ts +19 -0
  91. package/src/contracts/disabled/index.ts +21 -0
  92. package/src/contracts/display/display.contract.ts +41 -0
  93. package/src/contracts/display/display.generator.ts +57 -0
  94. package/src/contracts/display/display.instance.ts +173 -0
  95. package/src/contracts/display/display.keys.ts +9 -0
  96. package/src/contracts/display/display.module.css +4 -0
  97. package/src/contracts/display/display.scss +63 -0
  98. package/src/contracts/display/display.types.ts +75 -0
  99. package/src/contracts/display/index.ts +27 -0
  100. package/src/contracts/elevation/elevation.contract.ts +37 -0
  101. package/src/contracts/elevation/elevation.generator.ts +71 -0
  102. package/src/contracts/elevation/elevation.keys.ts +6 -0
  103. package/src/contracts/elevation/elevation.module.css +35 -0
  104. package/src/contracts/elevation/elevation.scss +46 -0
  105. package/src/contracts/elevation/elevation.types.ts +21 -0
  106. package/src/contracts/elevation/index.ts +20 -0
  107. package/src/contracts/generator.ts +38 -0
  108. package/src/contracts/index.md +48 -0
  109. package/src/contracts/index.ts +37 -0
  110. package/src/contracts/round/index.ts +21 -0
  111. package/src/contracts/round/round.contract.ts +75 -0
  112. package/src/contracts/round/round.generator.ts +33 -0
  113. package/src/contracts/round/round.inject.module.scss +36 -0
  114. package/src/contracts/round/round.keys.ts +7 -0
  115. package/src/contracts/round/round.module.css +16 -0
  116. package/src/contracts/round/round.scss +90 -0
  117. package/src/contracts/round/round.types.ts +44 -0
  118. package/src/contracts/size/index.ts +22 -0
  119. package/src/contracts/size/size.contract.ts +98 -0
  120. package/src/contracts/size/size.generator.spec.ts +101 -0
  121. package/src/contracts/size/size.generator.ts +51 -0
  122. package/src/contracts/size/size.inject.module.scss +12 -0
  123. package/src/contracts/size/size.keys.ts +8 -0
  124. package/src/contracts/size/size.module.css +15 -0
  125. package/src/contracts/size/size.scss +28 -0
  126. package/src/contracts/size/size.types.ts +63 -0
  127. package/src/contracts/size/units.scss +17 -0
  128. package/src/contracts/style/index.ts +11 -0
  129. package/src/contracts/style/style.generator.ts +36 -0
  130. package/src/contracts/style/style.key.ts +4 -0
  131. package/src/contracts/style/style.types.ts +16 -0
  132. package/src/contracts/typography/index.ts +26 -0
  133. package/src/contracts/typography/typography.contract.ts +52 -0
  134. package/src/contracts/typography/typography.generator.spec.ts +48 -0
  135. package/src/contracts/typography/typography.generator.ts +44 -0
  136. package/src/contracts/typography/typography.inject.module.css +54 -0
  137. package/src/contracts/typography/typography.keys.ts +12 -0
  138. package/src/contracts/typography/typography.module.css +36 -0
  139. package/src/contracts/typography/typography.scss +21 -0
  140. package/src/contracts/typography/typography.types.ts +90 -0
  141. package/src/generator/theme.default.css +657 -0
  142. package/src/styles/duration.scss +17 -0
  143. package/src/styles/index.md +18 -0
  144. package/src/styles/is.scss +185 -0
  145. package/src/styles/ripple.scss +57 -0
  146. package/src/styles/theme.scss +16 -0
  147. package/src/styles/util.scss +78 -0
  148. package/src/styles/vars.scss +0 -0
@@ -0,0 +1,21 @@
1
+ import type { ThemeContract } from '@ui0/types'
2
+
3
+ import { ThemeContractFlags } from '../../constants'
4
+ import { disabledDeal } from './disabled.contract'
5
+ import { DisabledContractKey, DisabledRefKey } from './disabled.keys'
6
+ import type { DisabledProps } from './disabled.types'
7
+
8
+ export { default as $style } from './disabled.module.css'
9
+ export type * from './disabled.types'
10
+ export * from './disabled.keys'
11
+
12
+ export const DisabledPropsKeys: ReadonlyArray<keyof DisabledProps> = ['disabled']
13
+
14
+ export const disabled: ThemeContract = {
15
+ key: DisabledContractKey,
16
+ name: 'disabled',
17
+ ref: DisabledRefKey,
18
+ flag: ThemeContractFlags.dealer + ThemeContractFlags.generator,
19
+ deal: disabledDeal,
20
+ props: DisabledPropsKeys
21
+ }
@@ -0,0 +1,41 @@
1
+ // The display contract's processing endpoint: closes over the provider and
2
+ // returns the deal that maps the `display` prop onto classes/styles at
3
+ // runtime, resolving `inherit` against the provided context ref.
4
+ import type { ClassList, CSSModuleClassList } from '@ui0/types'
5
+ import type { ContractProvider, ThemeContractDeal } from '@ui0/types'
6
+ import { mergeCssModulesStyles } from '@ui0/util'
7
+ import type { Ref } from 'vue'
8
+
9
+ import { EMPTY_OBJ } from '../../constants'
10
+ import type { DisplayProps } from './display.types'
11
+
12
+ import $display from './display.module.css'
13
+
14
+ // the prop -> class wiring: resolves the prop value to the hashed class from
15
+ // the module map (merged with the component's own $style)
16
+ function displayClasses(props: DisplayProps, styles?: CSSModuleClassList): ClassList {
17
+ const $ = mergeCssModulesStyles($display, styles)
18
+ const out: ClassList = []
19
+ if (props.display && $.displayed) out.push($.displayed)
20
+ return out
21
+ }
22
+
23
+ export function displayDeal(instance: ContractProvider): ThemeContractDeal {
24
+ const props = instance.props
25
+
26
+ return {
27
+ classes: () => {
28
+ if (!props.display) return instance.inject([])
29
+
30
+ if (props.display === 'inherit') {
31
+ const ctx = instance.context as Record<string, Ref<any> | undefined> | null
32
+ if (ctx?.display) {
33
+ return instance.inject(displayClasses({ display: ctx.display.value }, instance.$styles))
34
+ }
35
+ }
36
+
37
+ return instance.inject(displayClasses(props as DisplayProps, instance.$styles))
38
+ },
39
+ styles: () => instance.inject(EMPTY_OBJ)
40
+ }
41
+ }
@@ -0,0 +1,57 @@
1
+ // The display contract's generator endpoint: turns the contract's slice of
2
+ // the appearance config (DisplayThemeAppearanceConfig) into css vars and
3
+ // setter classes. Values are authored in the theme config; this generator
4
+ // only knows the wiring.
5
+ import type { CSSModuleClassList, StyleList, VarList } from '@ui0/types'
6
+ import type { ThemeContractGenerator } from '@ui0/types'
7
+ import { toUnits } from '@ui0/util'
8
+
9
+ import type { ContractContext, ContractFactory } from '../generator'
10
+ import type { DisplayThemeAppearanceConfig } from './display.types'
11
+
12
+ // the logical class names shipped in display.module.css - class() re-exports
13
+ // the registry map, so the logical names resolve to the distiller hashes.
14
+ import $display from './display.module.css'
15
+
16
+ const displayGenerator: ContractFactory = (ctx: ContractContext): ThemeContractGenerator => {
17
+ const { config } = ctx
18
+
19
+ function vars(): Record<string, VarList> {
20
+ // after registration (see display.types.ts) this reads
21
+ // `config.display`; typed loosely until the contract is named.
22
+ const display = (config as { display?: DisplayThemeAppearanceConfig }).display
23
+ if (display?.base === undefined) return {}
24
+ return { display: { base: display.base } }
25
+ }
26
+
27
+ return {
28
+ namespace: 'd',
29
+ vars: (scheme) => (scheme ? {} : vars()),
30
+ css: () => `.${$display.displayed} { /* point the component slot at the theme var */ }`,
31
+ localScss: () => {
32
+ const brp = ctx.settings.display?.breakpoints
33
+ const out: StyleList = {}
34
+
35
+ if (brp) {
36
+ Object.keys(brp).forEach((k) => {
37
+ out[`$${k}`] = toUnits(brp[k])
38
+ })
39
+ }
40
+ return out
41
+ },
42
+ class: (): CSSModuleClassList => ({ ...$display }),
43
+ styles: () => {
44
+ const brp = ctx.settings.display?.breakpoints
45
+ const out: StyleList = {}
46
+
47
+ if (brp) {
48
+ Object.keys(brp).forEach((k) => {
49
+ out[`--0-display-${k}`] = toUnits(brp[k])
50
+ })
51
+ }
52
+ return out
53
+ }
54
+ }
55
+ }
56
+
57
+ export default displayGenerator
@@ -0,0 +1,173 @@
1
+ import type { Ui0ThemeContractSettings, EventHandler } from '@ui0/types'
2
+ import {
3
+ getClientHeight,
4
+ getClientWidth,
5
+ isBrowser,
6
+ objectMerge,
7
+ parseUnits,
8
+ RecordContainer,
9
+ type RecordContainerImpl
10
+ } from '@ui0/util'
11
+ import { ref, type Ref, type WatchHandle } from 'vue'
12
+
13
+ import cfg from '../../config.default'
14
+ import type {
15
+ DisplayBreakpoint,
16
+ DisplayBreakpoints,
17
+ DisplayImpl,
18
+ DisplayMeasure,
19
+ DisplayOperation,
20
+ DisplayWatcher
21
+ } from './display.types'
22
+
23
+ export class Display implements DisplayImpl {
24
+ breakpoints: DisplayBreakpoints
25
+ _watchers: DisplayWatcher[]
26
+ _watcher: WatchHandle | null
27
+ _activated: boolean
28
+ _listeners: RecordContainerImpl<any>
29
+ _width: Ref<number> | null
30
+ _height: Ref<number> | null
31
+ _resizeHandler: EventHandler
32
+
33
+ constructor(config: Ui0ThemeContractSettings['display']) {
34
+ this.breakpoints = objectMerge(cfg.contracts.display?.breakpoints, config?.breakpoints ?? {})
35
+
36
+ this._watchers = []
37
+ this._watcher = null
38
+ this._width = null
39
+ this._height = null
40
+ this._activated = false
41
+ this._resizeHandler = () => this.update()
42
+ this._listeners = new RecordContainer()
43
+ }
44
+
45
+ public update() {
46
+ if (this._width && this._height) {
47
+ this._width.value = getClientWidth()
48
+ this._height.value = getClientHeight()
49
+ }
50
+ let wasChanged = false
51
+
52
+ this._watchers.forEach((watcher) => {
53
+ const measure = watcher.measure === 'width' ? this._width : this._height
54
+ const oldValue = watcher.value.value
55
+
56
+ if (watcher.operation === 'between') {
57
+ const breakpoint1 = this.getBreakpointValue(String(watcher.points[0]))
58
+ const breakpoint2 = this.getBreakpointValue(String(watcher.points[1]))
59
+
60
+ if (measure && breakpoint1 && breakpoint2) {
61
+ const b1 = parseUnits(String(breakpoint1)).value
62
+ const b2 = parseUnits(String(breakpoint2)).value
63
+ // console.log('we', b1, b2)
64
+ if (b1 > b2) watcher.value.value = measure.value > b2 && measure.value < b1
65
+ if (b1 < b2) watcher.value.value = measure.value > b1 && measure.value < b2
66
+
67
+ if (watcher.value.value !== oldValue) {
68
+ wasChanged = true
69
+ }
70
+ }
71
+ }
72
+ if (watcher.operation === 'less' || watcher.operation === 'greater') {
73
+ const breakpoint1 = this.getBreakpointValue(String(watcher.points[0]))
74
+ if (breakpoint1 && measure) {
75
+ const b1 = parseUnits(String(breakpoint1)).value
76
+ watcher.value.value = watcher.operation === 'less' ? measure.value < b1 : measure.value > b1
77
+
78
+ if (watcher.value.value !== oldValue) {
79
+ wasChanged = true
80
+ }
81
+ }
82
+ }
83
+ })
84
+
85
+ if (wasChanged) {
86
+ this._listeners.items.forEach((item) => item())
87
+ }
88
+ }
89
+
90
+ public addUpdateListener(fn: AnyFn): number {
91
+ return this._listeners.add(fn)
92
+ }
93
+
94
+ public removeUpdateListener(index: number) {
95
+ this._listeners.remove(index)
96
+ }
97
+
98
+ public less(point: DisplayBreakpoint, measure: DisplayMeasure = 'width') {
99
+ const out = this.addWatcher('less', [point], measure)
100
+ this.activate()
101
+ return out
102
+ }
103
+
104
+ public between(point1: DisplayBreakpoint, point2: DisplayBreakpoint, measure: DisplayMeasure = 'width') {
105
+ const out = this.addWatcher('between', [point1, point2], measure)
106
+ this.activate()
107
+ this.update()
108
+ return out
109
+ }
110
+
111
+ public greater(point: DisplayBreakpoint, measure: DisplayMeasure = 'width') {
112
+ const out = this.addWatcher('greater', [point], measure)
113
+ this.activate()
114
+ return out
115
+ }
116
+
117
+ public activate() {
118
+ // if (this._activated) return
119
+
120
+ if (!this._width) this._width = ref(0)
121
+ if (!this._height) this._height = ref(0)
122
+
123
+ if (isBrowser && !this._activated) window.addEventListener('resize', this._resizeHandler, { passive: true })
124
+
125
+ this.update()
126
+ this._activated = true
127
+ }
128
+
129
+ public deactivate() {
130
+ this._activated = false
131
+ if (isBrowser) window.removeEventListener('resize', this._resizeHandler)
132
+ }
133
+
134
+ private getBreakpointValue(name: string): number {
135
+ // console.log('breaks', Object.keys(_breakpoints).length)
136
+ return +this.breakpoints[name]
137
+ }
138
+
139
+ private addWatcher(
140
+ operation: DisplayOperation,
141
+ points: DisplayBreakpoint[],
142
+ measure: DisplayMeasure = 'width'
143
+ ): Ref<boolean> {
144
+ let outRef: Ref<boolean> | undefined = undefined
145
+
146
+ this._watchers.forEach((watcher) => {
147
+ const isMultiValued = watcher.points.length > 1
148
+ if (watcher.operation === operation) {
149
+ if (isMultiValued) {
150
+ if (watcher.points[0] === points[0] && watcher.points[1] === points[1]) {
151
+ console.log('setting existing ', watcher.value)
152
+ outRef = watcher.value
153
+ }
154
+ } else {
155
+ if (watcher.points[0] === points[0]) {
156
+ outRef = watcher.value
157
+ }
158
+ }
159
+ }
160
+ })
161
+ if (!outRef) {
162
+ outRef = ref(false)
163
+ this._watchers.push({
164
+ measure,
165
+ points,
166
+ operation,
167
+ value: outRef
168
+ })
169
+ }
170
+
171
+ return outRef
172
+ }
173
+ }
@@ -0,0 +1,9 @@
1
+ import type { ThemeContract } from '@ui0/types'
2
+ import type { InjectionKey, Ref } from 'vue'
3
+
4
+ import type { DisplayImpl, DisplayProp, DisplayReturn } from './display.types'
5
+
6
+ /** context key the deal resolves `inherit` against (see useThemeContract) */
7
+ export const DisplayRefKey = Symbol('DisplayRefKey') as InjectionKey<Ref<DisplayProp | undefined>>
8
+ export const DisplayContractKey = Symbol('DisplayContractKey') as InjectionKey<ThemeContract>
9
+ export const DisplayKey = Symbol('DisplayContractKey') as InjectionKey<DisplayImpl>
@@ -0,0 +1,4 @@
1
+ .displayed,
2
+ .displayNormal {
3
+ /*@*/
4
+ }
@@ -0,0 +1,63 @@
1
+
2
+ // Component-namespace wiring for the display contract: the generator emits
3
+ // `--0-display-base` on the theme blocks; this mixin mirrors it into the
4
+ // component namespace so a component can consume (and locally override) it.
5
+ @mixin use() {
6
+ --0c-display: var(--0-display-base, initial);
7
+ }
8
+
9
+ @mixin wh($width, $height) {
10
+ $max-w: ' and (max-width: #{$width})';
11
+ $max-h: ' and (max-height: #{$height})';
12
+ @media only screen#{$max-h}#{$max-w} {
13
+ @content;
14
+ }
15
+ }
16
+ @mixin w-between($max, $min) {
17
+ $min-w: ' and (min-width: #{$min})';
18
+ $max-w: ' and (max-width: #{$max})';
19
+ @if $max == 0 and $min == 0 {
20
+ } @else {
21
+ @if $max == 0 {
22
+ $max-w: ' ';
23
+ }
24
+ @media only screen#{$min-w}#{$max-w} {
25
+ @content;
26
+ }
27
+ }
28
+ }
29
+
30
+ @mixin w-greater($val) {
31
+ @include w-between(0, $val) {
32
+ @content;
33
+ }
34
+ }
35
+
36
+ @mixin w-less($val) {
37
+ @include w-between($val, 0) {
38
+ @content;
39
+ }
40
+ }
41
+ @mixin h-between($max, $min) {
42
+ $minh: ' and (min-height: #{$min})';
43
+ $maxh: ' and (max-height: #{$max})';
44
+ @if $max == 0 and $min == 0 {
45
+ } @else {
46
+ @if $max == 0 {
47
+ $maxh: ' ';
48
+ }
49
+ @media only screen#{$minh}#{$maxh} {
50
+ @content;
51
+ }
52
+ }
53
+ }
54
+ @mixin h-greater($val) {
55
+ @include h-between(0, $val) {
56
+ @content;
57
+ }
58
+ }
59
+ @mixin h-less($val) {
60
+ @include h-between($val, 0) {
61
+ @content;
62
+ }
63
+ }
@@ -0,0 +1,75 @@
1
+ // ---------------------------------------------------------------------------
2
+ // CONTRACT TEMPLATE - a skeleton for new theme contracts, consumed by
3
+ // scripts/gen-contract.ts in the templates package:
4
+ //
5
+ // pnpm contract:gen
6
+ //
7
+ // The generator copies this folder into <package>/src/contracts/<name>/
8
+ // (the theme package ships the contracts folder), substituting display /
9
+ // Display / display / d in file bodies and names. After generation:
10
+ //
11
+ // 1. add the contract to src/contracts/index.ts (registry)
12
+ // 2. add <kebab>Generator to src/generator.ts (contractGenerators)
13
+ // 3. fill in the prop values / appearance config below
14
+ //
15
+ // The package itself stays unaware of the contract - types land in the
16
+ // registries via interface merging only.
17
+ // ---------------------------------------------------------------------------
18
+ import type { LiteralUnion, MaybeUnits, Numberish, UpdateFn } from '@ui0/types'
19
+ import type { Ref } from 'vue'
20
+
21
+ // 1. the values the prop accepts
22
+ export type DisplayProp = LiteralUnion<'some' | 'display' | 'values'>
23
+ export type DisplayOperation = 'greater' | 'less' | 'equal' | 'between'
24
+
25
+ export type DisplayMeasure = 'width' | 'height'
26
+
27
+ export type DisplayBreakpoint = string | 'xxs' | 's' | 'xs' | 'xxl' | 'lg' | 'xl' | 'hd' | 'fhd' | 'qhd' | 'uhd'
28
+ export type DisplayBreakpoints = Record<DisplayBreakpoint, Numberish>
29
+
30
+ // 2. the props this contract supplies to components
31
+ export interface DisplayProps {
32
+ // display?: DisplayProp
33
+ }
34
+
35
+ // 3. the contract's settings inside a theme preset (appearance config) -
36
+ // what the generator turns into css vars
37
+ export interface DisplayThemeAppearanceConfig {
38
+ /** e.g. a base value the generator emits as a css var */
39
+ base?: MaybeUnits
40
+ }
41
+
42
+ // 4. registration: announces this contract to the package registries.
43
+ declare module '@ui0/types/theme' {
44
+ interface Ui0ThemePresetAppearanceConfig {
45
+ display?: DisplayThemeAppearanceConfig
46
+ }
47
+ interface Ui0ThemeContractSettings {
48
+ display?: {
49
+ breakpoints: DisplayBreakpoints
50
+ }
51
+ }
52
+ interface Ui0ThemeContracts extends DisplayProps {}
53
+ }
54
+
55
+ export interface DisplayWatcher {
56
+ points: Array<any>
57
+ measure: DisplayMeasure
58
+ operation: DisplayOperation
59
+ value: Ref<boolean>
60
+ }
61
+
62
+ export interface DisplayReturn {
63
+ breakpoints: DisplayBreakpoints
64
+ between: (point: DisplayBreakpoint, b: DisplayBreakpoint, measure?: DisplayMeasure) => Ref<boolean>
65
+ greater: (point: DisplayBreakpoint, measure?: DisplayMeasure) => Ref<boolean>
66
+ less: (point: DisplayBreakpoint, measure?: DisplayMeasure) => Ref<boolean>
67
+ }
68
+
69
+ export interface DisplayImpl extends DisplayReturn {
70
+ activate: () => void
71
+ deactivate: () => void
72
+ update: () => void
73
+ addUpdateListener: (fn: UpdateFn) => number
74
+ removeUpdateListener: (id: number) => void
75
+ }
@@ -0,0 +1,27 @@
1
+ import type { ThemeContract } from '@ui0/types'
2
+
3
+ // The display contract's registry entry, generated from
4
+ // packages/templates/contract. Wiring left (see the generator output):
5
+ // 1. add `display` to src/contracts/index.ts (next to round/size/...)
6
+ // 2. add displayGenerator to src/generator.ts (contractGenerators)
7
+ import { ThemeContractFlags } from '../../constants'
8
+ import { displayDeal } from './display.contract'
9
+ import { DisplayContractKey, DisplayRefKey } from './display.keys'
10
+ import type { DisplayProps } from './display.types'
11
+
12
+ export { default as $style } from './display.module.css'
13
+ export type * from './display.types'
14
+ export * from './display.keys'
15
+
16
+ export const DisplayPropsKeys: ReadonlyArray<keyof DisplayProps> = [
17
+ // 'display'
18
+ ]
19
+
20
+ export const display: ThemeContract = {
21
+ key: DisplayContractKey,
22
+ name: 'display',
23
+ ref: DisplayRefKey,
24
+ deal: displayDeal,
25
+ flag: ThemeContractFlags.generator,
26
+ props: DisplayPropsKeys
27
+ }
@@ -0,0 +1,37 @@
1
+ // The elevation contract's processing endpoint: the `elevation` prop picks a
2
+ // step of the shadow scale; the deal resolves it to the hashed step class
3
+ // (`elv1..steps`, `n` suffix for inset), `inherit` reading the provided ref.
4
+ import type { ClassList, CSSModuleClassList } from '@ui0/types'
5
+ import type { ContractProvider, ThemeContractDeal } from '@ui0/types'
6
+ import { mergeCssModulesStyles } from '@ui0/util'
7
+
8
+ import { EMPTY_OBJ } from '../../constants'
9
+ import type { ElevationProps } from './elevation.types'
10
+
11
+ import $elevation from './elevation.module.css'
12
+
13
+ function elevationClasses(p: ElevationProps, s?: CSSModuleClassList): ClassList {
14
+ const { elevation } = p
15
+ if (elevation === undefined) return []
16
+ const $ = mergeCssModulesStyles($elevation, s)
17
+
18
+ const value = Number(elevation)
19
+
20
+ return Number.isNaN(value) ? [] : [$['elv' + Math.abs(value) + (value < 0 ? 'n' : '')], $.elv]
21
+ }
22
+
23
+ export function elevationDeal(instance: ContractProvider): ThemeContractDeal {
24
+ const props = instance.props
25
+
26
+ return {
27
+ classes: () => {
28
+ if (instance.context) {
29
+ if (props.elevation === 'inherit' && instance.context.elevation) {
30
+ return instance.inject(elevationClasses({ elevation: instance.context.elevation.value }, instance.$styles))
31
+ }
32
+ }
33
+ return instance.inject(elevationClasses(props, instance.$styles))
34
+ },
35
+ styles: () => instance.inject(EMPTY_OBJ)
36
+ }
37
+ }
@@ -0,0 +1,71 @@
1
+ // The elevation contract's generator endpoint: ONE .elv base class carries the
2
+ // box-shadow (built from css vars), the per-step classes (.elv1..steps /
3
+ // .elv1n..stepsn for inset) only fill in the var values for their step - no
4
+ // shadow math is repeated per class. Shadow tuning (color, steps) lands in
5
+ // the config with the elevation appearance settings.
6
+ import type { CSSModuleClassList } from '@ui0/types'
7
+ import type { ThemeContractGenerator } from '@ui0/types'
8
+
9
+ import type { ContractFactory } from '../generator'
10
+ import type { ElevationThemeAppearanceConfig } from './elevation.types'
11
+
12
+ import $elevation from './elevation.module.css'
13
+
14
+ // the shared shadow: a fixed highlight ring + three layers assembled from the
15
+ // --0-elv-* vars (the step classes fill those in; the base class shadows with
16
+ // zero/fallback values until one is applied)
17
+ const BASE_CLASS_BODY = ` box-shadow:
18
+ 0 0 0 1px rgba(255 255 255 / .1),
19
+ var(--0-elv-i, ) 0px 1px 1px rgba(var(--0-elv-c, 0 0 0) / 0.1),
20
+ var(--0-elv-i, ) var(--0-elv-x1, 0px) var(--0-elv-y1, 0px) rgba(var(--0-elv-c, 0 0 0) / 0.095),
21
+ var(--0-elv-i, ) var(--0-elv-x2, 0px) var(--0-elv-y2, 0px) rgba(var(--0-elv-c, 0 0 0) / 0.13);`
22
+
23
+ const elevationGenerator: ContractFactory = (ctx): ThemeContractGenerator => {
24
+ const { config, hash } = ctx
25
+ const steps = (config as { elevation?: ElevationThemeAppearanceConfig }).elevation?.steps ?? 16
26
+ function vars() {
27
+ return {
28
+ elv: {
29
+ x1: '1px',
30
+ y1: 'calc(var(--0-elv-x1) * 3)',
31
+ x2: 'calc(var(--0-elv-x1) * 1.5)',
32
+ y2: 'calc(var(--0-elv-x1) * 1.5)'
33
+ }
34
+ }
35
+ }
36
+ function stepVars(i: number, inset: boolean): string {
37
+ const lines = [` --0-elv-x1: ${i}px;`]
38
+ if (inset) lines.push(' --0-elv-i: inset;')
39
+ return lines.join('\n')
40
+ }
41
+
42
+ function css(): string {
43
+ const lines: string[] = [`.${$elevation.elv} {\n${BASE_CLASS_BODY}\n}`]
44
+ for (let i = 1; i <= steps; i++) {
45
+ lines.push(`.${hash(`elv${i}`)} {\n${stepVars(i, false)}\n}`)
46
+ lines.push(`.${hash(`elv${i}n`)} {\n${stepVars(i, true)}\n}`)
47
+ }
48
+ return lines.join('\n')
49
+ }
50
+
51
+ function classes(): CSSModuleClassList {
52
+ // the base class + the default 16 steps come from the registry map; a
53
+ // config that extends the step count generates the extras dynamically
54
+ const out: CSSModuleClassList = { ...$elevation }
55
+ for (let i = 1; i <= steps; i++) {
56
+ out[`elv${i}`] = hash(`elv${i}`)
57
+ out[`elv${i}n`] = hash(`elv${i}n`)
58
+ }
59
+ return out
60
+ }
61
+
62
+ return {
63
+ namespace: 'e',
64
+ vars,
65
+ css,
66
+ class: classes,
67
+ styles: () => ({})
68
+ }
69
+ }
70
+
71
+ export default elevationGenerator
@@ -0,0 +1,6 @@
1
+ import type { ComponentElevation } from '@ui0/types'
2
+ import type { ThemeContractDeal } from '@ui0/types'
3
+ import type { InjectionKey, Ref } from 'vue'
4
+
5
+ export const ElevationRefKey = Symbol('ElevationRefKey') as InjectionKey<Ref<ComponentElevation>>
6
+ export const ElevationContractKey = Symbol('ElevationContractKey') as InjectionKey<ThemeContractDeal>
@@ -0,0 +1,35 @@
1
+ .elv,
2
+ .elv1,
3
+ .elv1n,
4
+ .elv2,
5
+ .elv2n,
6
+ .elv3,
7
+ .elv3n,
8
+ .elv4,
9
+ .elv4n,
10
+ .elv5,
11
+ .elv5n,
12
+ .elv6,
13
+ .elv6n,
14
+ .elv7,
15
+ .elv7n,
16
+ .elv8,
17
+ .elv8n,
18
+ .elv9,
19
+ .elv9n,
20
+ .elv10,
21
+ .elv10n,
22
+ .elv11,
23
+ .elv11n,
24
+ .elv12,
25
+ .elv12n,
26
+ .elv13,
27
+ .elv13n,
28
+ .elv14,
29
+ .elv14n,
30
+ .elv15,
31
+ .elv15n,
32
+ .elv16,
33
+ .elv16n {
34
+ /*@*/
35
+ }
@@ -0,0 +1,46 @@
1
+ // Elevation: ONE base class carries the box-shadow assembled from the
2
+ // --0-elv-* css vars; the per-step classes (.elv1..$steps, .elv1n..stepsn for
3
+ // inset) only fill in the var values for their step - no shadow math is
4
+ // repeated per class. This file mirrors what elevation.generator.ts emits
5
+ // (the generator compiles this pattern with $steps from the theme config).
6
+
7
+ // the shared shadow: a fixed highlight ring + three layers assembled from the
8
+ // --0-elv-* vars (zero/fallback values until a step class is applied)
9
+ @mixin use() {
10
+ box-shadow:
11
+ 0 0 0 1px rgba(255 255 255 / 0.1),
12
+ var(--0-elv-i,) 0px 1px 1px rgba(var(--0-elv-c, 0 0 0) / 0.1),
13
+ var(--0-elv-i,) var(--0-elv-x1, 0px) var(--0-elv-y1, 0px) rgba(var(--0-elv-c, 0 0 0) / 0.095),
14
+ var(--0-elv-i,) var(--0-elv-x2, 0px) var(--0-elv-y2, 0px) rgba(var(--0-elv-c, 0 0 0) / 0.13);
15
+ }
16
+
17
+ @mixin elv($i, $inset: false) {
18
+ --0-elv-x1: #{$i}px;
19
+ --0-elv-y1: #{3 * $i}px;
20
+ --0-elv-x2: #{1.5 * $i}px;
21
+ --0-elv-y2: #{1.5 * $i}px;
22
+ @if $inset {
23
+ --0-elv-i: inset;
24
+ }
25
+ }
26
+
27
+ @mixin elevator($elevation, $color-value: #000) {
28
+ // legacy helper - kept for hand-written component styles; the generated
29
+ // classes use the var pattern above (@include use + @include elv)
30
+ $inset: 0;
31
+ @if $elevation < 0 {
32
+ $elevation: $elevation * -1;
33
+ $inset: inset 0;
34
+ } @else {
35
+ $inset: 0;
36
+ }
37
+ box-shadow:
38
+ 0 0 0 1px rgba(255, 255, 255, 0.1),
39
+ $inset 0px 1px 1px rgba($color-value, 0.1),
40
+ $inset #{1 * $elevation}px #{3 * $elevation}px rgba($color-value, 0.095),
41
+ $inset #{1.5 * $elevation}px #{1.5 * $elevation}px rgba($color-value, 0.13);
42
+ }
43
+
44
+ @function stroke() {
45
+ @return 0 0 0 1px rgba(255, 255, 255, 0.1);
46
+ }