tailwind-styled-v4 5.0.0 → 5.0.1
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/CHANGELOG.md +398 -0
- package/LICENSE +21 -0
- package/README.md +532 -0
- package/dist/analyzer.d.mts +114 -0
- package/dist/analyzer.d.ts +114 -0
- package/dist/analyzer.js +1555 -0
- package/dist/analyzer.js.map +1 -0
- package/dist/analyzer.mjs +1544 -0
- package/dist/analyzer.mjs.map +1 -0
- package/dist/{animate.d.cts → animate.d.mts} +3 -30
- package/dist/animate.d.ts +3 -30
- package/dist/animate.js +149 -99
- package/dist/animate.js.map +1 -1
- package/dist/{animate.cjs → animate.mjs} +130 -119
- package/dist/animate.mjs.map +1 -0
- package/dist/atomic.d.mts +18 -0
- package/dist/atomic.d.ts +18 -0
- package/dist/atomic.js +191 -0
- package/dist/atomic.js.map +1 -0
- package/dist/atomic.mjs +185 -0
- package/dist/atomic.mjs.map +1 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +6063 -0
- package/dist/cli.js.map +1 -0
- package/dist/cli.mjs +6053 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/compiler.d.mts +1009 -0
- package/dist/compiler.d.ts +1009 -0
- package/dist/compiler.js +4518 -0
- package/dist/compiler.js.map +1 -0
- package/dist/compiler.mjs +4443 -0
- package/dist/compiler.mjs.map +1 -0
- package/dist/dashboard.d.mts +272 -0
- package/dist/dashboard.d.ts +272 -0
- package/dist/dashboard.js +249 -0
- package/dist/dashboard.js.map +1 -0
- package/dist/dashboard.mjs +239 -0
- package/dist/dashboard.mjs.map +1 -0
- package/dist/devtools.js +170 -157
- package/dist/devtools.js.map +1 -1
- package/dist/{devtools.cjs → devtools.mjs} +165 -166
- package/dist/devtools.mjs.map +1 -0
- package/dist/engine.d.mts +84 -0
- package/dist/engine.d.ts +84 -0
- package/dist/engine.js +3014 -0
- package/dist/engine.js.map +1 -0
- package/dist/engine.mjs +3005 -0
- package/dist/engine.mjs.map +1 -0
- package/dist/{index.d.cts → index.d.mts} +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +945 -36
- package/dist/index.js.map +1 -1
- package/dist/{index.cjs → index.mjs} +899 -90
- package/dist/index.mjs.map +1 -0
- package/dist/liveTokenEngine-DYN3Zale.d.mts +34 -0
- package/dist/liveTokenEngine-DYN3Zale.d.ts +34 -0
- package/dist/{next.d.cts → next.d.mts} +2 -1
- package/dist/next.d.ts +2 -1
- package/dist/next.js +6853 -35
- package/dist/next.js.map +1 -1
- package/dist/next.mjs +7050 -0
- package/dist/next.mjs.map +1 -0
- package/dist/plugin.d.mts +90 -0
- package/dist/plugin.d.ts +90 -0
- package/dist/plugin.js +185 -0
- package/dist/plugin.js.map +1 -0
- package/dist/plugin.mjs +174 -0
- package/dist/plugin.mjs.map +1 -0
- package/dist/pluginRegistry.d.mts +83 -0
- package/dist/pluginRegistry.d.ts +83 -0
- package/dist/pluginRegistry.js +303 -0
- package/dist/pluginRegistry.js.map +1 -0
- package/dist/pluginRegistry.mjs +298 -0
- package/dist/pluginRegistry.mjs.map +1 -0
- package/dist/preset.js +9 -4
- package/dist/preset.js.map +1 -1
- package/dist/{preset.cjs → preset.mjs} +5 -14
- package/dist/preset.mjs.map +1 -0
- package/dist/rspack.d.mts +33 -0
- package/dist/rspack.d.ts +33 -0
- package/dist/rspack.js +55 -0
- package/dist/rspack.js.map +1 -0
- package/dist/rspack.mjs +45 -0
- package/dist/rspack.mjs.map +1 -0
- package/dist/runtime.d.mts +62 -0
- package/dist/runtime.d.ts +62 -0
- package/dist/runtime.js +207 -0
- package/dist/runtime.js.map +1 -0
- package/dist/runtime.mjs +188 -0
- package/dist/runtime.mjs.map +1 -0
- package/dist/runtimeCss.d.mts +65 -0
- package/dist/runtimeCss.d.ts +65 -0
- package/dist/{css.cjs → runtimeCss.js} +71 -4
- package/dist/runtimeCss.js.map +1 -0
- package/dist/{css.js → runtimeCss.mjs} +66 -5
- package/dist/runtimeCss.mjs.map +1 -0
- package/dist/scanner.d.mts +25 -0
- package/dist/scanner.d.ts +25 -0
- package/dist/scanner.js +717 -0
- package/dist/scanner.js.map +1 -0
- package/dist/scanner.mjs +703 -0
- package/dist/scanner.mjs.map +1 -0
- package/dist/shared.d.mts +85 -0
- package/dist/shared.d.ts +85 -0
- package/dist/shared.js +255 -0
- package/dist/shared.js.map +1 -0
- package/dist/shared.mjs +233 -0
- package/dist/shared.mjs.map +1 -0
- package/dist/storybookAddon.d.mts +108 -0
- package/dist/storybookAddon.d.ts +108 -0
- package/dist/storybookAddon.js +95 -0
- package/dist/storybookAddon.js.map +1 -0
- package/dist/storybookAddon.mjs +88 -0
- package/dist/storybookAddon.mjs.map +1 -0
- package/dist/svelte.d.mts +114 -0
- package/dist/svelte.d.ts +114 -0
- package/dist/svelte.js +67 -0
- package/dist/svelte.js.map +1 -0
- package/dist/svelte.mjs +59 -0
- package/dist/svelte.mjs.map +1 -0
- package/dist/testing.d.mts +185 -0
- package/dist/testing.d.ts +185 -0
- package/dist/testing.js +173 -0
- package/dist/testing.js.map +1 -0
- package/dist/testing.mjs +158 -0
- package/dist/testing.mjs.map +1 -0
- package/dist/theme.d.mts +188 -0
- package/dist/theme.d.ts +188 -0
- package/dist/theme.js +334 -0
- package/dist/theme.js.map +1 -0
- package/dist/theme.mjs +311 -0
- package/dist/theme.mjs.map +1 -0
- package/dist/types-DXr2PmGP.d.mts +31 -0
- package/dist/types-DXr2PmGP.d.ts +31 -0
- package/dist/vite.js +4181 -16
- package/dist/vite.js.map +1 -1
- package/dist/vite.mjs +4281 -0
- package/dist/vite.mjs.map +1 -0
- package/dist/vue.d.mts +89 -0
- package/dist/vue.d.ts +89 -0
- package/dist/vue.js +104 -0
- package/dist/vue.js.map +1 -0
- package/dist/vue.mjs +96 -0
- package/dist/vue.mjs.map +1 -0
- package/package.json +168 -65
- package/dist/animate.cjs.map +0 -1
- package/dist/chunk-VZEJV27B.js +0 -11
- package/dist/chunk-VZEJV27B.js.map +0 -1
- package/dist/chunk-Y5D3E72P.cjs +0 -13
- package/dist/chunk-Y5D3E72P.cjs.map +0 -1
- package/dist/css.cjs.map +0 -1
- package/dist/css.d.cts +0 -30
- package/dist/css.d.ts +0 -30
- package/dist/css.js.map +0 -1
- package/dist/devtools.cjs.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/next.cjs +0 -248
- package/dist/next.cjs.map +0 -1
- package/dist/preset.cjs.map +0 -1
- package/dist/turbopackLoader.cjs +0 -37
- package/dist/turbopackLoader.cjs.map +0 -1
- package/dist/turbopackLoader.d.cts +0 -12
- package/dist/turbopackLoader.d.ts +0 -12
- package/dist/turbopackLoader.js +0 -35
- package/dist/turbopackLoader.js.map +0 -1
- package/dist/vite.cjs +0 -138
- package/dist/vite.cjs.map +0 -1
- package/dist/webpackLoader.cjs +0 -51
- package/dist/webpackLoader.cjs.map +0 -1
- package/dist/webpackLoader.d.cts +0 -17
- package/dist/webpackLoader.d.ts +0 -17
- package/dist/webpackLoader.js +0 -49
- package/dist/webpackLoader.js.map +0 -1
- /package/dist/{devtools.d.cts → devtools.d.mts} +0 -0
- /package/dist/{preset.d.cts → preset.d.mts} +0 -0
- /package/dist/{vite.d.cts → vite.d.mts} +0 -0
package/dist/testing.mjs
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/* tailwind-styled-v4 v5.0.1 | MIT | https://github.com/dictionar32/tailwind-styled-v4 */
|
|
2
|
+
|
|
3
|
+
// packages/testing/src/index.ts
|
|
4
|
+
function toHaveClass(className) {
|
|
5
|
+
return (value) => {
|
|
6
|
+
const pass = Boolean(value?.classList?.contains(className));
|
|
7
|
+
return {
|
|
8
|
+
pass,
|
|
9
|
+
message: () => `expected element ${pass ? "not " : ""}to contain class '${className}'`
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function toHaveClasses(classNames) {
|
|
14
|
+
return (value) => {
|
|
15
|
+
if (!value) return { pass: false, message: () => "element is null or undefined" };
|
|
16
|
+
const missing = classNames.filter((c) => !value.classList.contains(c));
|
|
17
|
+
const pass = missing.length === 0;
|
|
18
|
+
return {
|
|
19
|
+
pass,
|
|
20
|
+
message: () => pass ? `expected element not to have all classes: ${classNames.join(", ")}` : `expected element to have classes: ${missing.join(", ")}`
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function toNotHaveClass(className) {
|
|
25
|
+
return (value) => {
|
|
26
|
+
if (!value) return { pass: false, message: () => "element is null or undefined" };
|
|
27
|
+
const pass = !value.classList.contains(className);
|
|
28
|
+
return {
|
|
29
|
+
pass,
|
|
30
|
+
message: () => `expected element ${pass ? "" : "not "}to have class '${className}'`
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
var tailwindMatchers = { toHaveClass, toHaveClasses, toNotHaveClass };
|
|
35
|
+
function expectClasses(element, classes) {
|
|
36
|
+
if (!element) throw new Error("expectClasses: element is null or undefined");
|
|
37
|
+
const missing = classes.filter((c) => !element.classList.contains(c));
|
|
38
|
+
if (missing.length > 0) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`Expected element to have classes: ${missing.join(", ")}
|
|
41
|
+
Actual classes: ${element.className}`
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function expectNoClasses(element, classes) {
|
|
46
|
+
if (!element) throw new Error("expectNoClasses: element is null or undefined");
|
|
47
|
+
const found = classes.filter((c) => element.classList.contains(c));
|
|
48
|
+
if (found.length > 0) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
`Expected element NOT to have classes: ${found.join(", ")}
|
|
51
|
+
Actual classes: ${element.className}`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function getClassList(element) {
|
|
56
|
+
if (!element) return [];
|
|
57
|
+
return Array.from(element.classList).sort();
|
|
58
|
+
}
|
|
59
|
+
function snapshotVariants(render, variants) {
|
|
60
|
+
return variants.map((variant) => ({
|
|
61
|
+
variant,
|
|
62
|
+
output: render(variant)
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
function expandVariantMatrix(matrix) {
|
|
66
|
+
const keys = Object.keys(matrix);
|
|
67
|
+
if (keys.length === 0) return [{}];
|
|
68
|
+
const result = [];
|
|
69
|
+
function walk(index, current) {
|
|
70
|
+
if (index >= keys.length) {
|
|
71
|
+
result.push({ ...current });
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const key = keys[index];
|
|
75
|
+
for (const value of matrix[key] ?? []) {
|
|
76
|
+
current[key] = value;
|
|
77
|
+
walk(index + 1, current);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
walk(0, {});
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
function testAllVariants(matrix, testFn) {
|
|
84
|
+
const combinations = expandVariantMatrix(matrix);
|
|
85
|
+
for (const variant of combinations) {
|
|
86
|
+
testFn(variant);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function expectClassesEqual(actual, expected) {
|
|
90
|
+
const actualSet = new Set(actual.trim().split(/\s+/).filter(Boolean));
|
|
91
|
+
const expectedSet = new Set(expected.trim().split(/\s+/).filter(Boolean));
|
|
92
|
+
const missing = [...expectedSet].filter((c) => !actualSet.has(c));
|
|
93
|
+
const extra = [...actualSet].filter((c) => !expectedSet.has(c));
|
|
94
|
+
if (missing.length > 0 || extra.length > 0) {
|
|
95
|
+
const parts = [];
|
|
96
|
+
if (missing.length > 0) parts.push(`Missing: ${missing.join(", ")}`);
|
|
97
|
+
if (extra.length > 0) parts.push(`Extra: ${extra.join(", ")}`);
|
|
98
|
+
throw new Error(`Class mismatch:
|
|
99
|
+
${parts.join("\n ")}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function expectEngineMetrics(metrics, expectations) {
|
|
103
|
+
if (expectations.minFiles !== void 0) {
|
|
104
|
+
const actual = metrics.totalFiles ?? 0;
|
|
105
|
+
if (actual < expectations.minFiles) {
|
|
106
|
+
throw new Error(
|
|
107
|
+
`Engine metrics: expected at least ${expectations.minFiles} files, got ${actual}`
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (expectations.maxBuildTimeMs !== void 0) {
|
|
112
|
+
const actual = metrics.buildTimeMs ?? 0;
|
|
113
|
+
if (actual > expectations.maxBuildTimeMs) {
|
|
114
|
+
throw new Error(
|
|
115
|
+
`Engine metrics: build took ${actual}ms, expected \u2264 ${expectations.maxBuildTimeMs}ms`
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (expectations.minUniqueClasses !== void 0) {
|
|
120
|
+
const actual = metrics.uniqueClasses ?? 0;
|
|
121
|
+
if (actual < expectations.minUniqueClasses) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
`Engine metrics: expected at least ${expectations.minUniqueClasses} unique classes, got ${actual}`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (expectations.cacheHitRateMin !== void 0) {
|
|
128
|
+
const hits = metrics.cacheHits ?? 0;
|
|
129
|
+
const total = hits + (metrics.cacheMisses ?? 0);
|
|
130
|
+
const rate = total > 0 ? hits / total : 0;
|
|
131
|
+
if (rate < expectations.cacheHitRateMin) {
|
|
132
|
+
throw new Error(
|
|
133
|
+
`Engine metrics: cache hit rate ${(rate * 100).toFixed(1)}%, expected \u2265 ${(expectations.cacheHitRateMin * 100).toFixed(1)}%`
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function toHaveEngineMetrics(expectations) {
|
|
139
|
+
return (metrics) => {
|
|
140
|
+
try {
|
|
141
|
+
expectEngineMetrics(metrics, expectations);
|
|
142
|
+
return { pass: true, message: () => "engine metrics matched expectations" };
|
|
143
|
+
} catch (e) {
|
|
144
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
145
|
+
return { pass: false, message: () => msg };
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
var tailwindMatchersWithMetrics = {
|
|
150
|
+
toHaveClass,
|
|
151
|
+
toHaveClasses,
|
|
152
|
+
toNotHaveClass,
|
|
153
|
+
toHaveEngineMetrics
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
export { expandVariantMatrix, expectClasses, expectClassesEqual, expectEngineMetrics, expectNoClasses, getClassList, snapshotVariants, tailwindMatchers, tailwindMatchersWithMetrics, testAllVariants, toHaveClass, toHaveClasses, toHaveEngineMetrics, toNotHaveClass };
|
|
157
|
+
//# sourceMappingURL=testing.mjs.map
|
|
158
|
+
//# sourceMappingURL=testing.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../packages/testing/src/index.ts"],"names":[],"mappings":"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","file":"testing.mjs","sourcesContent":["/**\n * tailwind-styled-v4 — Testing Utilities\n *\n * Integrasi dengan Jest / Vitest untuk test komponen tw().\n *\n * @example\n * // vitest.config.ts\n * import { tailwindStyledSetup } from '@tailwind-styled/testing'\n * export default defineConfig({ test: { setupFiles: ['@tailwind-styled/testing/setup'] } })\n *\n * // Button.test.ts\n * import { render } from '@testing-library/react'\n * import { expectClasses, getVariantClass } from '@tailwind-styled/testing'\n *\n * test('Button renders primary variant', () => {\n * const { container } = render(<Button intent=\"primary\" />)\n * expectClasses(container.firstChild, ['bg-blue-500', 'text-white'])\n * })\n */\n\n// ─── Jest/Vitest custom matchers ─────────────────────────────────────────────\n\n/**\n * Custom matcher: toHaveClass\n * Dipakai langsung atau via expect.extend(tailwindMatchers)\n *\n * @example\n * expect(element).toHaveClass('bg-blue-500')\n */\nexport function toHaveClass(className: string) {\n return (value: { classList?: { contains: (name: string) => boolean } }) => {\n const pass = Boolean(value?.classList?.contains(className))\n return {\n pass,\n message: () => `expected element ${pass ? \"not \" : \"\"}to contain class '${className}'`,\n }\n }\n}\n\n/**\n * Custom matcher: toHaveClasses\n * Cek beberapa class sekaligus\n *\n * @example\n * expect(element).toHaveClasses(['px-4', 'py-2', 'rounded'])\n */\nexport function toHaveClasses(classNames: string[]) {\n return (value: Element | null | undefined) => {\n if (!value) return { pass: false, message: () => \"element is null or undefined\" }\n const missing = classNames.filter((c) => !value.classList.contains(c))\n const pass = missing.length === 0\n return {\n pass,\n message: () =>\n pass\n ? `expected element not to have all classes: ${classNames.join(\", \")}`\n : `expected element to have classes: ${missing.join(\", \")}`,\n }\n }\n}\n\n/**\n * Custom matcher: toNotHaveClass\n *\n * @example\n * expect(element).toNotHaveClass('hidden')\n */\nexport function toNotHaveClass(className: string) {\n return (value: Element | null | undefined) => {\n if (!value) return { pass: false, message: () => \"element is null or undefined\" }\n const pass = !value.classList.contains(className)\n return {\n pass,\n message: () => `expected element ${pass ? \"\" : \"not \"}to have class '${className}'`,\n }\n }\n}\n\n/** Semua matchers — pakai dengan expect.extend(tailwindMatchers) */\nexport const tailwindMatchers = { toHaveClass, toHaveClasses, toNotHaveClass }\n\n// ─── Helper utilities ────────────────────────────────────────────────────────\n\n/**\n * Assert element punya semua class yang diharapkan.\n * Lebih ergonomis dari `expect(el).toHaveClasses([...])` untuk banyak class.\n *\n * @example\n * const { container } = render(<Button intent=\"primary\" size=\"lg\" />)\n * expectClasses(container.firstChild, ['bg-blue-500', 'text-white', 'h-12'])\n */\nexport function expectClasses(element: Element | null | undefined, classes: string[]): void {\n if (!element) throw new Error(\"expectClasses: element is null or undefined\")\n const missing = classes.filter((c) => !element.classList.contains(c))\n if (missing.length > 0) {\n throw new Error(\n `Expected element to have classes: ${missing.join(\", \")}\\n` +\n ` Actual classes: ${element.className}`\n )\n }\n}\n\n/**\n * Assert element tidak punya class tertentu.\n *\n * @example\n * expectNoClasses(container.firstChild, ['opacity-50', 'cursor-not-allowed'])\n */\nexport function expectNoClasses(element: Element | null | undefined, classes: string[]): void {\n if (!element) throw new Error(\"expectNoClasses: element is null or undefined\")\n const found = classes.filter((c) => element.classList.contains(c))\n if (found.length > 0) {\n throw new Error(\n `Expected element NOT to have classes: ${found.join(\", \")}\\n` +\n ` Actual classes: ${element.className}`\n )\n }\n}\n\n/**\n * Extract class list dari element sebagai sorted array.\n * Berguna untuk snapshot testing.\n *\n * @example\n * expect(getClassList(element)).toMatchSnapshot()\n */\nexport function getClassList(element: Element | null | undefined): string[] {\n if (!element) return []\n return Array.from(element.classList).sort()\n}\n\n// ─── Variant snapshot helpers ────────────────────────────────────────────────\n\n/**\n * Buat snapshot dari semua kombinasi variant.\n * Berguna untuk regression testing pada styled components.\n *\n * @example\n * const buttonVariants = snapshotVariants(\n * (props) => {\n * const { container } = render(<Button {...props} />)\n * return container.firstChild?.className ?? ''\n * },\n * [\n * { intent: 'primary', size: 'sm' },\n * { intent: 'primary', size: 'lg' },\n * { intent: 'danger', size: 'sm' },\n * ]\n * )\n * expect(buttonVariants).toMatchSnapshot()\n */\nexport function snapshotVariants<T>(render: (variant: T) => string, variants: T[]) {\n return variants.map((variant) => ({\n variant,\n output: render(variant),\n }))\n}\n\n/**\n * Generate semua kombinasi dari variant matrix.\n *\n * @example\n * const combinations = expandVariantMatrix({\n * intent: ['primary', 'danger'],\n * size: ['sm', 'md', 'lg'],\n * disabled: [true, false],\n * })\n * // → 2 × 3 × 2 = 12 kombinasi\n */\nexport function expandVariantMatrix(\n matrix: Record<string, Array<string | number | boolean>>\n): Array<Record<string, string | number | boolean>> {\n const keys = Object.keys(matrix)\n if (keys.length === 0) return [{}]\n\n const result: Array<Record<string, string | number | boolean>> = []\n\n function walk(index: number, current: Record<string, string | number | boolean>) {\n if (index >= keys.length) {\n result.push({ ...current })\n return\n }\n const key = keys[index]!\n for (const value of matrix[key] ?? []) {\n current[key] = value\n walk(index + 1, current)\n }\n }\n\n walk(0, {})\n return result\n}\n\n/**\n * Test semua kombinasi variant sekaligus — no missing coverage.\n *\n * @example\n * testAllVariants(\n * Button,\n * { intent: ['primary','danger'], size: ['sm','lg'] },\n * (el, variant) => {\n * expect(el).not.toBeNull()\n * if (variant.intent === 'primary') expectClasses(el, ['bg-blue-500'])\n * }\n * )\n */\nexport function testAllVariants(\n matrix: Record<string, Array<string | number | boolean>>,\n testFn: (variant: Record<string, string | number | boolean>) => void\n): void {\n const combinations = expandVariantMatrix(matrix)\n for (const variant of combinations) {\n testFn(variant)\n }\n}\n\n// ─── CSS-in-JS output assertions ─────────────────────────────────────────────\n\n/**\n * Bandingkan dua class string secara semantik (urutan tidak penting).\n *\n * @example\n * expectClassesEqual('px-4 py-2 bg-blue-500', 'bg-blue-500 px-4 py-2') // pass\n */\nexport function expectClassesEqual(actual: string, expected: string): void {\n const actualSet = new Set(actual.trim().split(/\\s+/).filter(Boolean))\n const expectedSet = new Set(expected.trim().split(/\\s+/).filter(Boolean))\n\n const missing = [...expectedSet].filter((c) => !actualSet.has(c))\n const extra = [...actualSet].filter((c) => !expectedSet.has(c))\n\n if (missing.length > 0 || extra.length > 0) {\n const parts: string[] = []\n if (missing.length > 0) parts.push(`Missing: ${missing.join(\", \")}`)\n if (extra.length > 0) parts.push(`Extra: ${extra.join(\", \")}`)\n throw new Error(`Class mismatch:\\n ${parts.join(\"\\n \")}`)\n }\n}\n\n// ─── Engine metrics matchers ──────────────────────────────────────────────────\n\nexport interface EngineMetricsSnapshot {\n totalFiles?: number\n uniqueClasses?: number\n buildTimeMs?: number\n cssBytes?: number\n cacheHits?: number\n cacheMisses?: number\n incrementalRuns?: number\n fullRescans?: number\n}\n\n/**\n * Assert engine metrics snapshot meets minimum thresholds.\n *\n * @example\n * const result = await engine.build()\n * expectEngineMetrics(metrics.snapshot(), {\n * totalFiles: 10,\n * buildTimeMs: 5000, // max\n * })\n */\nexport function expectEngineMetrics(\n metrics: EngineMetricsSnapshot,\n expectations: {\n minFiles?: number\n maxBuildTimeMs?: number\n minUniqueClasses?: number\n cacheHitRateMin?: number // 0–1\n }\n): void {\n if (expectations.minFiles !== undefined) {\n const actual = metrics.totalFiles ?? 0\n if (actual < expectations.minFiles) {\n throw new Error(\n `Engine metrics: expected at least ${expectations.minFiles} files, got ${actual}`\n )\n }\n }\n\n if (expectations.maxBuildTimeMs !== undefined) {\n const actual = metrics.buildTimeMs ?? 0\n if (actual > expectations.maxBuildTimeMs) {\n throw new Error(\n `Engine metrics: build took ${actual}ms, expected ≤ ${expectations.maxBuildTimeMs}ms`\n )\n }\n }\n\n if (expectations.minUniqueClasses !== undefined) {\n const actual = metrics.uniqueClasses ?? 0\n if (actual < expectations.minUniqueClasses) {\n throw new Error(\n `Engine metrics: expected at least ${expectations.minUniqueClasses} unique classes, got ${actual}`\n )\n }\n }\n\n if (expectations.cacheHitRateMin !== undefined) {\n const hits = metrics.cacheHits ?? 0\n const total = hits + (metrics.cacheMisses ?? 0)\n const rate = total > 0 ? hits / total : 0\n if (rate < expectations.cacheHitRateMin) {\n throw new Error(\n `Engine metrics: cache hit rate ${(rate * 100).toFixed(1)}%, expected ≥ ${(expectations.cacheHitRateMin * 100).toFixed(1)}%`\n )\n }\n }\n}\n\n/**\n * Custom Jest/Vitest matcher: toHaveEngineMetrics\n *\n * @example\n * expect(metrics).toHaveEngineMetrics({ minFiles: 1 })\n */\nexport function toHaveEngineMetrics(expectations: Parameters<typeof expectEngineMetrics>[1]) {\n return (metrics: EngineMetricsSnapshot) => {\n try {\n expectEngineMetrics(metrics, expectations)\n return { pass: true, message: () => \"engine metrics matched expectations\" }\n } catch (e: unknown) {\n const msg = e instanceof Error ? e.message : String(e)\n return { pass: false, message: () => msg }\n }\n }\n}\n\n/** All matchers including engine metrics */\nexport const tailwindMatchersWithMetrics = {\n toHaveClass,\n toHaveClasses,\n toNotHaveClass,\n toHaveEngineMetrics,\n}\n"]}
|
package/dist/theme.d.mts
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
export { L as LiveTokenEngineBridge, a as LiveTokenSet, T as TokenMap, b as TokenSubscriber, c as applyTokenSet, d as createUseTokens, g as generateTokenCssString, e as getToken, f as getTokens, l as liveToken, h as liveTokenEngine, s as setToken, i as setTokens, j as subscribeTokens, t as tokenRef, k as tokenVar } from './liveTokenEngine-DYN3Zale.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* tailwind-styled-v5 — Multi-Theme Engine + Live Token Engine
|
|
5
|
+
*
|
|
6
|
+
* Enterprise-grade theming. Support light/dark/brand themes dengan
|
|
7
|
+
* CSS variables. Zero runtime overhead — themes di-resolve via CSS.
|
|
8
|
+
*
|
|
9
|
+
* Live token engine provides runtime token state management with
|
|
10
|
+
* CSS variable sync to document root.
|
|
11
|
+
*
|
|
12
|
+
* Fitur:
|
|
13
|
+
* - Multiple named themes (light, dark, brand, high-contrast)
|
|
14
|
+
* - CSS variable output (Tailwind v4 compatible)
|
|
15
|
+
* - Theme contract (TypeScript-safe — missing tokens = TS error)
|
|
16
|
+
* - Per-component theme override
|
|
17
|
+
* - White-label ready
|
|
18
|
+
* - Live token engine (runtime token state + CSS sync)
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* // 1. Define contract
|
|
22
|
+
* const contract = defineThemeContract({
|
|
23
|
+
* colors: { bg: "", fg: "", primary: "", muted: "" },
|
|
24
|
+
* font: { sans: "", mono: "" },
|
|
25
|
+
* })
|
|
26
|
+
*
|
|
27
|
+
* // 2. Create themes
|
|
28
|
+
* const lightTheme = createTheme(contract, "light", {
|
|
29
|
+
* colors: { bg: "#ffffff", fg: "#09090b", primary: "#3b82f6", muted: "#71717a" },
|
|
30
|
+
* font: { sans: "InterVariable, sans-serif", mono: "JetBrains Mono, monospace" },
|
|
31
|
+
* })
|
|
32
|
+
*
|
|
33
|
+
* const darkTheme = createTheme(contract, "dark", {
|
|
34
|
+
* colors: { bg: "#09090b", fg: "#fafafa", primary: "#60a5fa", muted: "#a1a1aa" },
|
|
35
|
+
* font: { sans: "InterVariable, sans-serif", mono: "JetBrains Mono, monospace" },
|
|
36
|
+
* })
|
|
37
|
+
*
|
|
38
|
+
* // 3. Use tokens in components
|
|
39
|
+
* const Card = tw.div`bg-[var(--colors-bg)] text-[var(--colors-fg)] p-6`
|
|
40
|
+
*
|
|
41
|
+
* // 4. Apply in layout
|
|
42
|
+
* // <html data-theme="dark"> or inject CSS
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
type ThemeTokenMap = Record<string, Record<string, string>>;
|
|
46
|
+
interface ThemeContract<T extends ThemeTokenMap> {
|
|
47
|
+
_contract: T;
|
|
48
|
+
_vars: ThemeVars<T>;
|
|
49
|
+
}
|
|
50
|
+
type ThemeVars<T extends ThemeTokenMap> = {
|
|
51
|
+
[Group in keyof T]: {
|
|
52
|
+
[Token in keyof T[Group]]: string;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
interface Theme<T extends ThemeTokenMap> {
|
|
56
|
+
name: string;
|
|
57
|
+
contract: ThemeContract<T>;
|
|
58
|
+
values: T;
|
|
59
|
+
/** CSS string to inject (`:root` or `[data-theme="name"]`) */
|
|
60
|
+
css: string;
|
|
61
|
+
/** CSS variables as a flat record */
|
|
62
|
+
vars: Record<string, string>;
|
|
63
|
+
/** Apply this theme to an element via data attribute */
|
|
64
|
+
selector: string;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Define the shape of your theme. All themes must satisfy this contract.
|
|
68
|
+
* Returns typed CSS variable references for use in tw components.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* const contract = defineThemeContract({
|
|
72
|
+
* colors: { bg: "", fg: "", primary: "" },
|
|
73
|
+
* font: { sans: "" },
|
|
74
|
+
* })
|
|
75
|
+
*
|
|
76
|
+
* // Use in components:
|
|
77
|
+
* const Card = tw.div`bg-[${contract._vars.colors.bg}]`
|
|
78
|
+
* // → tw.div`bg-[var(--colors-bg)]`
|
|
79
|
+
*/
|
|
80
|
+
declare function defineThemeContract<T extends ThemeTokenMap>(shape: T): ThemeContract<T>;
|
|
81
|
+
/**
|
|
82
|
+
* Create a typed theme that satisfies a contract.
|
|
83
|
+
*
|
|
84
|
+
* @param contract - Theme contract from defineThemeContract()
|
|
85
|
+
* @param name - Theme name ("light", "dark", "brand", etc.)
|
|
86
|
+
* @param values - Token values (TypeScript enforces completeness)
|
|
87
|
+
* @param asRoot - If true, use :root selector. Default: false (uses [data-theme])
|
|
88
|
+
*/
|
|
89
|
+
declare function createTheme<T extends ThemeTokenMap>(contract: ThemeContract<T>, name: string, values: T, asRoot?: boolean): Theme<T>;
|
|
90
|
+
declare class ThemeRegistry {
|
|
91
|
+
private themes;
|
|
92
|
+
private defaultTheme;
|
|
93
|
+
/** Register a theme */
|
|
94
|
+
register<T extends ThemeTokenMap>(theme: Theme<T>, isDefault?: boolean): this;
|
|
95
|
+
/** Get a theme by name */
|
|
96
|
+
get(name: string): Theme<any> | undefined;
|
|
97
|
+
/** Get all theme names */
|
|
98
|
+
names(): string[];
|
|
99
|
+
/**
|
|
100
|
+
* Generate combined CSS for all themes.
|
|
101
|
+
* Inject into <head> or a .css file.
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* // In globals.css or layout.tsx
|
|
105
|
+
* const css = registry.generateCss()
|
|
106
|
+
*/
|
|
107
|
+
generateCss(): string;
|
|
108
|
+
/**
|
|
109
|
+
* Get the CSS for a specific theme only.
|
|
110
|
+
*/
|
|
111
|
+
getThemeCss(name: string): string | null;
|
|
112
|
+
/**
|
|
113
|
+
* Inject all theme CSS into document <head> (browser only).
|
|
114
|
+
* Call once on app init.
|
|
115
|
+
*/
|
|
116
|
+
inject(styleId?: string): void;
|
|
117
|
+
/**
|
|
118
|
+
* Switch active theme by setting data-theme on <html>.
|
|
119
|
+
*/
|
|
120
|
+
apply(name: string, target?: HTMLElement): void;
|
|
121
|
+
/**
|
|
122
|
+
* Get current active theme name from data-theme attribute.
|
|
123
|
+
*/
|
|
124
|
+
current(target?: HTMLElement): string | null;
|
|
125
|
+
}
|
|
126
|
+
interface MultiThemeConfig<T extends ThemeTokenMap> {
|
|
127
|
+
contract: ThemeContract<T>;
|
|
128
|
+
light: T;
|
|
129
|
+
dark: T;
|
|
130
|
+
/** Additional named themes (brand, high-contrast, etc.) */
|
|
131
|
+
extras?: Record<string, T>;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Create a ThemeRegistry with light/dark + optional extras in one call.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* const { registry, vars } = createMultiTheme({
|
|
138
|
+
* contract: defineThemeContract({
|
|
139
|
+
* colors: { bg: "", fg: "", primary: "", border: "" }
|
|
140
|
+
* }),
|
|
141
|
+
* light: {
|
|
142
|
+
* colors: { bg: "#fff", fg: "#09090b", primary: "#3b82f6", border: "#e5e7eb" }
|
|
143
|
+
* },
|
|
144
|
+
* dark: {
|
|
145
|
+
* colors: { bg: "#09090b", fg: "#fafafa", primary: "#60a5fa", border: "#27272a" }
|
|
146
|
+
* },
|
|
147
|
+
* })
|
|
148
|
+
*
|
|
149
|
+
* // Inject CSS:
|
|
150
|
+
* registry.inject()
|
|
151
|
+
*
|
|
152
|
+
* // Use tokens in components:
|
|
153
|
+
* const Card = tw.div`bg-[${vars.colors.bg}] text-[${vars.colors.fg}]`
|
|
154
|
+
*/
|
|
155
|
+
declare function createMultiTheme<T extends ThemeTokenMap>(config: MultiThemeConfig<T>): {
|
|
156
|
+
registry: ThemeRegistry;
|
|
157
|
+
vars: ThemeVars<T>;
|
|
158
|
+
light: Theme<T>;
|
|
159
|
+
dark: Theme<T>;
|
|
160
|
+
};
|
|
161
|
+
interface DesignTokens {
|
|
162
|
+
[path: string]: string | DesignTokens;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Flatten nested design token object into CSS variables.
|
|
166
|
+
* Supports Figma-style nested tokens.
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* compileDesignTokens({
|
|
170
|
+
* color: {
|
|
171
|
+
* brand: { primary: "#3b82f6", secondary: "#6366f1" },
|
|
172
|
+
* neutral: { 50: "#fafafa", 900: "#09090b" }
|
|
173
|
+
* },
|
|
174
|
+
* spacing: { base: "4px", lg: "16px" }
|
|
175
|
+
* })
|
|
176
|
+
* →
|
|
177
|
+
* :root {
|
|
178
|
+
* --color-brand-primary: #3b82f6;
|
|
179
|
+
* --color-brand-secondary: #6366f1;
|
|
180
|
+
* --color-neutral-50: #fafafa;
|
|
181
|
+
* --color-neutral-900: #09090b;
|
|
182
|
+
* --spacing-base: 4px;
|
|
183
|
+
* --spacing-lg: 16px;
|
|
184
|
+
* }
|
|
185
|
+
*/
|
|
186
|
+
declare function compileDesignTokens(tokens: DesignTokens, prefix?: string): string;
|
|
187
|
+
|
|
188
|
+
export { type DesignTokens, type MultiThemeConfig, type Theme, type ThemeContract, ThemeRegistry, type ThemeTokenMap, type ThemeVars, compileDesignTokens, createMultiTheme, createTheme, defineThemeContract };
|
package/dist/theme.d.ts
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
export { L as LiveTokenEngineBridge, a as LiveTokenSet, T as TokenMap, b as TokenSubscriber, c as applyTokenSet, d as createUseTokens, g as generateTokenCssString, e as getToken, f as getTokens, l as liveToken, h as liveTokenEngine, s as setToken, i as setTokens, j as subscribeTokens, t as tokenRef, k as tokenVar } from './liveTokenEngine-DYN3Zale.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* tailwind-styled-v5 — Multi-Theme Engine + Live Token Engine
|
|
5
|
+
*
|
|
6
|
+
* Enterprise-grade theming. Support light/dark/brand themes dengan
|
|
7
|
+
* CSS variables. Zero runtime overhead — themes di-resolve via CSS.
|
|
8
|
+
*
|
|
9
|
+
* Live token engine provides runtime token state management with
|
|
10
|
+
* CSS variable sync to document root.
|
|
11
|
+
*
|
|
12
|
+
* Fitur:
|
|
13
|
+
* - Multiple named themes (light, dark, brand, high-contrast)
|
|
14
|
+
* - CSS variable output (Tailwind v4 compatible)
|
|
15
|
+
* - Theme contract (TypeScript-safe — missing tokens = TS error)
|
|
16
|
+
* - Per-component theme override
|
|
17
|
+
* - White-label ready
|
|
18
|
+
* - Live token engine (runtime token state + CSS sync)
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* // 1. Define contract
|
|
22
|
+
* const contract = defineThemeContract({
|
|
23
|
+
* colors: { bg: "", fg: "", primary: "", muted: "" },
|
|
24
|
+
* font: { sans: "", mono: "" },
|
|
25
|
+
* })
|
|
26
|
+
*
|
|
27
|
+
* // 2. Create themes
|
|
28
|
+
* const lightTheme = createTheme(contract, "light", {
|
|
29
|
+
* colors: { bg: "#ffffff", fg: "#09090b", primary: "#3b82f6", muted: "#71717a" },
|
|
30
|
+
* font: { sans: "InterVariable, sans-serif", mono: "JetBrains Mono, monospace" },
|
|
31
|
+
* })
|
|
32
|
+
*
|
|
33
|
+
* const darkTheme = createTheme(contract, "dark", {
|
|
34
|
+
* colors: { bg: "#09090b", fg: "#fafafa", primary: "#60a5fa", muted: "#a1a1aa" },
|
|
35
|
+
* font: { sans: "InterVariable, sans-serif", mono: "JetBrains Mono, monospace" },
|
|
36
|
+
* })
|
|
37
|
+
*
|
|
38
|
+
* // 3. Use tokens in components
|
|
39
|
+
* const Card = tw.div`bg-[var(--colors-bg)] text-[var(--colors-fg)] p-6`
|
|
40
|
+
*
|
|
41
|
+
* // 4. Apply in layout
|
|
42
|
+
* // <html data-theme="dark"> or inject CSS
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
type ThemeTokenMap = Record<string, Record<string, string>>;
|
|
46
|
+
interface ThemeContract<T extends ThemeTokenMap> {
|
|
47
|
+
_contract: T;
|
|
48
|
+
_vars: ThemeVars<T>;
|
|
49
|
+
}
|
|
50
|
+
type ThemeVars<T extends ThemeTokenMap> = {
|
|
51
|
+
[Group in keyof T]: {
|
|
52
|
+
[Token in keyof T[Group]]: string;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
interface Theme<T extends ThemeTokenMap> {
|
|
56
|
+
name: string;
|
|
57
|
+
contract: ThemeContract<T>;
|
|
58
|
+
values: T;
|
|
59
|
+
/** CSS string to inject (`:root` or `[data-theme="name"]`) */
|
|
60
|
+
css: string;
|
|
61
|
+
/** CSS variables as a flat record */
|
|
62
|
+
vars: Record<string, string>;
|
|
63
|
+
/** Apply this theme to an element via data attribute */
|
|
64
|
+
selector: string;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Define the shape of your theme. All themes must satisfy this contract.
|
|
68
|
+
* Returns typed CSS variable references for use in tw components.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* const contract = defineThemeContract({
|
|
72
|
+
* colors: { bg: "", fg: "", primary: "" },
|
|
73
|
+
* font: { sans: "" },
|
|
74
|
+
* })
|
|
75
|
+
*
|
|
76
|
+
* // Use in components:
|
|
77
|
+
* const Card = tw.div`bg-[${contract._vars.colors.bg}]`
|
|
78
|
+
* // → tw.div`bg-[var(--colors-bg)]`
|
|
79
|
+
*/
|
|
80
|
+
declare function defineThemeContract<T extends ThemeTokenMap>(shape: T): ThemeContract<T>;
|
|
81
|
+
/**
|
|
82
|
+
* Create a typed theme that satisfies a contract.
|
|
83
|
+
*
|
|
84
|
+
* @param contract - Theme contract from defineThemeContract()
|
|
85
|
+
* @param name - Theme name ("light", "dark", "brand", etc.)
|
|
86
|
+
* @param values - Token values (TypeScript enforces completeness)
|
|
87
|
+
* @param asRoot - If true, use :root selector. Default: false (uses [data-theme])
|
|
88
|
+
*/
|
|
89
|
+
declare function createTheme<T extends ThemeTokenMap>(contract: ThemeContract<T>, name: string, values: T, asRoot?: boolean): Theme<T>;
|
|
90
|
+
declare class ThemeRegistry {
|
|
91
|
+
private themes;
|
|
92
|
+
private defaultTheme;
|
|
93
|
+
/** Register a theme */
|
|
94
|
+
register<T extends ThemeTokenMap>(theme: Theme<T>, isDefault?: boolean): this;
|
|
95
|
+
/** Get a theme by name */
|
|
96
|
+
get(name: string): Theme<any> | undefined;
|
|
97
|
+
/** Get all theme names */
|
|
98
|
+
names(): string[];
|
|
99
|
+
/**
|
|
100
|
+
* Generate combined CSS for all themes.
|
|
101
|
+
* Inject into <head> or a .css file.
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* // In globals.css or layout.tsx
|
|
105
|
+
* const css = registry.generateCss()
|
|
106
|
+
*/
|
|
107
|
+
generateCss(): string;
|
|
108
|
+
/**
|
|
109
|
+
* Get the CSS for a specific theme only.
|
|
110
|
+
*/
|
|
111
|
+
getThemeCss(name: string): string | null;
|
|
112
|
+
/**
|
|
113
|
+
* Inject all theme CSS into document <head> (browser only).
|
|
114
|
+
* Call once on app init.
|
|
115
|
+
*/
|
|
116
|
+
inject(styleId?: string): void;
|
|
117
|
+
/**
|
|
118
|
+
* Switch active theme by setting data-theme on <html>.
|
|
119
|
+
*/
|
|
120
|
+
apply(name: string, target?: HTMLElement): void;
|
|
121
|
+
/**
|
|
122
|
+
* Get current active theme name from data-theme attribute.
|
|
123
|
+
*/
|
|
124
|
+
current(target?: HTMLElement): string | null;
|
|
125
|
+
}
|
|
126
|
+
interface MultiThemeConfig<T extends ThemeTokenMap> {
|
|
127
|
+
contract: ThemeContract<T>;
|
|
128
|
+
light: T;
|
|
129
|
+
dark: T;
|
|
130
|
+
/** Additional named themes (brand, high-contrast, etc.) */
|
|
131
|
+
extras?: Record<string, T>;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Create a ThemeRegistry with light/dark + optional extras in one call.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* const { registry, vars } = createMultiTheme({
|
|
138
|
+
* contract: defineThemeContract({
|
|
139
|
+
* colors: { bg: "", fg: "", primary: "", border: "" }
|
|
140
|
+
* }),
|
|
141
|
+
* light: {
|
|
142
|
+
* colors: { bg: "#fff", fg: "#09090b", primary: "#3b82f6", border: "#e5e7eb" }
|
|
143
|
+
* },
|
|
144
|
+
* dark: {
|
|
145
|
+
* colors: { bg: "#09090b", fg: "#fafafa", primary: "#60a5fa", border: "#27272a" }
|
|
146
|
+
* },
|
|
147
|
+
* })
|
|
148
|
+
*
|
|
149
|
+
* // Inject CSS:
|
|
150
|
+
* registry.inject()
|
|
151
|
+
*
|
|
152
|
+
* // Use tokens in components:
|
|
153
|
+
* const Card = tw.div`bg-[${vars.colors.bg}] text-[${vars.colors.fg}]`
|
|
154
|
+
*/
|
|
155
|
+
declare function createMultiTheme<T extends ThemeTokenMap>(config: MultiThemeConfig<T>): {
|
|
156
|
+
registry: ThemeRegistry;
|
|
157
|
+
vars: ThemeVars<T>;
|
|
158
|
+
light: Theme<T>;
|
|
159
|
+
dark: Theme<T>;
|
|
160
|
+
};
|
|
161
|
+
interface DesignTokens {
|
|
162
|
+
[path: string]: string | DesignTokens;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Flatten nested design token object into CSS variables.
|
|
166
|
+
* Supports Figma-style nested tokens.
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* compileDesignTokens({
|
|
170
|
+
* color: {
|
|
171
|
+
* brand: { primary: "#3b82f6", secondary: "#6366f1" },
|
|
172
|
+
* neutral: { 50: "#fafafa", 900: "#09090b" }
|
|
173
|
+
* },
|
|
174
|
+
* spacing: { base: "4px", lg: "16px" }
|
|
175
|
+
* })
|
|
176
|
+
* →
|
|
177
|
+
* :root {
|
|
178
|
+
* --color-brand-primary: #3b82f6;
|
|
179
|
+
* --color-brand-secondary: #6366f1;
|
|
180
|
+
* --color-neutral-50: #fafafa;
|
|
181
|
+
* --color-neutral-900: #09090b;
|
|
182
|
+
* --spacing-base: 4px;
|
|
183
|
+
* --spacing-lg: 16px;
|
|
184
|
+
* }
|
|
185
|
+
*/
|
|
186
|
+
declare function compileDesignTokens(tokens: DesignTokens, prefix?: string): string;
|
|
187
|
+
|
|
188
|
+
export { type DesignTokens, type MultiThemeConfig, type Theme, type ThemeContract, ThemeRegistry, type ThemeTokenMap, type ThemeVars, compileDesignTokens, createMultiTheme, createTheme, defineThemeContract };
|