tailwind-styled-v4 4.0.0 → 5.0.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/animate.cjs +754 -235
- package/dist/animate.cjs.map +1 -1
- package/dist/animate.d.cts +55 -99
- package/dist/animate.d.ts +55 -99
- package/dist/animate.js +742 -235
- package/dist/animate.js.map +1 -1
- package/dist/chunk-VZEJV27B.js +11 -0
- package/dist/chunk-VZEJV27B.js.map +1 -0
- package/dist/chunk-Y5D3E72P.cjs +13 -0
- package/dist/chunk-Y5D3E72P.cjs.map +1 -0
- package/dist/css.cjs +61 -11
- package/dist/css.cjs.map +1 -1
- package/dist/css.d.cts +3 -18
- package/dist/css.d.ts +3 -18
- package/dist/css.js +61 -11
- package/dist/css.js.map +1 -1
- package/dist/devtools.cjs +200 -88
- package/dist/devtools.cjs.map +1 -1
- package/dist/devtools.js +200 -88
- package/dist/devtools.js.map +1 -1
- package/dist/index.cjs +430 -135
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +74 -3
- package/dist/index.d.ts +74 -3
- package/dist/index.js +415 -132
- package/dist/index.js.map +1 -1
- package/dist/next.cjs +118 -138
- package/dist/next.cjs.map +1 -1
- package/dist/next.d.cts +28 -19
- package/dist/next.d.ts +28 -19
- package/dist/next.js +111 -131
- package/dist/next.js.map +1 -1
- package/dist/preset.cjs +312 -18
- package/dist/preset.cjs.map +1 -1
- package/dist/preset.d.cts +29 -2
- package/dist/preset.d.ts +29 -2
- package/dist/preset.js +311 -19
- package/dist/preset.js.map +1 -1
- package/dist/turbopackLoader.cjs +24 -2676
- package/dist/turbopackLoader.cjs.map +1 -1
- package/dist/turbopackLoader.d.cts +3 -13
- package/dist/turbopackLoader.d.ts +3 -13
- package/dist/turbopackLoader.js +24 -2670
- package/dist/turbopackLoader.js.map +1 -1
- package/dist/vite.cjs +90 -57
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.d.cts +35 -6
- package/dist/vite.d.ts +35 -6
- package/dist/vite.js +90 -58
- package/dist/vite.js.map +1 -1
- package/dist/webpackLoader.cjs +27 -2646
- package/dist/webpackLoader.cjs.map +1 -1
- package/dist/webpackLoader.d.cts +3 -10
- package/dist/webpackLoader.d.ts +3 -10
- package/dist/webpackLoader.js +27 -2640
- package/dist/webpackLoader.js.map +1 -1
- package/package.json +31 -28
- package/dist/astTransform-ua-eapqs.d.cts +0 -41
- package/dist/astTransform-ua-eapqs.d.ts +0 -41
- package/dist/compiler.cjs +0 -3594
- package/dist/compiler.cjs.map +0 -1
- package/dist/compiler.d.cts +0 -716
- package/dist/compiler.d.ts +0 -716
- package/dist/compiler.js +0 -3535
- package/dist/compiler.js.map +0 -1
- package/dist/plugins.cjs +0 -396
- package/dist/plugins.cjs.map +0 -1
- package/dist/plugins.d.cts +0 -231
- package/dist/plugins.d.ts +0 -231
- package/dist/plugins.js +0 -381
- package/dist/plugins.js.map +0 -1
- package/dist/theme.cjs +0 -154
- package/dist/theme.cjs.map +0 -1
- package/dist/theme.d.cts +0 -181
- package/dist/theme.d.ts +0 -181
- package/dist/theme.js +0 -148
- package/dist/theme.js.map +0 -1
package/dist/theme.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../theme/src/index.ts"],"names":[],"mappings":"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","file":"theme.cjs","sourcesContent":["/**\n * tailwind-styled-v4 — Multi-Theme Engine\n *\n * Enterprise-grade theming. Support light/dark/brand themes dengan\n * CSS variables. Zero runtime overhead — themes di-resolve via CSS.\n *\n * Fitur:\n * - Multiple named themes (light, dark, brand, high-contrast)\n * - CSS variable output (Tailwind v4 compatible)\n * - Theme contract (TypeScript-safe — missing tokens = TS error)\n * - Per-component theme override\n * - White-label ready\n *\n * @example\n * // 1. Define contract\n * const contract = defineThemeContract({\n * colors: { bg: \"\", fg: \"\", primary: \"\", muted: \"\" },\n * font: { sans: \"\", mono: \"\" },\n * })\n *\n * // 2. Create themes\n * const lightTheme = createTheme(contract, \"light\", {\n * colors: { bg: \"#ffffff\", fg: \"#09090b\", primary: \"#3b82f6\", muted: \"#71717a\" },\n * font: { sans: \"InterVariable, sans-serif\", mono: \"JetBrains Mono, monospace\" },\n * })\n *\n * const darkTheme = createTheme(contract, \"dark\", {\n * colors: { bg: \"#09090b\", fg: \"#fafafa\", primary: \"#60a5fa\", muted: \"#a1a1aa\" },\n * font: { sans: \"InterVariable, sans-serif\", mono: \"JetBrains Mono, monospace\" },\n * })\n *\n * // 3. Use tokens in components\n * const Card = tw.div`bg-[var(--colors-bg)] text-[var(--colors-fg)] p-6`\n *\n * // 4. Apply in layout\n * // <html data-theme=\"dark\"> or inject CSS\n */\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type TokenMap = Record<string, Record<string, string>>\n\nexport interface ThemeContract<T extends TokenMap> {\n _contract: T\n _vars: ThemeVars<T>\n}\n\nexport type ThemeVars<T extends TokenMap> = {\n [Group in keyof T]: {\n [Token in keyof T[Group]]: string // \"var(--group-token)\"\n }\n}\n\nexport interface Theme<T extends TokenMap> {\n name: string\n contract: ThemeContract<T>\n values: T\n /** CSS string to inject (`:root` or `[data-theme=\"name\"]`) */\n css: string\n /** CSS variables as a flat record */\n vars: Record<string, string>\n /** Apply this theme to an element via data attribute */\n selector: string\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// defineThemeContract\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Define the shape of your theme. All themes must satisfy this contract.\n * Returns typed CSS variable references for use in tw components.\n *\n * @example\n * const contract = defineThemeContract({\n * colors: { bg: \"\", fg: \"\", primary: \"\" },\n * font: { sans: \"\" },\n * })\n *\n * // Use in components:\n * const Card = tw.div`bg-[${contract._vars.colors.bg}]`\n * // → tw.div`bg-[var(--colors-bg)]`\n */\nexport function defineThemeContract<T extends TokenMap>(shape: T): ThemeContract<T> {\n const vars = {} as ThemeVars<T>\n\n for (const group in shape) {\n ;(vars as any)[group] = {}\n for (const token in shape[group]) {\n ;(vars as any)[group][token] = `var(--${group}-${token})`\n }\n }\n\n return { _contract: shape, _vars: vars }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// createTheme\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Create a typed theme that satisfies a contract.\n *\n * @param contract - Theme contract from defineThemeContract()\n * @param name - Theme name (\"light\", \"dark\", \"brand\", etc.)\n * @param values - Token values (TypeScript enforces completeness)\n * @param asRoot - If true, use :root selector. Default: false (uses [data-theme])\n */\nexport function createTheme<T extends TokenMap>(\n contract: ThemeContract<T>,\n name: string,\n values: T,\n asRoot = false\n): Theme<T> {\n const flatVars: Record<string, string> = {}\n const cssLines: string[] = []\n\n for (const group in values) {\n for (const token in values[group]) {\n const varName = `--${group}-${token}`\n const value = values[group][token]\n flatVars[varName] = value\n cssLines.push(` ${varName}: ${value};`)\n }\n }\n\n const selector = asRoot ? \":root\" : `[data-theme=\"${name}\"]`\n const css = `${selector} {\\n${cssLines.join(\"\\n\")}\\n}`\n\n return {\n name,\n contract,\n values,\n css,\n vars: flatVars,\n selector,\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ThemeRegistry — manage multiple themes\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport class ThemeRegistry {\n private themes = new Map<string, Theme<any>>()\n private defaultTheme: string | null = null\n\n /** Register a theme */\n register<T extends TokenMap>(theme: Theme<T>, isDefault = false): this {\n this.themes.set(theme.name, theme)\n if (isDefault || !this.defaultTheme) {\n this.defaultTheme = theme.name\n }\n return this\n }\n\n /** Get a theme by name */\n get(name: string): Theme<any> | undefined {\n return this.themes.get(name)\n }\n\n /** Get all theme names */\n names(): string[] {\n return Array.from(this.themes.keys())\n }\n\n /**\n * Generate combined CSS for all themes.\n * Inject into <head> or a .css file.\n *\n * @example\n * // In globals.css or layout.tsx\n * const css = registry.generateCss()\n */\n generateCss(): string {\n return Array.from(this.themes.values())\n .map((t) => t.css)\n .join(\"\\n\\n\")\n }\n\n /**\n * Get the CSS for a specific theme only.\n */\n getThemeCss(name: string): string | null {\n return this.themes.get(name)?.css ?? null\n }\n\n /**\n * Inject all theme CSS into document <head> (browser only).\n * Call once on app init.\n */\n inject(styleId = \"__tw_themes\"): void {\n if (typeof document === \"undefined\") return\n\n let style = document.getElementById(styleId) as HTMLStyleElement | null\n if (!style) {\n style = document.createElement(\"style\")\n style.id = styleId\n document.head.appendChild(style)\n }\n style.textContent = this.generateCss()\n }\n\n /**\n * Switch active theme by setting data-theme on <html>.\n */\n apply(name: string, target: HTMLElement = document.documentElement): void {\n if (typeof document === \"undefined\") return\n if (!this.themes.has(name)) {\n console.warn(`[tailwind-styled-v4] Theme \"${name}\" not registered.`)\n return\n }\n target.dataset.theme = name\n }\n\n /**\n * Get current active theme name from data-theme attribute.\n */\n current(target: HTMLElement = document.documentElement): string | null {\n if (typeof document === \"undefined\") return this.defaultTheme\n return target.dataset.theme ?? this.defaultTheme\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Convenience: createMultiTheme — shorthand for common light/dark setup\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface MultiThemeConfig<T extends TokenMap> {\n contract: ThemeContract<T>\n light: T\n dark: T\n /** Additional named themes (brand, high-contrast, etc.) */\n extras?: Record<string, T>\n}\n\n/**\n * Create a ThemeRegistry with light/dark + optional extras in one call.\n *\n * @example\n * const { registry, vars } = createMultiTheme({\n * contract: defineThemeContract({\n * colors: { bg: \"\", fg: \"\", primary: \"\", border: \"\" }\n * }),\n * light: {\n * colors: { bg: \"#fff\", fg: \"#09090b\", primary: \"#3b82f6\", border: \"#e5e7eb\" }\n * },\n * dark: {\n * colors: { bg: \"#09090b\", fg: \"#fafafa\", primary: \"#60a5fa\", border: \"#27272a\" }\n * },\n * })\n *\n * // Inject CSS:\n * registry.inject()\n *\n * // Use tokens in components:\n * const Card = tw.div`bg-[${vars.colors.bg}] text-[${vars.colors.fg}]`\n */\nexport function createMultiTheme<T extends TokenMap>(\n config: MultiThemeConfig<T>\n): {\n registry: ThemeRegistry\n vars: ThemeVars<T>\n light: Theme<T>\n dark: Theme<T>\n} {\n const registry = new ThemeRegistry()\n\n const light = createTheme(config.contract, \"light\", config.light, true) // :root\n const dark = createTheme(config.contract, \"dark\", config.dark, false)\n\n registry.register(light, true)\n registry.register(dark)\n\n for (const [name, values] of Object.entries(config.extras ?? {})) {\n registry.register(createTheme(config.contract, name, values as T))\n }\n\n return {\n registry,\n vars: config.contract._vars,\n light,\n dark,\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Design Token Compiler — generate CSS vars from token object\n// (Enterprise feature: sync with Figma variables)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface DesignTokens {\n [path: string]: string | DesignTokens\n}\n\n/**\n * Flatten nested design token object into CSS variables.\n * Supports Figma-style nested tokens.\n *\n * @example\n * compileDesignTokens({\n * color: {\n * brand: { primary: \"#3b82f6\", secondary: \"#6366f1\" },\n * neutral: { 50: \"#fafafa\", 900: \"#09090b\" }\n * },\n * spacing: { base: \"4px\", lg: \"16px\" }\n * })\n * →\n * :root {\n * --color-brand-primary: #3b82f6;\n * --color-brand-secondary: #6366f1;\n * --color-neutral-50: #fafafa;\n * --color-neutral-900: #09090b;\n * --spacing-base: 4px;\n * --spacing-lg: 16px;\n * }\n */\nexport function compileDesignTokens(tokens: DesignTokens, prefix = \"\"): string {\n const vars: string[] = []\n\n function flatten(obj: DesignTokens, path: string) {\n for (const [key, value] of Object.entries(obj)) {\n const varPath = path ? `${path}-${key}` : key\n if (typeof value === \"string\") {\n vars.push(` --${varPath}: ${value};`)\n } else {\n flatten(value as DesignTokens, varPath)\n }\n }\n }\n\n flatten(tokens, prefix)\n return `:root {\\n${vars.join(\"\\n\")}\\n}`\n}\n"]}
|
package/dist/theme.d.cts
DELETED
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* tailwind-styled-v4 — Multi-Theme Engine
|
|
3
|
-
*
|
|
4
|
-
* Enterprise-grade theming. Support light/dark/brand themes dengan
|
|
5
|
-
* CSS variables. Zero runtime overhead — themes di-resolve via CSS.
|
|
6
|
-
*
|
|
7
|
-
* Fitur:
|
|
8
|
-
* - Multiple named themes (light, dark, brand, high-contrast)
|
|
9
|
-
* - CSS variable output (Tailwind v4 compatible)
|
|
10
|
-
* - Theme contract (TypeScript-safe — missing tokens = TS error)
|
|
11
|
-
* - Per-component theme override
|
|
12
|
-
* - White-label ready
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* // 1. Define contract
|
|
16
|
-
* const contract = defineThemeContract({
|
|
17
|
-
* colors: { bg: "", fg: "", primary: "", muted: "" },
|
|
18
|
-
* font: { sans: "", mono: "" },
|
|
19
|
-
* })
|
|
20
|
-
*
|
|
21
|
-
* // 2. Create themes
|
|
22
|
-
* const lightTheme = createTheme(contract, "light", {
|
|
23
|
-
* colors: { bg: "#ffffff", fg: "#09090b", primary: "#3b82f6", muted: "#71717a" },
|
|
24
|
-
* font: { sans: "InterVariable, sans-serif", mono: "JetBrains Mono, monospace" },
|
|
25
|
-
* })
|
|
26
|
-
*
|
|
27
|
-
* const darkTheme = createTheme(contract, "dark", {
|
|
28
|
-
* colors: { bg: "#09090b", fg: "#fafafa", primary: "#60a5fa", muted: "#a1a1aa" },
|
|
29
|
-
* font: { sans: "InterVariable, sans-serif", mono: "JetBrains Mono, monospace" },
|
|
30
|
-
* })
|
|
31
|
-
*
|
|
32
|
-
* // 3. Use tokens in components
|
|
33
|
-
* const Card = tw.div`bg-[var(--colors-bg)] text-[var(--colors-fg)] p-6`
|
|
34
|
-
*
|
|
35
|
-
* // 4. Apply in layout
|
|
36
|
-
* // <html data-theme="dark"> or inject CSS
|
|
37
|
-
*/
|
|
38
|
-
type TokenMap = Record<string, Record<string, string>>;
|
|
39
|
-
interface ThemeContract<T extends TokenMap> {
|
|
40
|
-
_contract: T;
|
|
41
|
-
_vars: ThemeVars<T>;
|
|
42
|
-
}
|
|
43
|
-
type ThemeVars<T extends TokenMap> = {
|
|
44
|
-
[Group in keyof T]: {
|
|
45
|
-
[Token in keyof T[Group]]: string;
|
|
46
|
-
};
|
|
47
|
-
};
|
|
48
|
-
interface Theme<T extends TokenMap> {
|
|
49
|
-
name: string;
|
|
50
|
-
contract: ThemeContract<T>;
|
|
51
|
-
values: T;
|
|
52
|
-
/** CSS string to inject (`:root` or `[data-theme="name"]`) */
|
|
53
|
-
css: string;
|
|
54
|
-
/** CSS variables as a flat record */
|
|
55
|
-
vars: Record<string, string>;
|
|
56
|
-
/** Apply this theme to an element via data attribute */
|
|
57
|
-
selector: string;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Define the shape of your theme. All themes must satisfy this contract.
|
|
61
|
-
* Returns typed CSS variable references for use in tw components.
|
|
62
|
-
*
|
|
63
|
-
* @example
|
|
64
|
-
* const contract = defineThemeContract({
|
|
65
|
-
* colors: { bg: "", fg: "", primary: "" },
|
|
66
|
-
* font: { sans: "" },
|
|
67
|
-
* })
|
|
68
|
-
*
|
|
69
|
-
* // Use in components:
|
|
70
|
-
* const Card = tw.div`bg-[${contract._vars.colors.bg}]`
|
|
71
|
-
* // → tw.div`bg-[var(--colors-bg)]`
|
|
72
|
-
*/
|
|
73
|
-
declare function defineThemeContract<T extends TokenMap>(shape: T): ThemeContract<T>;
|
|
74
|
-
/**
|
|
75
|
-
* Create a typed theme that satisfies a contract.
|
|
76
|
-
*
|
|
77
|
-
* @param contract - Theme contract from defineThemeContract()
|
|
78
|
-
* @param name - Theme name ("light", "dark", "brand", etc.)
|
|
79
|
-
* @param values - Token values (TypeScript enforces completeness)
|
|
80
|
-
* @param asRoot - If true, use :root selector. Default: false (uses [data-theme])
|
|
81
|
-
*/
|
|
82
|
-
declare function createTheme<T extends TokenMap>(contract: ThemeContract<T>, name: string, values: T, asRoot?: boolean): Theme<T>;
|
|
83
|
-
declare class ThemeRegistry {
|
|
84
|
-
private themes;
|
|
85
|
-
private defaultTheme;
|
|
86
|
-
/** Register a theme */
|
|
87
|
-
register<T extends TokenMap>(theme: Theme<T>, isDefault?: boolean): this;
|
|
88
|
-
/** Get a theme by name */
|
|
89
|
-
get(name: string): Theme<any> | undefined;
|
|
90
|
-
/** Get all theme names */
|
|
91
|
-
names(): string[];
|
|
92
|
-
/**
|
|
93
|
-
* Generate combined CSS for all themes.
|
|
94
|
-
* Inject into <head> or a .css file.
|
|
95
|
-
*
|
|
96
|
-
* @example
|
|
97
|
-
* // In globals.css or layout.tsx
|
|
98
|
-
* const css = registry.generateCss()
|
|
99
|
-
*/
|
|
100
|
-
generateCss(): string;
|
|
101
|
-
/**
|
|
102
|
-
* Get the CSS for a specific theme only.
|
|
103
|
-
*/
|
|
104
|
-
getThemeCss(name: string): string | null;
|
|
105
|
-
/**
|
|
106
|
-
* Inject all theme CSS into document <head> (browser only).
|
|
107
|
-
* Call once on app init.
|
|
108
|
-
*/
|
|
109
|
-
inject(styleId?: string): void;
|
|
110
|
-
/**
|
|
111
|
-
* Switch active theme by setting data-theme on <html>.
|
|
112
|
-
*/
|
|
113
|
-
apply(name: string, target?: HTMLElement): void;
|
|
114
|
-
/**
|
|
115
|
-
* Get current active theme name from data-theme attribute.
|
|
116
|
-
*/
|
|
117
|
-
current(target?: HTMLElement): string | null;
|
|
118
|
-
}
|
|
119
|
-
interface MultiThemeConfig<T extends TokenMap> {
|
|
120
|
-
contract: ThemeContract<T>;
|
|
121
|
-
light: T;
|
|
122
|
-
dark: T;
|
|
123
|
-
/** Additional named themes (brand, high-contrast, etc.) */
|
|
124
|
-
extras?: Record<string, T>;
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Create a ThemeRegistry with light/dark + optional extras in one call.
|
|
128
|
-
*
|
|
129
|
-
* @example
|
|
130
|
-
* const { registry, vars } = createMultiTheme({
|
|
131
|
-
* contract: defineThemeContract({
|
|
132
|
-
* colors: { bg: "", fg: "", primary: "", border: "" }
|
|
133
|
-
* }),
|
|
134
|
-
* light: {
|
|
135
|
-
* colors: { bg: "#fff", fg: "#09090b", primary: "#3b82f6", border: "#e5e7eb" }
|
|
136
|
-
* },
|
|
137
|
-
* dark: {
|
|
138
|
-
* colors: { bg: "#09090b", fg: "#fafafa", primary: "#60a5fa", border: "#27272a" }
|
|
139
|
-
* },
|
|
140
|
-
* })
|
|
141
|
-
*
|
|
142
|
-
* // Inject CSS:
|
|
143
|
-
* registry.inject()
|
|
144
|
-
*
|
|
145
|
-
* // Use tokens in components:
|
|
146
|
-
* const Card = tw.div`bg-[${vars.colors.bg}] text-[${vars.colors.fg}]`
|
|
147
|
-
*/
|
|
148
|
-
declare function createMultiTheme<T extends TokenMap>(config: MultiThemeConfig<T>): {
|
|
149
|
-
registry: ThemeRegistry;
|
|
150
|
-
vars: ThemeVars<T>;
|
|
151
|
-
light: Theme<T>;
|
|
152
|
-
dark: Theme<T>;
|
|
153
|
-
};
|
|
154
|
-
interface DesignTokens {
|
|
155
|
-
[path: string]: string | DesignTokens;
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* Flatten nested design token object into CSS variables.
|
|
159
|
-
* Supports Figma-style nested tokens.
|
|
160
|
-
*
|
|
161
|
-
* @example
|
|
162
|
-
* compileDesignTokens({
|
|
163
|
-
* color: {
|
|
164
|
-
* brand: { primary: "#3b82f6", secondary: "#6366f1" },
|
|
165
|
-
* neutral: { 50: "#fafafa", 900: "#09090b" }
|
|
166
|
-
* },
|
|
167
|
-
* spacing: { base: "4px", lg: "16px" }
|
|
168
|
-
* })
|
|
169
|
-
* →
|
|
170
|
-
* :root {
|
|
171
|
-
* --color-brand-primary: #3b82f6;
|
|
172
|
-
* --color-brand-secondary: #6366f1;
|
|
173
|
-
* --color-neutral-50: #fafafa;
|
|
174
|
-
* --color-neutral-900: #09090b;
|
|
175
|
-
* --spacing-base: 4px;
|
|
176
|
-
* --spacing-lg: 16px;
|
|
177
|
-
* }
|
|
178
|
-
*/
|
|
179
|
-
declare function compileDesignTokens(tokens: DesignTokens, prefix?: string): string;
|
|
180
|
-
|
|
181
|
-
export { type DesignTokens, type MultiThemeConfig, type Theme, type ThemeContract, ThemeRegistry, type ThemeVars, type TokenMap, compileDesignTokens, createMultiTheme, createTheme, defineThemeContract };
|
package/dist/theme.d.ts
DELETED
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* tailwind-styled-v4 — Multi-Theme Engine
|
|
3
|
-
*
|
|
4
|
-
* Enterprise-grade theming. Support light/dark/brand themes dengan
|
|
5
|
-
* CSS variables. Zero runtime overhead — themes di-resolve via CSS.
|
|
6
|
-
*
|
|
7
|
-
* Fitur:
|
|
8
|
-
* - Multiple named themes (light, dark, brand, high-contrast)
|
|
9
|
-
* - CSS variable output (Tailwind v4 compatible)
|
|
10
|
-
* - Theme contract (TypeScript-safe — missing tokens = TS error)
|
|
11
|
-
* - Per-component theme override
|
|
12
|
-
* - White-label ready
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* // 1. Define contract
|
|
16
|
-
* const contract = defineThemeContract({
|
|
17
|
-
* colors: { bg: "", fg: "", primary: "", muted: "" },
|
|
18
|
-
* font: { sans: "", mono: "" },
|
|
19
|
-
* })
|
|
20
|
-
*
|
|
21
|
-
* // 2. Create themes
|
|
22
|
-
* const lightTheme = createTheme(contract, "light", {
|
|
23
|
-
* colors: { bg: "#ffffff", fg: "#09090b", primary: "#3b82f6", muted: "#71717a" },
|
|
24
|
-
* font: { sans: "InterVariable, sans-serif", mono: "JetBrains Mono, monospace" },
|
|
25
|
-
* })
|
|
26
|
-
*
|
|
27
|
-
* const darkTheme = createTheme(contract, "dark", {
|
|
28
|
-
* colors: { bg: "#09090b", fg: "#fafafa", primary: "#60a5fa", muted: "#a1a1aa" },
|
|
29
|
-
* font: { sans: "InterVariable, sans-serif", mono: "JetBrains Mono, monospace" },
|
|
30
|
-
* })
|
|
31
|
-
*
|
|
32
|
-
* // 3. Use tokens in components
|
|
33
|
-
* const Card = tw.div`bg-[var(--colors-bg)] text-[var(--colors-fg)] p-6`
|
|
34
|
-
*
|
|
35
|
-
* // 4. Apply in layout
|
|
36
|
-
* // <html data-theme="dark"> or inject CSS
|
|
37
|
-
*/
|
|
38
|
-
type TokenMap = Record<string, Record<string, string>>;
|
|
39
|
-
interface ThemeContract<T extends TokenMap> {
|
|
40
|
-
_contract: T;
|
|
41
|
-
_vars: ThemeVars<T>;
|
|
42
|
-
}
|
|
43
|
-
type ThemeVars<T extends TokenMap> = {
|
|
44
|
-
[Group in keyof T]: {
|
|
45
|
-
[Token in keyof T[Group]]: string;
|
|
46
|
-
};
|
|
47
|
-
};
|
|
48
|
-
interface Theme<T extends TokenMap> {
|
|
49
|
-
name: string;
|
|
50
|
-
contract: ThemeContract<T>;
|
|
51
|
-
values: T;
|
|
52
|
-
/** CSS string to inject (`:root` or `[data-theme="name"]`) */
|
|
53
|
-
css: string;
|
|
54
|
-
/** CSS variables as a flat record */
|
|
55
|
-
vars: Record<string, string>;
|
|
56
|
-
/** Apply this theme to an element via data attribute */
|
|
57
|
-
selector: string;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Define the shape of your theme. All themes must satisfy this contract.
|
|
61
|
-
* Returns typed CSS variable references for use in tw components.
|
|
62
|
-
*
|
|
63
|
-
* @example
|
|
64
|
-
* const contract = defineThemeContract({
|
|
65
|
-
* colors: { bg: "", fg: "", primary: "" },
|
|
66
|
-
* font: { sans: "" },
|
|
67
|
-
* })
|
|
68
|
-
*
|
|
69
|
-
* // Use in components:
|
|
70
|
-
* const Card = tw.div`bg-[${contract._vars.colors.bg}]`
|
|
71
|
-
* // → tw.div`bg-[var(--colors-bg)]`
|
|
72
|
-
*/
|
|
73
|
-
declare function defineThemeContract<T extends TokenMap>(shape: T): ThemeContract<T>;
|
|
74
|
-
/**
|
|
75
|
-
* Create a typed theme that satisfies a contract.
|
|
76
|
-
*
|
|
77
|
-
* @param contract - Theme contract from defineThemeContract()
|
|
78
|
-
* @param name - Theme name ("light", "dark", "brand", etc.)
|
|
79
|
-
* @param values - Token values (TypeScript enforces completeness)
|
|
80
|
-
* @param asRoot - If true, use :root selector. Default: false (uses [data-theme])
|
|
81
|
-
*/
|
|
82
|
-
declare function createTheme<T extends TokenMap>(contract: ThemeContract<T>, name: string, values: T, asRoot?: boolean): Theme<T>;
|
|
83
|
-
declare class ThemeRegistry {
|
|
84
|
-
private themes;
|
|
85
|
-
private defaultTheme;
|
|
86
|
-
/** Register a theme */
|
|
87
|
-
register<T extends TokenMap>(theme: Theme<T>, isDefault?: boolean): this;
|
|
88
|
-
/** Get a theme by name */
|
|
89
|
-
get(name: string): Theme<any> | undefined;
|
|
90
|
-
/** Get all theme names */
|
|
91
|
-
names(): string[];
|
|
92
|
-
/**
|
|
93
|
-
* Generate combined CSS for all themes.
|
|
94
|
-
* Inject into <head> or a .css file.
|
|
95
|
-
*
|
|
96
|
-
* @example
|
|
97
|
-
* // In globals.css or layout.tsx
|
|
98
|
-
* const css = registry.generateCss()
|
|
99
|
-
*/
|
|
100
|
-
generateCss(): string;
|
|
101
|
-
/**
|
|
102
|
-
* Get the CSS for a specific theme only.
|
|
103
|
-
*/
|
|
104
|
-
getThemeCss(name: string): string | null;
|
|
105
|
-
/**
|
|
106
|
-
* Inject all theme CSS into document <head> (browser only).
|
|
107
|
-
* Call once on app init.
|
|
108
|
-
*/
|
|
109
|
-
inject(styleId?: string): void;
|
|
110
|
-
/**
|
|
111
|
-
* Switch active theme by setting data-theme on <html>.
|
|
112
|
-
*/
|
|
113
|
-
apply(name: string, target?: HTMLElement): void;
|
|
114
|
-
/**
|
|
115
|
-
* Get current active theme name from data-theme attribute.
|
|
116
|
-
*/
|
|
117
|
-
current(target?: HTMLElement): string | null;
|
|
118
|
-
}
|
|
119
|
-
interface MultiThemeConfig<T extends TokenMap> {
|
|
120
|
-
contract: ThemeContract<T>;
|
|
121
|
-
light: T;
|
|
122
|
-
dark: T;
|
|
123
|
-
/** Additional named themes (brand, high-contrast, etc.) */
|
|
124
|
-
extras?: Record<string, T>;
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Create a ThemeRegistry with light/dark + optional extras in one call.
|
|
128
|
-
*
|
|
129
|
-
* @example
|
|
130
|
-
* const { registry, vars } = createMultiTheme({
|
|
131
|
-
* contract: defineThemeContract({
|
|
132
|
-
* colors: { bg: "", fg: "", primary: "", border: "" }
|
|
133
|
-
* }),
|
|
134
|
-
* light: {
|
|
135
|
-
* colors: { bg: "#fff", fg: "#09090b", primary: "#3b82f6", border: "#e5e7eb" }
|
|
136
|
-
* },
|
|
137
|
-
* dark: {
|
|
138
|
-
* colors: { bg: "#09090b", fg: "#fafafa", primary: "#60a5fa", border: "#27272a" }
|
|
139
|
-
* },
|
|
140
|
-
* })
|
|
141
|
-
*
|
|
142
|
-
* // Inject CSS:
|
|
143
|
-
* registry.inject()
|
|
144
|
-
*
|
|
145
|
-
* // Use tokens in components:
|
|
146
|
-
* const Card = tw.div`bg-[${vars.colors.bg}] text-[${vars.colors.fg}]`
|
|
147
|
-
*/
|
|
148
|
-
declare function createMultiTheme<T extends TokenMap>(config: MultiThemeConfig<T>): {
|
|
149
|
-
registry: ThemeRegistry;
|
|
150
|
-
vars: ThemeVars<T>;
|
|
151
|
-
light: Theme<T>;
|
|
152
|
-
dark: Theme<T>;
|
|
153
|
-
};
|
|
154
|
-
interface DesignTokens {
|
|
155
|
-
[path: string]: string | DesignTokens;
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* Flatten nested design token object into CSS variables.
|
|
159
|
-
* Supports Figma-style nested tokens.
|
|
160
|
-
*
|
|
161
|
-
* @example
|
|
162
|
-
* compileDesignTokens({
|
|
163
|
-
* color: {
|
|
164
|
-
* brand: { primary: "#3b82f6", secondary: "#6366f1" },
|
|
165
|
-
* neutral: { 50: "#fafafa", 900: "#09090b" }
|
|
166
|
-
* },
|
|
167
|
-
* spacing: { base: "4px", lg: "16px" }
|
|
168
|
-
* })
|
|
169
|
-
* →
|
|
170
|
-
* :root {
|
|
171
|
-
* --color-brand-primary: #3b82f6;
|
|
172
|
-
* --color-brand-secondary: #6366f1;
|
|
173
|
-
* --color-neutral-50: #fafafa;
|
|
174
|
-
* --color-neutral-900: #09090b;
|
|
175
|
-
* --spacing-base: 4px;
|
|
176
|
-
* --spacing-lg: 16px;
|
|
177
|
-
* }
|
|
178
|
-
*/
|
|
179
|
-
declare function compileDesignTokens(tokens: DesignTokens, prefix?: string): string;
|
|
180
|
-
|
|
181
|
-
export { type DesignTokens, type MultiThemeConfig, type Theme, type ThemeContract, ThemeRegistry, type ThemeVars, type TokenMap, compileDesignTokens, createMultiTheme, createTheme, defineThemeContract };
|
package/dist/theme.js
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
/* tailwind-styled-v4 v4 | MIT | https://github.com/dictionar32/tailwind-styled-v4 */
|
|
2
|
-
|
|
3
|
-
// ../theme/src/index.ts
|
|
4
|
-
function defineThemeContract(shape) {
|
|
5
|
-
const vars = {};
|
|
6
|
-
for (const group in shape) {
|
|
7
|
-
vars[group] = {};
|
|
8
|
-
for (const token in shape[group]) {
|
|
9
|
-
vars[group][token] = `var(--${group}-${token})`;
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
return { _contract: shape, _vars: vars };
|
|
13
|
-
}
|
|
14
|
-
function createTheme(contract, name, values, asRoot = false) {
|
|
15
|
-
const flatVars = {};
|
|
16
|
-
const cssLines = [];
|
|
17
|
-
for (const group in values) {
|
|
18
|
-
for (const token in values[group]) {
|
|
19
|
-
const varName = `--${group}-${token}`;
|
|
20
|
-
const value = values[group][token];
|
|
21
|
-
flatVars[varName] = value;
|
|
22
|
-
cssLines.push(` ${varName}: ${value};`);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
const selector = asRoot ? ":root" : `[data-theme="${name}"]`;
|
|
26
|
-
const css = `${selector} {
|
|
27
|
-
${cssLines.join("\n")}
|
|
28
|
-
}`;
|
|
29
|
-
return {
|
|
30
|
-
name,
|
|
31
|
-
contract,
|
|
32
|
-
values,
|
|
33
|
-
css,
|
|
34
|
-
vars: flatVars,
|
|
35
|
-
selector
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
var ThemeRegistry = class {
|
|
39
|
-
constructor() {
|
|
40
|
-
this.themes = /* @__PURE__ */ new Map();
|
|
41
|
-
this.defaultTheme = null;
|
|
42
|
-
}
|
|
43
|
-
/** Register a theme */
|
|
44
|
-
register(theme, isDefault = false) {
|
|
45
|
-
this.themes.set(theme.name, theme);
|
|
46
|
-
if (isDefault || !this.defaultTheme) {
|
|
47
|
-
this.defaultTheme = theme.name;
|
|
48
|
-
}
|
|
49
|
-
return this;
|
|
50
|
-
}
|
|
51
|
-
/** Get a theme by name */
|
|
52
|
-
get(name) {
|
|
53
|
-
return this.themes.get(name);
|
|
54
|
-
}
|
|
55
|
-
/** Get all theme names */
|
|
56
|
-
names() {
|
|
57
|
-
return Array.from(this.themes.keys());
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Generate combined CSS for all themes.
|
|
61
|
-
* Inject into <head> or a .css file.
|
|
62
|
-
*
|
|
63
|
-
* @example
|
|
64
|
-
* // In globals.css or layout.tsx
|
|
65
|
-
* const css = registry.generateCss()
|
|
66
|
-
*/
|
|
67
|
-
generateCss() {
|
|
68
|
-
return Array.from(this.themes.values()).map((t) => t.css).join("\n\n");
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Get the CSS for a specific theme only.
|
|
72
|
-
*/
|
|
73
|
-
getThemeCss(name) {
|
|
74
|
-
var _a, _b;
|
|
75
|
-
return (_b = (_a = this.themes.get(name)) == null ? void 0 : _a.css) != null ? _b : null;
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Inject all theme CSS into document <head> (browser only).
|
|
79
|
-
* Call once on app init.
|
|
80
|
-
*/
|
|
81
|
-
inject(styleId = "__tw_themes") {
|
|
82
|
-
if (typeof document === "undefined") return;
|
|
83
|
-
let style = document.getElementById(styleId);
|
|
84
|
-
if (!style) {
|
|
85
|
-
style = document.createElement("style");
|
|
86
|
-
style.id = styleId;
|
|
87
|
-
document.head.appendChild(style);
|
|
88
|
-
}
|
|
89
|
-
style.textContent = this.generateCss();
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Switch active theme by setting data-theme on <html>.
|
|
93
|
-
*/
|
|
94
|
-
apply(name, target = document.documentElement) {
|
|
95
|
-
if (typeof document === "undefined") return;
|
|
96
|
-
if (!this.themes.has(name)) {
|
|
97
|
-
console.warn(`[tailwind-styled-v4] Theme "${name}" not registered.`);
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
target.dataset.theme = name;
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Get current active theme name from data-theme attribute.
|
|
104
|
-
*/
|
|
105
|
-
current(target = document.documentElement) {
|
|
106
|
-
var _a;
|
|
107
|
-
if (typeof document === "undefined") return this.defaultTheme;
|
|
108
|
-
return (_a = target.dataset.theme) != null ? _a : this.defaultTheme;
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
function createMultiTheme(config) {
|
|
112
|
-
var _a;
|
|
113
|
-
const registry = new ThemeRegistry();
|
|
114
|
-
const light = createTheme(config.contract, "light", config.light, true);
|
|
115
|
-
const dark = createTheme(config.contract, "dark", config.dark, false);
|
|
116
|
-
registry.register(light, true);
|
|
117
|
-
registry.register(dark);
|
|
118
|
-
for (const [name, values] of Object.entries((_a = config.extras) != null ? _a : {})) {
|
|
119
|
-
registry.register(createTheme(config.contract, name, values));
|
|
120
|
-
}
|
|
121
|
-
return {
|
|
122
|
-
registry,
|
|
123
|
-
vars: config.contract._vars,
|
|
124
|
-
light,
|
|
125
|
-
dark
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
function compileDesignTokens(tokens, prefix = "") {
|
|
129
|
-
const vars = [];
|
|
130
|
-
function flatten(obj, path) {
|
|
131
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
132
|
-
const varPath = path ? `${path}-${key}` : key;
|
|
133
|
-
if (typeof value === "string") {
|
|
134
|
-
vars.push(` --${varPath}: ${value};`);
|
|
135
|
-
} else {
|
|
136
|
-
flatten(value, varPath);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
flatten(tokens, prefix);
|
|
141
|
-
return `:root {
|
|
142
|
-
${vars.join("\n")}
|
|
143
|
-
}`;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export { ThemeRegistry, compileDesignTokens, createMultiTheme, createTheme, defineThemeContract };
|
|
147
|
-
//# sourceMappingURL=theme.js.map
|
|
148
|
-
//# sourceMappingURL=theme.js.map
|
package/dist/theme.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../theme/src/index.ts"],"names":[],"mappings":"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","file":"theme.js","sourcesContent":["/**\n * tailwind-styled-v4 — Multi-Theme Engine\n *\n * Enterprise-grade theming. Support light/dark/brand themes dengan\n * CSS variables. Zero runtime overhead — themes di-resolve via CSS.\n *\n * Fitur:\n * - Multiple named themes (light, dark, brand, high-contrast)\n * - CSS variable output (Tailwind v4 compatible)\n * - Theme contract (TypeScript-safe — missing tokens = TS error)\n * - Per-component theme override\n * - White-label ready\n *\n * @example\n * // 1. Define contract\n * const contract = defineThemeContract({\n * colors: { bg: \"\", fg: \"\", primary: \"\", muted: \"\" },\n * font: { sans: \"\", mono: \"\" },\n * })\n *\n * // 2. Create themes\n * const lightTheme = createTheme(contract, \"light\", {\n * colors: { bg: \"#ffffff\", fg: \"#09090b\", primary: \"#3b82f6\", muted: \"#71717a\" },\n * font: { sans: \"InterVariable, sans-serif\", mono: \"JetBrains Mono, monospace\" },\n * })\n *\n * const darkTheme = createTheme(contract, \"dark\", {\n * colors: { bg: \"#09090b\", fg: \"#fafafa\", primary: \"#60a5fa\", muted: \"#a1a1aa\" },\n * font: { sans: \"InterVariable, sans-serif\", mono: \"JetBrains Mono, monospace\" },\n * })\n *\n * // 3. Use tokens in components\n * const Card = tw.div`bg-[var(--colors-bg)] text-[var(--colors-fg)] p-6`\n *\n * // 4. Apply in layout\n * // <html data-theme=\"dark\"> or inject CSS\n */\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type TokenMap = Record<string, Record<string, string>>\n\nexport interface ThemeContract<T extends TokenMap> {\n _contract: T\n _vars: ThemeVars<T>\n}\n\nexport type ThemeVars<T extends TokenMap> = {\n [Group in keyof T]: {\n [Token in keyof T[Group]]: string // \"var(--group-token)\"\n }\n}\n\nexport interface Theme<T extends TokenMap> {\n name: string\n contract: ThemeContract<T>\n values: T\n /** CSS string to inject (`:root` or `[data-theme=\"name\"]`) */\n css: string\n /** CSS variables as a flat record */\n vars: Record<string, string>\n /** Apply this theme to an element via data attribute */\n selector: string\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// defineThemeContract\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Define the shape of your theme. All themes must satisfy this contract.\n * Returns typed CSS variable references for use in tw components.\n *\n * @example\n * const contract = defineThemeContract({\n * colors: { bg: \"\", fg: \"\", primary: \"\" },\n * font: { sans: \"\" },\n * })\n *\n * // Use in components:\n * const Card = tw.div`bg-[${contract._vars.colors.bg}]`\n * // → tw.div`bg-[var(--colors-bg)]`\n */\nexport function defineThemeContract<T extends TokenMap>(shape: T): ThemeContract<T> {\n const vars = {} as ThemeVars<T>\n\n for (const group in shape) {\n ;(vars as any)[group] = {}\n for (const token in shape[group]) {\n ;(vars as any)[group][token] = `var(--${group}-${token})`\n }\n }\n\n return { _contract: shape, _vars: vars }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// createTheme\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Create a typed theme that satisfies a contract.\n *\n * @param contract - Theme contract from defineThemeContract()\n * @param name - Theme name (\"light\", \"dark\", \"brand\", etc.)\n * @param values - Token values (TypeScript enforces completeness)\n * @param asRoot - If true, use :root selector. Default: false (uses [data-theme])\n */\nexport function createTheme<T extends TokenMap>(\n contract: ThemeContract<T>,\n name: string,\n values: T,\n asRoot = false\n): Theme<T> {\n const flatVars: Record<string, string> = {}\n const cssLines: string[] = []\n\n for (const group in values) {\n for (const token in values[group]) {\n const varName = `--${group}-${token}`\n const value = values[group][token]\n flatVars[varName] = value\n cssLines.push(` ${varName}: ${value};`)\n }\n }\n\n const selector = asRoot ? \":root\" : `[data-theme=\"${name}\"]`\n const css = `${selector} {\\n${cssLines.join(\"\\n\")}\\n}`\n\n return {\n name,\n contract,\n values,\n css,\n vars: flatVars,\n selector,\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ThemeRegistry — manage multiple themes\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport class ThemeRegistry {\n private themes = new Map<string, Theme<any>>()\n private defaultTheme: string | null = null\n\n /** Register a theme */\n register<T extends TokenMap>(theme: Theme<T>, isDefault = false): this {\n this.themes.set(theme.name, theme)\n if (isDefault || !this.defaultTheme) {\n this.defaultTheme = theme.name\n }\n return this\n }\n\n /** Get a theme by name */\n get(name: string): Theme<any> | undefined {\n return this.themes.get(name)\n }\n\n /** Get all theme names */\n names(): string[] {\n return Array.from(this.themes.keys())\n }\n\n /**\n * Generate combined CSS for all themes.\n * Inject into <head> or a .css file.\n *\n * @example\n * // In globals.css or layout.tsx\n * const css = registry.generateCss()\n */\n generateCss(): string {\n return Array.from(this.themes.values())\n .map((t) => t.css)\n .join(\"\\n\\n\")\n }\n\n /**\n * Get the CSS for a specific theme only.\n */\n getThemeCss(name: string): string | null {\n return this.themes.get(name)?.css ?? null\n }\n\n /**\n * Inject all theme CSS into document <head> (browser only).\n * Call once on app init.\n */\n inject(styleId = \"__tw_themes\"): void {\n if (typeof document === \"undefined\") return\n\n let style = document.getElementById(styleId) as HTMLStyleElement | null\n if (!style) {\n style = document.createElement(\"style\")\n style.id = styleId\n document.head.appendChild(style)\n }\n style.textContent = this.generateCss()\n }\n\n /**\n * Switch active theme by setting data-theme on <html>.\n */\n apply(name: string, target: HTMLElement = document.documentElement): void {\n if (typeof document === \"undefined\") return\n if (!this.themes.has(name)) {\n console.warn(`[tailwind-styled-v4] Theme \"${name}\" not registered.`)\n return\n }\n target.dataset.theme = name\n }\n\n /**\n * Get current active theme name from data-theme attribute.\n */\n current(target: HTMLElement = document.documentElement): string | null {\n if (typeof document === \"undefined\") return this.defaultTheme\n return target.dataset.theme ?? this.defaultTheme\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Convenience: createMultiTheme — shorthand for common light/dark setup\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface MultiThemeConfig<T extends TokenMap> {\n contract: ThemeContract<T>\n light: T\n dark: T\n /** Additional named themes (brand, high-contrast, etc.) */\n extras?: Record<string, T>\n}\n\n/**\n * Create a ThemeRegistry with light/dark + optional extras in one call.\n *\n * @example\n * const { registry, vars } = createMultiTheme({\n * contract: defineThemeContract({\n * colors: { bg: \"\", fg: \"\", primary: \"\", border: \"\" }\n * }),\n * light: {\n * colors: { bg: \"#fff\", fg: \"#09090b\", primary: \"#3b82f6\", border: \"#e5e7eb\" }\n * },\n * dark: {\n * colors: { bg: \"#09090b\", fg: \"#fafafa\", primary: \"#60a5fa\", border: \"#27272a\" }\n * },\n * })\n *\n * // Inject CSS:\n * registry.inject()\n *\n * // Use tokens in components:\n * const Card = tw.div`bg-[${vars.colors.bg}] text-[${vars.colors.fg}]`\n */\nexport function createMultiTheme<T extends TokenMap>(\n config: MultiThemeConfig<T>\n): {\n registry: ThemeRegistry\n vars: ThemeVars<T>\n light: Theme<T>\n dark: Theme<T>\n} {\n const registry = new ThemeRegistry()\n\n const light = createTheme(config.contract, \"light\", config.light, true) // :root\n const dark = createTheme(config.contract, \"dark\", config.dark, false)\n\n registry.register(light, true)\n registry.register(dark)\n\n for (const [name, values] of Object.entries(config.extras ?? {})) {\n registry.register(createTheme(config.contract, name, values as T))\n }\n\n return {\n registry,\n vars: config.contract._vars,\n light,\n dark,\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Design Token Compiler — generate CSS vars from token object\n// (Enterprise feature: sync with Figma variables)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface DesignTokens {\n [path: string]: string | DesignTokens\n}\n\n/**\n * Flatten nested design token object into CSS variables.\n * Supports Figma-style nested tokens.\n *\n * @example\n * compileDesignTokens({\n * color: {\n * brand: { primary: \"#3b82f6\", secondary: \"#6366f1\" },\n * neutral: { 50: \"#fafafa\", 900: \"#09090b\" }\n * },\n * spacing: { base: \"4px\", lg: \"16px\" }\n * })\n * →\n * :root {\n * --color-brand-primary: #3b82f6;\n * --color-brand-secondary: #6366f1;\n * --color-neutral-50: #fafafa;\n * --color-neutral-900: #09090b;\n * --spacing-base: 4px;\n * --spacing-lg: 16px;\n * }\n */\nexport function compileDesignTokens(tokens: DesignTokens, prefix = \"\"): string {\n const vars: string[] = []\n\n function flatten(obj: DesignTokens, path: string) {\n for (const [key, value] of Object.entries(obj)) {\n const varPath = path ? `${path}-${key}` : key\n if (typeof value === \"string\") {\n vars.push(` --${varPath}: ${value};`)\n } else {\n flatten(value as DesignTokens, varPath)\n }\n }\n }\n\n flatten(tokens, prefix)\n return `:root {\\n${vars.join(\"\\n\")}\\n}`\n}\n"]}
|