@pitlane/theme 0.1.0 → 0.3.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.
package/dist/index.mjs CHANGED
@@ -1,460 +1,6 @@
1
- import { createElement, css as css$1 } from "remix/ui";
2
- //#region src/brands.ts
3
- /**
4
- * The twelve DTCG token types, in canonical order.
5
- *
6
- * @internal
7
- */
8
- const TOKEN_TYPES = [
9
- "color",
10
- "dimension",
11
- "duration",
12
- "fontFamily",
13
- "fontWeight",
14
- "number",
15
- "cubicBezier",
16
- "shadow",
17
- "border",
18
- "transition",
19
- "gradient",
20
- "strokeStyle"
21
- ];
22
- //#endregion
23
- //#region src/tokens.ts
24
- /**
25
- * The error {@link createTheme} throws for every validation and
26
- * serialization failure. Validation is eager, so a bad document never
27
- * emits CSS. Every message names the offending token path.
28
- *
29
- * | Condition | Message shape |
30
- * | --- | --- |
31
- * | Unknown `$type` | `"color.brand" has unknown $type "sparkles"` |
32
- * | Unresolvable `$type` | `"color.brand" has no resolvable $type` |
33
- * | Typography token | `"heading": typography tokens are not supported in v1` |
34
- * | Reserved character in a name | `Token or group name "a.b" contains characters reserved by DTCG references (".", "{", "}")` |
35
- * | Empty CSS identifier | `Token path segment "!" produces an empty CSS identifier` |
36
- * | Malformed node | `"color.bg" is neither a group nor a token` |
37
- * | Variable-name collision | `Tokens "a" and "b" both produce the CSS variable --x` |
38
- * | Alias to a missing token | `"color.bg" references unknown token "color.white"` |
39
- * | Alias to a wrong-typed token | `"x" references "space.sm" of type "dimension" where "color" is required` |
40
- * | Alias cycle | `Alias cycle: a → b → a` |
41
- * | Invalid value for a declared type | `"x" has an invalid color value: …` — also `unknown colorSpace`, `unknown fontWeight keyword`, and `unknown strokeStyle keyword`; an empty `fontFamily` array counts |
42
- * | Bad mode override | `Mode override "x" does not exist in the base document`, `Mode override "x" may only set $value`, or (via a cross-type alias) the wrong-typed-alias message |
43
- * | Unminted `raw()` ref | `raw(): "var(--x)" names a var this theme never minted` |
44
- */
45
- var ThemeError = class extends Error {
46
- name = "ThemeError";
47
- };
48
- const ALIAS_RE = /^\{([^{}]+)\}$/;
49
- /**
50
- * Extracts the target key from a `"{path.to.token}"` alias string, or
51
- * `null` when the value is not an alias reference.
52
- *
53
- * @internal
54
- */
55
- function aliasTarget(value) {
56
- if (typeof value !== "string") return null;
57
- let match = ALIAS_RE.exec(value);
58
- return match ? match[1] : null;
59
- }
60
- /**
61
- * Kebab-cases one path segment for a CSS variable name. Throws when
62
- * the segment reduces to an empty identifier.
63
- *
64
- * @internal
65
- */
66
- function kebabSegment(segment) {
67
- let kebab = segment.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
68
- if (!kebab) throw new ThemeError(`Token path segment "${segment}" produces an empty CSS identifier`);
69
- return kebab;
70
- }
71
- /**
72
- * Walks a document, resolves every token's type and CSS variable
73
- * name, and returns the parsed tokens keyed by dotted path.
74
- *
75
- * @internal
76
- */
77
- function parseTokens(document) {
78
- let entries = /* @__PURE__ */ new Map();
79
- walk(document, [], validateType(document.$type, "$root"), entries);
80
- let tokens = /* @__PURE__ */ new Map();
81
- let varNames = /* @__PURE__ */ new Map();
82
- for (let entry of entries.values()) {
83
- let type = resolveType(entry.key, entries, []);
84
- let varName = `--${entry.path.map(kebabSegment).join("-")}`;
85
- let existing = varNames.get(varName);
86
- if (existing !== void 0) throw new ThemeError(`Tokens "${existing}" and "${entry.key}" both produce the CSS variable ${varName}`);
87
- varNames.set(varName, entry.key);
88
- let alias = aliasTarget(entry.value);
89
- tokens.set(entry.key, {
90
- key: entry.key,
91
- path: entry.path,
92
- varName,
93
- type,
94
- value: entry.value,
95
- ...alias === null ? {} : { aliasOf: alias }
96
- });
97
- }
98
- return tokens;
99
- }
100
- function walk(node, path, inherited, out) {
101
- for (let [key, child] of Object.entries(node)) {
102
- if (key.startsWith("$")) continue;
103
- let childPath = [...path, key];
104
- let childKey = childPath.join(".");
105
- if (/[.{}]/.test(key)) throw new ThemeError(`Token or group name "${childKey}" contains characters reserved by DTCG references (".", "{", "}")`);
106
- if (typeof child !== "object" || child === null || Array.isArray(child)) throw new ThemeError(`"${childKey}" is neither a group nor a token`);
107
- let record = child;
108
- let ownType = validateType(record.$type, childKey);
109
- if ("$value" in record) out.set(childKey, {
110
- key: childKey,
111
- path: childPath,
112
- ownType,
113
- inheritedType: inherited,
114
- value: record.$value
115
- });
116
- else walk(record, childPath, ownType ?? inherited, out);
117
- }
118
- }
119
- function validateType(value, key) {
120
- if (value === void 0) return void 0;
121
- if (value === "typography") throw new ThemeError(`"${key}": typography tokens are not supported in v1`);
122
- if (!TOKEN_TYPES.includes(value)) throw new ThemeError(`"${key}" has unknown $type "${String(value)}"`);
123
- return value;
124
- }
125
- function resolveType(key, entries, chain) {
126
- if (chain.includes(key)) throw new ThemeError(`Alias cycle: ${[...chain, key].join(" → ")}`);
127
- let entry = entries.get(key);
128
- if (!entry) throw new ThemeError(`"${chain[chain.length - 1] ?? key}" references unknown token "${key}"`);
129
- if (entry.ownType) return entry.ownType;
130
- let alias = aliasTarget(entry.value);
131
- if (alias !== null) return resolveType(alias, entries, [...chain, key]);
132
- if (entry.inheritedType) return entry.inheritedType;
133
- throw new ThemeError(`"${key}" has no resolvable $type`);
134
- }
135
- //#endregion
136
- //#region src/serialize.ts
137
- const FONT_WEIGHT_KEYWORDS = {
138
- thin: 100,
139
- hairline: 100,
140
- "extra-light": 200,
141
- "ultra-light": 200,
142
- light: 300,
143
- normal: 400,
144
- regular: 400,
145
- book: 400,
146
- medium: 500,
147
- "semi-bold": 600,
148
- "demi-bold": 600,
149
- bold: 700,
150
- "extra-bold": 800,
151
- "ultra-bold": 800,
152
- black: 900,
153
- heavy: 900,
154
- "extra-black": 950,
155
- "ultra-black": 950
156
- };
157
- const COLOR_FUNCTIONS = {
158
- hsl: {
159
- fn: "hsl",
160
- percents: [1, 2]
161
- },
162
- hwb: {
163
- fn: "hwb",
164
- percents: [1, 2]
165
- },
166
- lab: { fn: "lab" },
167
- lch: { fn: "lch" },
168
- oklab: { fn: "oklab" },
169
- oklch: { fn: "oklch" }
170
- };
171
- const COLOR_SPACES = /* @__PURE__ */ new Set([
172
- "srgb",
173
- "srgb-linear",
174
- "display-p3",
175
- "a98-rgb",
176
- "prophoto-rgb",
177
- "rec2020",
178
- "xyz-d65",
179
- "xyz-d50"
180
- ]);
181
- const STROKE_KEYWORDS = /* @__PURE__ */ new Set([
182
- "solid",
183
- "dashed",
184
- "dotted",
185
- "double",
186
- "groove",
187
- "ridge",
188
- "outset",
189
- "inset"
190
- ]);
191
- function serializeValue(type, value, ctx, key) {
192
- switch (type) {
193
- case "color": return serializeColor(value, key);
194
- case "dimension": return serializeMeasure(value, ["px", "rem"], key);
195
- case "duration": return serializeMeasure(value, ["ms", "s"], key);
196
- case "fontFamily": return serializeFontFamily(value, key);
197
- case "fontWeight": return serializeFontWeight(value, key);
198
- case "number": return serializeNumber(value, key);
199
- case "cubicBezier": return serializeCubicBezier(value, key);
200
- case "shadow": return serializeShadow(value, ctx, key);
201
- case "border": return serializeBorder(value, ctx, key);
202
- case "transition": return serializeTransition(value, ctx, key);
203
- case "gradient": return serializeGradient(value, ctx, key);
204
- case "strokeStyle": return serializeStrokeStyle(value, key);
205
- }
206
- }
207
- function field(type, value, ctx, key) {
208
- let alias = aliasTarget(value);
209
- if (alias !== null) return ctx.varRefFor(alias, key, type);
210
- return serializeValue(type, value, ctx, key);
211
- }
212
- function invalid(key, type, value) {
213
- return new ThemeError(`"${key}" has an invalid ${type} value: ${JSON.stringify(value)}`);
214
- }
215
- function serializeColor(value, key) {
216
- if (typeof value === "string") return value;
217
- if (typeof value !== "object" || value === null) throw invalid(key, "color", value);
218
- let { colorSpace, components, alpha, hex } = value;
219
- if (typeof hex === "string") return hex;
220
- if (typeof colorSpace !== "string" || !Array.isArray(components)) throw invalid(key, "color", value);
221
- let parts = components.map((component) => component === "none" ? "none" : serializeNumber(component, key));
222
- let alphaPart = alpha === void 0 ? "" : ` / ${serializeNumber(alpha, key)}`;
223
- let fn = COLOR_FUNCTIONS[colorSpace];
224
- if (fn) {
225
- let printed = parts.map((part, index) => fn.percents?.includes(index) && part !== "none" ? `${part}%` : part);
226
- return `${fn.fn}(${printed.join(" ")}${alphaPart})`;
227
- }
228
- if (COLOR_SPACES.has(colorSpace)) return `color(${colorSpace} ${parts.join(" ")}${alphaPart})`;
229
- throw new ThemeError(`"${key}" has unknown colorSpace "${colorSpace}"`);
230
- }
231
- function serializeMeasure(value, units, key) {
232
- if (typeof value === "string") return value;
233
- if (typeof value === "object" && value !== null) {
234
- let { value: amount, unit } = value;
235
- if (typeof amount === "number" && typeof unit === "string" && units.includes(unit)) return `${amount}${unit}`;
236
- }
237
- throw invalid(key, `dimension/duration (${units.join("|")})`, value);
238
- }
239
- function serializeFontFamily(value, key) {
240
- let names = Array.isArray(value) ? value : [value];
241
- if (names.length === 0) throw invalid(key, "fontFamily", value);
242
- return names.map((name) => {
243
- if (typeof name !== "string") throw invalid(key, "fontFamily", value);
244
- return /^[a-zA-Z][a-zA-Z-]*$/.test(name) ? name : `"${name.replaceAll("\"", "\\\"")}"`;
245
- }).join(", ");
246
- }
247
- function serializeFontWeight(value, key) {
248
- if (typeof value === "number") {
249
- if (value >= 1 && value <= 1e3) return String(value);
250
- throw invalid(key, "fontWeight", value);
251
- }
252
- if (typeof value === "string") {
253
- let mapped = FONT_WEIGHT_KEYWORDS[value];
254
- if (mapped !== void 0) return String(mapped);
255
- throw new ThemeError(`"${key}" has unknown fontWeight keyword "${value}"`);
256
- }
257
- throw invalid(key, "fontWeight", value);
258
- }
259
- function serializeNumber(value, key) {
260
- if (typeof value !== "number" || !Number.isFinite(value)) throw invalid(key, "number", value);
261
- return String(value);
262
- }
263
- function serializeCubicBezier(value, key) {
264
- if (!Array.isArray(value) || value.length !== 4) throw invalid(key, "cubicBezier", value);
265
- return `cubic-bezier(${value.map((part) => serializeNumber(part, key)).join(", ")})`;
266
- }
267
- function serializeShadow(value, ctx, key) {
268
- return (Array.isArray(value) ? value : [value]).map((shadow) => {
269
- if (typeof shadow !== "object" || shadow === null) throw invalid(key, "shadow", value);
270
- let { color, offsetX, offsetY, blur, spread, inset } = shadow;
271
- let parts = [
272
- field("dimension", offsetX, ctx, key),
273
- field("dimension", offsetY, ctx, key),
274
- blur === void 0 ? "0" : field("dimension", blur, ctx, key),
275
- spread === void 0 ? "0" : field("dimension", spread, ctx, key),
276
- field("color", color, ctx, key)
277
- ];
278
- return `${inset === true ? "inset " : ""}${parts.join(" ")}`;
279
- }).join(", ");
280
- }
281
- function serializeBorder(value, ctx, key) {
282
- if (typeof value !== "object" || value === null) throw invalid(key, "border", value);
283
- let { color, width, style } = value;
284
- return `${field("dimension", width, ctx, key)} ${field("strokeStyle", style, ctx, key)} ${field("color", color, ctx, key)}`;
285
- }
286
- function serializeTransition(value, ctx, key) {
287
- if (typeof value !== "object" || value === null) throw invalid(key, "transition", value);
288
- let { duration, timingFunction, delay } = value;
289
- let delayPart = delay === void 0 ? "0s" : field("duration", delay, ctx, key);
290
- return `${field("duration", duration, ctx, key)} ${field("cubicBezier", timingFunction, ctx, key)} ${delayPart}`;
291
- }
292
- function serializeGradient(value, ctx, key) {
293
- if (!Array.isArray(value)) throw invalid(key, "gradient", value);
294
- return value.map((stop) => {
295
- if (typeof stop !== "object" || stop === null) throw invalid(key, "gradient", value);
296
- let { color, position } = stop;
297
- if (typeof position !== "number") throw invalid(key, "gradient stop position", position);
298
- return `${field("color", color, ctx, key)} ${Number((position * 100).toFixed(4))}%`;
299
- }).join(", ");
300
- }
301
- function serializeStrokeStyle(value, key) {
302
- if (typeof value === "string") {
303
- if (STROKE_KEYWORDS.has(value)) return value;
304
- throw new ThemeError(`"${key}" has unknown strokeStyle keyword "${value}"`);
305
- }
306
- if (typeof value === "object" && value !== null) return "dashed";
307
- throw invalid(key, "strokeStyle", value);
308
- }
309
- //#endregion
310
- //#region src/theme.ts
311
- function createThemeComponent(cssText) {
312
- let escaped = cssText.replace(/<\/style/gi, "<\\/style");
313
- return function Theme(handle) {
314
- return () => createElement("style", {
315
- nonce: handle.props.nonce,
316
- "data-pitlane-theme": "",
317
- innerHTML: escaped
318
- });
319
- };
320
- }
321
- /**
322
- * Compiles a DTCG design-token document into a typed accessor, a
323
- * `raw` resolver, and a `<Theme />` component. All validation and
324
- * serialization happen eagerly here: a malformed document throws
325
- * {@link ThemeError} rather than emitting broken CSS.
326
- *
327
- * Each token becomes a CSS custom property named after its
328
- * kebab-cased path — `color.gray.900` becomes `--color-gray-900`.
329
- * Two paths that collide after kebab-casing throw, as do names
330
- * containing `.`, `{`, or `}`, which the alias syntax reserves.
331
- *
332
- * Author the document in TypeScript, not imported JSON: `createTheme`
333
- * infers a `const` type parameter, so an inline object needs no
334
- * `as const`, but a JSON import widens its literals and the token
335
- * brands degrade.
336
- *
337
- * @param config - The token document. Groups nest to any depth; a
338
- * node with a `$value` is a token.
339
- * @param options - Optional per-mode overrides ({@link ThemeOptions}).
340
- * @returns The {@link ThemeResult}: `token`, `raw`, and `Theme`.
341
- * @throws ThemeError on any validation failure (unknown or
342
- * unresolvable `$type`, reserved characters, variable collision,
343
- * unknown or wrong-typed alias, alias cycle, invalid value, or a bad
344
- * mode override).
345
- *
346
- * @see {@link DTCGDocument} for the document shape, the accepted
347
- * `$value` forms, and alias semantics.
348
- *
349
- * @example
350
- * ```ts
351
- * export let { token: t, raw, Theme } = createTheme(
352
- * {
353
- * color: {
354
- * $type: "color",
355
- * white: { $value: "#fff" },
356
- * gray: { 900: { $value: "#171717" } },
357
- * bg: { $value: "{color.white}" }, // alias → var() indirection
358
- * },
359
- * },
360
- * { modes: { dark: { color: { bg: { $value: "{color.gray.900}" } } } } },
361
- * );
362
- *
363
- * t.color.bg; // "var(--color-bg)"
364
- * raw(t.color.bg); // "#fff" (base mode, alias chased to the end)
365
- * ```
366
- */
367
- function createTheme(config, options = {}) {
368
- let compiled = compile(config, options.modes ?? {});
369
- let rawByRef = /* @__PURE__ */ new Map();
370
- let rawByKey = /* @__PURE__ */ new Map();
371
- for (let token of compiled.tokens.values()) rawByRef.set(`var(${token.varName})`, resolveRaw(token, compiled.tokens, rawByKey, []));
372
- return {
373
- token: buildAccessor(compiled.tokens),
374
- raw(ref) {
375
- let value = rawByRef.get(ref);
376
- if (value === void 0) throw new ThemeError(`raw(): "${ref}" names a var this theme never minted`);
377
- return value;
378
- },
379
- Theme: createThemeComponent(compiled.cssText)
380
- };
381
- }
382
- function compile(config, modes) {
383
- let tokens = parseTokens(config);
384
- let ctx = { varRefFor(key, from, expected) {
385
- let target = tokens.get(key);
386
- if (!target) throw new ThemeError(`"${from}" references unknown token "${key}"`);
387
- if (target.type !== expected) throw new ThemeError(`"${from}" references "${key}" of type "${target.type}" where "${expected}" is required`);
388
- return `var(${target.varName})`;
389
- } };
390
- let declarations = /* @__PURE__ */ new Map();
391
- for (let token of tokens.values()) declarations.set(token.varName, token.aliasOf !== void 0 ? ctx.varRefFor(token.aliasOf, token.key, token.type) : serializeValue(token.type, token.value, ctx, token.key));
392
- let modeBlocks = [];
393
- for (let mode of ["light", "dark"]) {
394
- let overrides = modes[mode];
395
- if (overrides === void 0) continue;
396
- let modeDeclarations = compileModeOverrides(overrides, tokens, ctx);
397
- if (modeDeclarations.size === 0) continue;
398
- let lines = [...modeDeclarations].map(([name, value]) => ` ${name}: ${value};`);
399
- modeBlocks.push(`@media (prefers-color-scheme: ${mode}) {\n :root {\n${lines.join("\n")}\n }\n}`);
400
- }
401
- return {
402
- tokens,
403
- cssText: buildCssText(declarations, modeBlocks)
404
- };
405
- }
406
- function compileModeOverrides(overrides, tokens, ctx) {
407
- let out = /* @__PURE__ */ new Map();
408
- walkMode(overrides, [], tokens, ctx, out);
409
- return out;
410
- }
411
- function walkMode(node, path, tokens, ctx, out) {
412
- if (typeof node !== "object" || node === null) throw new ThemeError(`Mode override "${path.join(".")}" is neither a group nor a token`);
413
- let record = node;
414
- if ("$value" in record) {
415
- let key = path.join(".");
416
- if (Object.keys(record).length !== 1) throw new ThemeError(`Mode override "${key}" may only set $value`);
417
- let base = tokens.get(key);
418
- if (!base) throw new ThemeError(`Mode override "${key}" does not exist in the base document`);
419
- let alias = aliasTarget(record.$value);
420
- out.set(base.varName, alias !== null ? ctx.varRefFor(alias, key, base.type) : serializeValue(base.type, record.$value, ctx, key));
421
- return;
422
- }
423
- for (let [key, child] of Object.entries(record)) {
424
- if (key.startsWith("$")) throw new ThemeError(`Mode override "${[...path, key].join(".")}" may only set $value`);
425
- walkMode(child, [...path, key], tokens, ctx, out);
426
- }
427
- }
428
- function resolveRaw(token, tokens, memo, chain) {
429
- let cached = memo.get(token.key);
430
- if (cached !== void 0) return cached;
431
- if (chain.includes(token.key)) throw new ThemeError(`Alias cycle: ${[...chain, token.key].join(" → ")}`);
432
- let value;
433
- if (token.aliasOf !== void 0) {
434
- let target = tokens.get(token.aliasOf);
435
- if (!target) throw new ThemeError(`"${token.key}" references unknown token "${token.aliasOf}"`);
436
- value = resolveRaw(target, tokens, memo, [...chain, token.key]);
437
- } else value = serializeValue(token.type, token.value, { varRefFor(key, from) {
438
- let target = tokens.get(key);
439
- if (!target) throw new ThemeError(`"${from}" references unknown token "${key}"`);
440
- return resolveRaw(target, tokens, memo, [...chain, token.key]);
441
- } }, token.key);
442
- memo.set(token.key, value);
443
- return value;
444
- }
445
- function buildAccessor(tokens) {
446
- let root = Object.create(null);
447
- for (let token of tokens.values()) {
448
- let node = root;
449
- for (let segment of token.path.slice(0, -1)) node = node[segment] ??= Object.create(null);
450
- node[token.path[token.path.length - 1]] = `var(${token.varName})`;
451
- }
452
- return root;
453
- }
454
- function buildCssText(declarations, modeBlocks) {
455
- return [`:root {\n${[...declarations].map(([name, value]) => ` ${name}: ${value};`).join("\n")}\n}`, ...modeBlocks].join("\n\n");
456
- }
457
- //#endregion
1
+ import { S as ThemeError } from "./schema-JmfNnMzr.mjs";
2
+ import { t as createTheme } from "./theme-CMaWrQJ5.mjs";
3
+ import { css as css$1 } from "remix/ui";
458
4
  //#region src/css.ts
