@anvpx/motion-themes 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.
@@ -0,0 +1,412 @@
1
+ import type { ResolvedTheme } from "@anvpx/motion-schema";
2
+ declare const TOKEN_FILES: {
3
+ readonly default: {
4
+ $description: string;
5
+ color: {
6
+ $type: string;
7
+ bg: {
8
+ $value: string;
9
+ };
10
+ bgEnd: {
11
+ $value: string;
12
+ };
13
+ glow: {
14
+ $value: string;
15
+ };
16
+ glow2: {
17
+ $value: string;
18
+ };
19
+ surface: {
20
+ $value: string;
21
+ };
22
+ card: {
23
+ $value: string;
24
+ };
25
+ divider: {
26
+ $value: string;
27
+ };
28
+ accentSoft: {
29
+ $value: string;
30
+ };
31
+ fg: {
32
+ $value: string;
33
+ };
34
+ muted: {
35
+ $value: string;
36
+ };
37
+ accent: {
38
+ $value: string;
39
+ };
40
+ accent2: {
41
+ $value: string;
42
+ };
43
+ onAccent: {
44
+ $value: string;
45
+ };
46
+ onAccent2: {
47
+ $value: string;
48
+ };
49
+ deep: {
50
+ $value: string;
51
+ };
52
+ deepEnd: {
53
+ $value: string;
54
+ };
55
+ onDeep: {
56
+ $value: string;
57
+ };
58
+ onDeepMuted: {
59
+ $value: string;
60
+ };
61
+ };
62
+ chart: {
63
+ $type: string;
64
+ "1": {
65
+ $value: string;
66
+ };
67
+ "2": {
68
+ $value: string;
69
+ };
70
+ "3": {
71
+ $value: string;
72
+ };
73
+ "4": {
74
+ $value: string;
75
+ };
76
+ "5": {
77
+ $value: string;
78
+ };
79
+ "6": {
80
+ $value: string;
81
+ };
82
+ "7": {
83
+ $value: string;
84
+ };
85
+ "8": {
86
+ $value: string;
87
+ };
88
+ };
89
+ font: {
90
+ $type: string;
91
+ sans: {
92
+ $value: string[];
93
+ };
94
+ mono: {
95
+ $value: string[];
96
+ };
97
+ };
98
+ fontSize: {
99
+ $type: string;
100
+ sm: {
101
+ $value: string;
102
+ };
103
+ md: {
104
+ $value: string;
105
+ };
106
+ lg: {
107
+ $value: string;
108
+ };
109
+ xl: {
110
+ $value: string;
111
+ };
112
+ title: {
113
+ $value: string;
114
+ };
115
+ display: {
116
+ $value: string;
117
+ };
118
+ heroSm: {
119
+ $value: string;
120
+ };
121
+ heroMd: {
122
+ $value: string;
123
+ };
124
+ hero: {
125
+ $value: string;
126
+ };
127
+ };
128
+ spacing: {
129
+ $type: string;
130
+ xs: {
131
+ $value: string;
132
+ };
133
+ sm: {
134
+ $value: string;
135
+ };
136
+ md: {
137
+ $value: string;
138
+ };
139
+ lg: {
140
+ $value: string;
141
+ };
142
+ xl: {
143
+ $value: string;
144
+ };
145
+ xxl: {
146
+ $value: string;
147
+ };
148
+ };
149
+ radius: {
150
+ $type: string;
151
+ sm: {
152
+ $value: string;
153
+ };
154
+ md: {
155
+ $value: string;
156
+ };
157
+ lg: {
158
+ $value: string;
159
+ };
160
+ xl: {
161
+ $value: string;
162
+ };
163
+ full: {
164
+ $value: string;
165
+ };
166
+ };
167
+ opacity: {
168
+ $type: string;
169
+ subtle: {
170
+ $value: number;
171
+ };
172
+ medium: {
173
+ $value: number;
174
+ };
175
+ full: {
176
+ $value: number;
177
+ };
178
+ };
179
+ duration: {
180
+ $type: string;
181
+ fast: {
182
+ $value: string;
183
+ };
184
+ base: {
185
+ $value: string;
186
+ };
187
+ slow: {
188
+ $value: string;
189
+ };
190
+ };
191
+ $extensions: {
192
+ "motion-kit": {
193
+ textPairs: {
194
+ fg: string;
195
+ bg: string;
196
+ }[];
197
+ };
198
+ };
199
+ };
200
+ readonly dark: {
201
+ $description: string;
202
+ color: {
203
+ $type: string;
204
+ bg: {
205
+ $value: string;
206
+ };
207
+ bgEnd: {
208
+ $value: string;
209
+ };
210
+ glow: {
211
+ $value: string;
212
+ };
213
+ glow2: {
214
+ $value: string;
215
+ };
216
+ surface: {
217
+ $value: string;
218
+ };
219
+ card: {
220
+ $value: string;
221
+ };
222
+ divider: {
223
+ $value: string;
224
+ };
225
+ accentSoft: {
226
+ $value: string;
227
+ };
228
+ fg: {
229
+ $value: string;
230
+ };
231
+ muted: {
232
+ $value: string;
233
+ };
234
+ accent: {
235
+ $value: string;
236
+ };
237
+ accent2: {
238
+ $value: string;
239
+ };
240
+ onAccent: {
241
+ $value: string;
242
+ };
243
+ onAccent2: {
244
+ $value: string;
245
+ };
246
+ deep: {
247
+ $value: string;
248
+ };
249
+ deepEnd: {
250
+ $value: string;
251
+ };
252
+ onDeep: {
253
+ $value: string;
254
+ };
255
+ onDeepMuted: {
256
+ $value: string;
257
+ };
258
+ };
259
+ chart: {
260
+ $type: string;
261
+ "1": {
262
+ $value: string;
263
+ };
264
+ "2": {
265
+ $value: string;
266
+ };
267
+ "3": {
268
+ $value: string;
269
+ };
270
+ "4": {
271
+ $value: string;
272
+ };
273
+ "5": {
274
+ $value: string;
275
+ };
276
+ "6": {
277
+ $value: string;
278
+ };
279
+ "7": {
280
+ $value: string;
281
+ };
282
+ "8": {
283
+ $value: string;
284
+ };
285
+ };
286
+ font: {
287
+ $type: string;
288
+ sans: {
289
+ $value: string[];
290
+ };
291
+ mono: {
292
+ $value: string[];
293
+ };
294
+ };
295
+ fontSize: {
296
+ $type: string;
297
+ sm: {
298
+ $value: string;
299
+ };
300
+ md: {
301
+ $value: string;
302
+ };
303
+ lg: {
304
+ $value: string;
305
+ };
306
+ xl: {
307
+ $value: string;
308
+ };
309
+ title: {
310
+ $value: string;
311
+ };
312
+ display: {
313
+ $value: string;
314
+ };
315
+ heroSm: {
316
+ $value: string;
317
+ };
318
+ heroMd: {
319
+ $value: string;
320
+ };
321
+ hero: {
322
+ $value: string;
323
+ };
324
+ };
325
+ spacing: {
326
+ $type: string;
327
+ xs: {
328
+ $value: string;
329
+ };
330
+ sm: {
331
+ $value: string;
332
+ };
333
+ md: {
334
+ $value: string;
335
+ };
336
+ lg: {
337
+ $value: string;
338
+ };
339
+ xl: {
340
+ $value: string;
341
+ };
342
+ xxl: {
343
+ $value: string;
344
+ };
345
+ };
346
+ radius: {
347
+ $type: string;
348
+ sm: {
349
+ $value: string;
350
+ };
351
+ md: {
352
+ $value: string;
353
+ };
354
+ lg: {
355
+ $value: string;
356
+ };
357
+ xl: {
358
+ $value: string;
359
+ };
360
+ full: {
361
+ $value: string;
362
+ };
363
+ };
364
+ opacity: {
365
+ $type: string;
366
+ subtle: {
367
+ $value: number;
368
+ };
369
+ medium: {
370
+ $value: number;
371
+ };
372
+ full: {
373
+ $value: number;
374
+ };
375
+ };
376
+ duration: {
377
+ $type: string;
378
+ fast: {
379
+ $value: string;
380
+ };
381
+ base: {
382
+ $value: string;
383
+ };
384
+ slow: {
385
+ $value: string;
386
+ };
387
+ };
388
+ $extensions: {
389
+ "motion-kit": {
390
+ textPairs: {
391
+ fg: string;
392
+ bg: string;
393
+ }[];
394
+ };
395
+ };
396
+ };
397
+ };
398
+ /**
399
+ * Shared-library themes resolvable beside the built-ins (byte-identical
400
+ * copies of the shared token source; single source of truth stays in the
401
+ * templates package). Callers merge these into extraDocs; they are never
402
+ * built-ins, so template styles keep their own docs without collision.
403
+ */
404
+ export declare const bundledThemeDocs: () => Record<string, unknown>;
405
+ export type ThemeName = keyof typeof TOKEN_FILES;
406
+ export declare const THEME_NAMES: ThemeName[];
407
+ /**
408
+ * Single projection of DTCG JSON into flat JS tokens; the CSS output derives from this.
409
+ * `extraDocs` maps theme name to a DTCG document supplied by the caller (e.g. vendored project themes).
410
+ */
411
+ export declare const resolveTheme: (name: string, extraDocs?: Readonly<Record<string, unknown>>) => ResolvedTheme;
412
+ export {};
@@ -0,0 +1,71 @@
1
+ import { readGroup, toColours, toFontStacks, toNumbers, toPx, toSeconds } from "./dtcg.js";
2
+ import { optionalVars } from "./optional.js";
3
+ import calmDarkTokens from "./tokens/calm-dark.json" with { type: "json" };
4
+ import darkTokens from "./tokens/dark.json" with { type: "json" };
5
+ import defaultTokens from "./tokens/default.json" with { type: "json" };
6
+ import techDarkTokens from "./tokens/tech-dark.json" with { type: "json" };
7
+ const TOKEN_FILES = { default: defaultTokens, dark: darkTokens };
8
+ /**
9
+ * Shared-library themes resolvable beside the built-ins (byte-identical
10
+ * copies of the shared token source; single source of truth stays in the
11
+ * templates package). Callers merge these into extraDocs; they are never
12
+ * built-ins, so template styles keep their own docs without collision.
13
+ */
14
+ export const bundledThemeDocs = () => ({
15
+ "tech-dark": techDarkTokens,
16
+ "calm-dark": calmDarkTokens,
17
+ });
18
+ export const THEME_NAMES = Object.keys(TOKEN_FILES);
19
+ const isThemeName = (name) => Object.hasOwn(TOKEN_FILES, name);
20
+ const asRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value)
21
+ ? value
22
+ : undefined;
23
+ const isTextPair = (value) => {
24
+ const pair = asRecord(value);
25
+ return typeof pair?.fg === "string" && typeof pair.bg === "string";
26
+ };
27
+ const readTextPairs = (tokens, name) => {
28
+ const pairs = asRecord(asRecord(tokens.$extensions)?.["motion-kit"])?.textPairs;
29
+ if (!Array.isArray(pairs) || !pairs.every(isTextPair)) {
30
+ throw new Error(`theme "${name}" needs $extensions.motion-kit.textPairs: [{fg,bg}]`);
31
+ }
32
+ return pairs.map(({ fg, bg }) => ({ fg, bg }));
33
+ };
34
+ const knownNames = (extraDocs) => [
35
+ ...THEME_NAMES,
36
+ ...Object.keys(extraDocs),
37
+ ];
38
+ const docFor = (name, extraDocs) => {
39
+ const extras = Object.keys(extraDocs).filter(isThemeName);
40
+ if (extras.length > 0) {
41
+ throw new Error(`theme "${extras[0]}" collides with a built-in theme; rename it`);
42
+ }
43
+ if (isThemeName(name))
44
+ return TOKEN_FILES[name];
45
+ const extra = Object.hasOwn(extraDocs, name) ? asRecord(extraDocs[name]) : undefined;
46
+ if (extra === undefined) {
47
+ throw new Error(`unknown theme "${name}"; use one of: ${knownNames(extraDocs).join("|")}`);
48
+ }
49
+ return extra;
50
+ };
51
+ /**
52
+ * Single projection of DTCG JSON into flat JS tokens; the CSS output derives from this.
53
+ * `extraDocs` maps theme name to a DTCG document supplied by the caller (e.g. vendored project themes).
54
+ */
55
+ export const resolveTheme = (name, extraDocs = {}) => {
56
+ const tokens = docFor(name, extraDocs);
57
+ const base = {
58
+ name,
59
+ colors: toColours(readGroup(tokens, "color")),
60
+ chart: Object.values(toColours(readGroup(tokens, "chart"))),
61
+ fonts: toFontStacks(readGroup(tokens, "font")),
62
+ fontSizes: toPx(readGroup(tokens, "fontSize")),
63
+ spacing: toPx(readGroup(tokens, "spacing")),
64
+ radii: toPx(readGroup(tokens, "radius")),
65
+ opacities: toNumbers(readGroup(tokens, "opacity")),
66
+ durations: toSeconds(readGroup(tokens, "duration")),
67
+ textPairs: readTextPairs(tokens, name),
68
+ };
69
+ const vars = optionalVars(base, tokens);
70
+ return Object.keys(vars).length > 0 ? { ...base, vars } : base;
71
+ };