459
5
  /**
460
6
  * Brand-enforced wrapper over `remix/ui`'s `css()` mixin. Token-mapped
@@ -504,6 +50,56 @@ function normalizeStyles(styles) {
504
50
  return out;
505
51
  }
506
52
  //#endregion
53
+ //#region src/scale.ts
54
+ /**
55
+ * Turns one token into a multiplier, so a scale does not have to name
56
+ * every step. The returned function produces
57
+ * `calc(<base> * <steps>)`, keeping whichever brand the base carried.
58
+ *
59
+ * Use this for a token you did not declare as a scale. A token
60
+ * declared with `s.scale()` is already a multiplier, and its base is
61
+ * `t.spacing.token` when you need it.
62
+ *
63
+ * The result is a CSS string like any other, so it also works as an
64
+ * authored token value.
65
+ *
66
+ * @param base - The token to multiply
67
+ * @returns A function from steps to a token of the same type
68
+ *
69
+ * @example
70
+ * ```ts
71
+ * import { scale } from "@pitlane/theme";
72
+ *
73
+ * let step = scale(t.tracking.tight);
74
+ * css({ letterSpacing: step(2) }); // calc(var(--tracking-tight) * 2)
75
+ * ```
76
+ */
77
+ function scale(base) {
78
+ return (steps) => `calc(${base} * ${steps})`;
79
+ }
80
+ /**
81
+ * A `light-dark()` color, resolved by the browser against the
82
+ * `color-scheme` property. A subtree that sets `color-scheme` flips
83
+ * whatever the media query says, which is what a theme toggle needs.
84
+ *
85
+ * Both arguments may be token references, so a mode override of either
86
+ * primitive still reaches the result.
87
+ *
88
+ * `light-dark()` is color-only. Use `modes` for anything else.
89
+ *
90
+ * @param light - The color for a light `color-scheme`
91
+ * @param dark - The color for a dark `color-scheme`
92
+ * @returns The `light-dark()` function text
93
+ *
94
+ * @example
95
+ * ```ts
96
+ * tokens: { surface: { page: lightDark("#ffffff", "#1a1a1a") } }
97
+ * ```
98
+ */
99
+ function lightDark(light, dark) {
100
+ return `light-dark(${light}, ${dark})`;
101
+ }
102
+ //#endregion
507
103
  //#region src/tva.ts
508
104
  /**
509
105
  * Builds a variant resolver modeled on [cva](https://cva.style). Where
@@ -626,4 +222,4 @@ function cx(...inputs) {
626
222
  return out.join(" ");
627
223
  }
628
224
  //#endregion
629
- export { ThemeError, combine, createTheme, css, cx, tva };
225
+ export { ThemeError, combine, createTheme, css, cx, lightDark, scale, tva };