@12nil/theme-registry-package 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,291 @@
1
+ import React from 'react';
2
+
3
+ declare const RUNTIME_THEME_REGISTRY_VERSION = "0.2.0";
4
+ interface ThemePalette {
5
+ primary: string;
6
+ secondary: string;
7
+ tertiary?: string;
8
+ background: string;
9
+ text: string;
10
+ accent: string;
11
+ muted: string;
12
+ error: string;
13
+ warning: string;
14
+ success: string;
15
+ info: string;
16
+ }
17
+ interface SemanticColorTokens {
18
+ primary: string;
19
+ secondary: string;
20
+ destructive: string;
21
+ success: string;
22
+ warning: string;
23
+ info: string;
24
+ }
25
+ interface SemanticSurfaceTokens {
26
+ page: string;
27
+ card: string;
28
+ sidebar: string;
29
+ modal: string;
30
+ popover: string;
31
+ }
32
+ interface SemanticTextTokens {
33
+ primary: string;
34
+ secondary: string;
35
+ tertiary: string;
36
+ disabled: string;
37
+ }
38
+ interface SemanticBorderTokens {
39
+ default: string;
40
+ subtle: string;
41
+ strong: string;
42
+ }
43
+ interface ThemeTokensV2 {
44
+ colors: SemanticColorTokens;
45
+ surface: SemanticSurfaceTokens;
46
+ text: SemanticTextTokens;
47
+ border: SemanticBorderTokens;
48
+ }
49
+ type ThemeTokenSet = ThemePalette | ThemeTokensV2;
50
+ interface ThemeCompatibility {
51
+ minRegistryVersion?: string;
52
+ }
53
+ interface ThemeMetadata {
54
+ author?: string;
55
+ version?: string;
56
+ description?: string;
57
+ homepage?: string;
58
+ license?: string;
59
+ compatibility?: ThemeCompatibility;
60
+ }
61
+ interface ThemeDefinition {
62
+ name: string;
63
+ modes: Record<string, ThemeTokenSet>;
64
+ metadata?: ThemeMetadata;
65
+ }
66
+ type ThemeRegistrationConflict = 'throw' | 'replace' | 'merge';
67
+ interface ThemeRegistrationOptions {
68
+ ifExists?: ThemeRegistrationConflict;
69
+ validate?: boolean;
70
+ }
71
+ interface ThemeFallbackConfig {
72
+ themeName?: string;
73
+ modeName?: string;
74
+ }
75
+ interface ThemeValidationIssue {
76
+ path: string;
77
+ message: string;
78
+ }
79
+ interface ThemeValidationResult {
80
+ valid: boolean;
81
+ issues: ThemeValidationIssue[];
82
+ }
83
+ interface ThemeResolutionResult {
84
+ themeName: string | null;
85
+ modeName: string | null;
86
+ tokens: ThemeTokenSet | null;
87
+ }
88
+ interface ThemePluginDefinition {
89
+ name: string;
90
+ version?: string;
91
+ description?: string;
92
+ author?: string;
93
+ homepage?: string;
94
+ license?: string;
95
+ minRegistryVersion?: string;
96
+ theme?: ThemeDefinition;
97
+ themes?: ThemeDefinition[];
98
+ icons?: Record<string, string>;
99
+ fonts?: Record<string, string>;
100
+ spacing?: Record<string, string | number>;
101
+ typography?: Record<string, string | number>;
102
+ metadata?: Record<string, unknown>;
103
+ }
104
+ interface ThemePluginContributions {
105
+ icons: Record<string, string>;
106
+ fonts: Record<string, string>;
107
+ spacing: Record<string, string | number>;
108
+ typography: Record<string, string | number>;
109
+ metadata: Record<string, unknown>;
110
+ }
111
+ type ThemeCompositionLayerType = 'brand' | 'appearance' | 'accessibility';
112
+ interface ThemeCompositionLayerDefinition {
113
+ type: ThemeCompositionLayerType;
114
+ name: string;
115
+ tokens: ThemeTokenSet;
116
+ metadata?: Record<string, unknown>;
117
+ }
118
+ interface ThemeCompositionSelection {
119
+ brand?: string | null;
120
+ appearance?: string | null;
121
+ accessibility?: string | null;
122
+ }
123
+ interface ThemeCompositionResult {
124
+ selection: ThemeCompositionSelection;
125
+ tokens: ThemeTokenSet | null;
126
+ appliedLayers: ThemeCompositionLayerDefinition[];
127
+ }
128
+ type ThemeLoadSource = string | (() => Promise<ThemeDefinition | ThemeDefinition[]>);
129
+ interface ThemeLoadOptions {
130
+ ifExists?: ThemeRegistrationConflict;
131
+ validate?: boolean;
132
+ cacheKey?: string;
133
+ maxAgeMs?: number;
134
+ forceRefresh?: boolean;
135
+ }
136
+ interface ThemeLoadResult {
137
+ loaded: number;
138
+ themes: string[];
139
+ source: string;
140
+ fromCache: boolean;
141
+ }
142
+ interface ThemeLoadCacheEntry {
143
+ themes: ThemeDefinition[];
144
+ loadedAt: number;
145
+ source: string;
146
+ }
147
+ interface ThemeDocumentAttributes {
148
+ 'data-theme': string;
149
+ 'data-mode': string;
150
+ }
151
+ type PluginRegistrationConflict = 'throw' | 'replace' | 'merge';
152
+ interface PluginRegistrationOptions {
153
+ ifExists?: PluginRegistrationConflict;
154
+ themeIfExists?: ThemeRegistrationConflict;
155
+ validateThemes?: boolean;
156
+ }
157
+ interface ThemeRegistryEventPayloadMap {
158
+ registered: {
159
+ themeName: string;
160
+ };
161
+ replaced: {
162
+ themeName: string;
163
+ };
164
+ merged: {
165
+ themeName: string;
166
+ };
167
+ unregistered: {
168
+ themeName: string;
169
+ };
170
+ variantAdded: {
171
+ themeName: string;
172
+ variantName: string;
173
+ };
174
+ themeChanged: {
175
+ themeName: string;
176
+ modeName: string;
177
+ };
178
+ loaded: {
179
+ source: string;
180
+ count: number;
181
+ fromCache: boolean;
182
+ };
183
+ composed: {
184
+ appliedLayers: string[];
185
+ };
186
+ pluginRegistered: {
187
+ pluginName: string;
188
+ };
189
+ pluginUnregistered: {
190
+ pluginName: string;
191
+ };
192
+ destroyed: {
193
+ timestamp: string;
194
+ };
195
+ }
196
+ type ThemeRegistryEvent = keyof ThemeRegistryEventPayloadMap;
197
+ type ThemeRegistryEventHandler<K extends ThemeRegistryEvent> = (payload: ThemeRegistryEventPayloadMap[K]) => void;
198
+ declare function isThemeTokensV2(tokens: ThemeTokenSet): tokens is ThemeTokensV2;
199
+ declare function validateTheme(theme: ThemeDefinition): ThemeValidationResult;
200
+ declare class ThemeRegistry {
201
+ private themes;
202
+ private plugins;
203
+ private compositionLayers;
204
+ private loadedThemeCache;
205
+ private currentTheme;
206
+ private currentMode;
207
+ private fallbackConfig;
208
+ private listeners;
209
+ getVersion(): string;
210
+ on<K extends ThemeRegistryEvent>(event: K, handler: ThemeRegistryEventHandler<K>): () => void;
211
+ onRegistered(handler: ThemeRegistryEventHandler<'registered'>): () => void;
212
+ onThemeChanged(handler: ThemeRegistryEventHandler<'themeChanged'>): () => void;
213
+ onVariantAdded(handler: ThemeRegistryEventHandler<'variantAdded'>): () => void;
214
+ onDestroyed(handler: ThemeRegistryEventHandler<'destroyed'>): () => void;
215
+ onLoaded(handler: ThemeRegistryEventHandler<'loaded'>): () => void;
216
+ onComposed(handler: ThemeRegistryEventHandler<'composed'>): () => void;
217
+ private emit;
218
+ register(theme: ThemeDefinition, options?: ThemeRegistrationOptions): void;
219
+ replace(themeName: string, theme: ThemeDefinition): void;
220
+ merge(themeName: string, theme: ThemeDefinition): void;
221
+ unregister(themeName: string): boolean;
222
+ setFallback(config: ThemeFallbackConfig): void;
223
+ registerPlugin(plugin: ThemePluginDefinition, options?: PluginRegistrationOptions): void;
224
+ registerCompositionLayer(layer: ThemeCompositionLayerDefinition, options?: ThemeRegistrationOptions): void;
225
+ getCompositionLayer(type: ThemeCompositionLayerType, name: string): ThemeCompositionLayerDefinition | undefined;
226
+ getCompositionLayers(type?: ThemeCompositionLayerType): ThemeCompositionLayerDefinition[];
227
+ compose(selection: ThemeCompositionSelection): ThemeCompositionResult;
228
+ getInitialThemeAttributes(themeName: string | null, modeName: string | null): ThemeDocumentAttributes | null;
229
+ hydrateThemeOnDocument(themeName: string | null, modeName: string | null): ThemeResolutionResult;
230
+ load(source: ThemeLoadSource, options?: ThemeLoadOptions): Promise<ThemeLoadResult>;
231
+ clearLoadCache(cacheKey?: string): void;
232
+ unregisterPlugin(pluginName: string): boolean;
233
+ getPlugin(name: string): ThemePluginDefinition | undefined;
234
+ getPlugins(): ThemePluginDefinition[];
235
+ getPluginContributions(pluginName: string): ThemePluginContributions | undefined;
236
+ getMergedPluginContributions(): ThemePluginContributions;
237
+ /**
238
+ * Dynamically register a theme mode - useful for packages to add their own modes
239
+ * @param themeName - The name of the existing theme to add the mode to
240
+ * @param modeName - The name of the mode (e.g., 'ocean', 'forest', 'high-contrast')
241
+ * @param tokens - The tokens for this mode
242
+ */
243
+ registerMode(themeName: string, modeName: string, tokens: ThemeTokenSet): void;
244
+ /**
245
+ * Register multiple modes at once for a theme
246
+ */
247
+ registerModes(themeName: string, modes: Record<string, ThemeTokenSet>): void;
248
+ /**
249
+ * Create and register a new theme with specific modes
250
+ * Useful for packages that want to add their own themes dynamically
251
+ */
252
+ createTheme(name: string, modes: Record<string, ThemeTokenSet>): void;
253
+ private getFallbackThemeName;
254
+ private resolveModeName;
255
+ resolveSelection(themeName: string | null, modeName: string | null): ThemeResolutionResult;
256
+ get(name: string): ThemeDefinition | undefined;
257
+ getAll(): ThemeDefinition[];
258
+ getTokens(themeName: string, modeName: string): ThemeTokenSet | undefined;
259
+ setCurrent(themeName: string, modeName: string): void;
260
+ getCurrent(): {
261
+ theme: string | null;
262
+ mode: string | null;
263
+ };
264
+ has(themeName: string): boolean;
265
+ getModes(themeName: string): string[];
266
+ getVariants(themeName: string): string[];
267
+ destroy(): void;
268
+ }
269
+ declare const themeRegistry: ThemeRegistry;
270
+
271
+ interface ThemeContextType {
272
+ currentTheme: string | null;
273
+ currentMode: string | null;
274
+ themes: ThemeDefinition[];
275
+ setTheme: (themeName: string, mode: string) => void;
276
+ setMode: (mode: string) => void;
277
+ availableModes: string[];
278
+ getThemeColors: () => ThemeTokenSet | null;
279
+ }
280
+ declare function ThemeProvider({ children }: {
281
+ children: React.ReactNode;
282
+ }): React.JSX.Element;
283
+ declare function useTheme(): ThemeContextType;
284
+
285
+ declare function paletteToCSSVariables(palette: ThemePalette): Record<string, string>;
286
+ declare function tokenSetToCSSVariables(tokens: ThemeTokenSet): Record<string, string>;
287
+ declare function generateThemeCSS(theme: ThemeDefinition): string;
288
+ declare function generateAllThemesCSS(themes: ThemeDefinition[]): string;
289
+ declare function applyThemeToDocument(theme: ThemeDefinition, mode: string): void;
290
+
291
+ export { type ThemePalette as ColorModePalette, type PluginRegistrationConflict, type PluginRegistrationOptions, RUNTIME_THEME_REGISTRY_VERSION, type ThemeCompatibility, type ThemeCompositionLayerDefinition, type ThemeCompositionLayerType, type ThemeCompositionResult, type ThemeCompositionSelection, type ThemeDefinition, type ThemeDocumentAttributes, type ThemeFallbackConfig, type ThemeLoadCacheEntry, type ThemeLoadOptions, type ThemeLoadResult, type ThemeLoadSource, type ThemeMetadata, type ThemePalette, type ThemePluginContributions, type ThemePluginDefinition, ThemeProvider, type ThemeRegistrationConflict, type ThemeRegistrationOptions, type ThemeRegistryEvent, type ThemeRegistryEventHandler, type ThemeRegistryEventPayloadMap, type ThemeResolutionResult, type ThemeTokenSet, type ThemeTokensV2, type ThemeValidationIssue, type ThemeValidationResult, applyThemeToDocument, themeRegistry as default, generateAllThemesCSS, generateThemeCSS, isThemeTokensV2, paletteToCSSVariables, themeRegistry, tokenSetToCSSVariables, useTheme, validateTheme };
package/dist/index.js ADDED
@@ -0,0 +1,172 @@
1
+ import {
2
+ RUNTIME_THEME_REGISTRY_VERSION,
3
+ isThemeTokensV2,
4
+ themeRegistry,
5
+ validateTheme
6
+ } from "./chunk-GUPUN2GN.js";
7
+
8
+ // theme-provider.tsx
9
+ import { createContext, useContext, useEffect, useState, useCallback } from "react";
10
+
11
+ // theme-utils.ts
12
+ function paletteToCSSVariables(palette) {
13
+ return tokenSetToCSSVariables(palette);
14
+ }
15
+ function semanticTokensToCSSVariables(tokens) {
16
+ const variables = {};
17
+ const appendCategory = (category, categoryValues) => {
18
+ ;
19
+ Object.entries(categoryValues).forEach(([key, value]) => {
20
+ const keyName = String(key);
21
+ variables[`--theme-${category}-${keyName}`] = value;
22
+ if (category === "colors") {
23
+ variables[`--color-theme-${keyName}`] = value;
24
+ }
25
+ });
26
+ };
27
+ appendCategory("colors", tokens.colors);
28
+ appendCategory("surface", tokens.surface);
29
+ appendCategory("text", tokens.text);
30
+ appendCategory("border", tokens.border);
31
+ return variables;
32
+ }
33
+ function legacyTokensToCSSVariables(tokens) {
34
+ const variables = {};
35
+ Object.entries(tokens).forEach(([key, value]) => {
36
+ if (key === "tertiary" && !value) return;
37
+ if (!value) return;
38
+ variables[`--color-theme-${String(key)}`] = value;
39
+ });
40
+ return variables;
41
+ }
42
+ function tokenSetToCSSVariables(tokens) {
43
+ if (isThemeTokensV2(tokens)) {
44
+ return semanticTokensToCSSVariables(tokens);
45
+ }
46
+ return legacyTokensToCSSVariables(tokens);
47
+ }
48
+ function generateThemeCSS(theme) {
49
+ const modes = Object.entries(theme.modes);
50
+ let css = `/* Theme: ${theme.name} */
51
+ `;
52
+ modes.forEach(([modeName, tokens]) => {
53
+ css += `
54
+ [data-theme="${theme.name}"][data-mode="${modeName}"] {
55
+ `;
56
+ const variables = tokenSetToCSSVariables(tokens);
57
+ Object.entries(variables).forEach(([name, value]) => {
58
+ css += ` ${name}: ${value};
59
+ `;
60
+ });
61
+ css += `}
62
+ `;
63
+ });
64
+ return css;
65
+ }
66
+ function generateAllThemesCSS(themes) {
67
+ return themes.map(generateThemeCSS).join("\n");
68
+ }
69
+ function applyThemeToDocument(theme, mode) {
70
+ const tokens = theme.modes[mode];
71
+ if (!tokens) return;
72
+ document.documentElement.setAttribute("data-theme", theme.name);
73
+ document.documentElement.setAttribute("data-mode", mode);
74
+ const variables = tokenSetToCSSVariables(tokens);
75
+ Object.entries(variables).forEach(([name, value]) => {
76
+ document.documentElement.style.setProperty(name, value);
77
+ });
78
+ }
79
+
80
+ // theme-provider.tsx
81
+ import { jsx } from "react/jsx-runtime";
82
+ var ThemeContext = createContext(void 0);
83
+ function ThemeProvider({ children }) {
84
+ const [currentTheme, setCurrentTheme] = useState(null);
85
+ const [currentMode, setCurrentMode] = useState(null);
86
+ const [themes, setThemes] = useState([]);
87
+ useEffect(() => {
88
+ const registeredThemes = themeRegistry.getAll();
89
+ if (registeredThemes.length === 0) {
90
+ import("./app_theme-K3SQNO7I.js").then(() => {
91
+ setThemes(themeRegistry.getAll());
92
+ });
93
+ } else {
94
+ setThemes(registeredThemes);
95
+ }
96
+ if (registeredThemes.length > 0) {
97
+ const saved = localStorage.getItem("fnp-theme");
98
+ const savedMode = localStorage.getItem("fnp-mode");
99
+ const resolved = themeRegistry.resolveSelection(saved, savedMode);
100
+ if (!resolved.themeName || !resolved.modeName || !resolved.tokens) return;
101
+ setCurrentTheme(resolved.themeName);
102
+ setCurrentMode(resolved.modeName);
103
+ themeRegistry.setCurrent(resolved.themeName, resolved.modeName);
104
+ applyTheme(resolved.tokens);
105
+ localStorage.setItem("fnp-theme", resolved.themeName);
106
+ localStorage.setItem("fnp-mode", resolved.modeName);
107
+ }
108
+ }, []);
109
+ const applyTheme = useCallback((tokens) => {
110
+ const root = document.documentElement;
111
+ const variables = tokenSetToCSSVariables(tokens);
112
+ Object.entries(variables).forEach(([name, value]) => {
113
+ root.style.setProperty(name, value);
114
+ });
115
+ }, []);
116
+ const setTheme = useCallback((themeName, mode) => {
117
+ const resolved = themeRegistry.resolveSelection(themeName, mode);
118
+ if (!resolved.themeName || !resolved.modeName || !resolved.tokens) return;
119
+ setCurrentTheme(resolved.themeName);
120
+ setCurrentMode(resolved.modeName);
121
+ themeRegistry.setCurrent(resolved.themeName, resolved.modeName);
122
+ applyTheme(resolved.tokens);
123
+ localStorage.setItem("fnp-theme", resolved.themeName);
124
+ localStorage.setItem("fnp-mode", resolved.modeName);
125
+ }, [applyTheme]);
126
+ const setMode = useCallback((mode) => {
127
+ if (!currentTheme) return;
128
+ setTheme(currentTheme, mode);
129
+ }, [currentTheme, setTheme]);
130
+ const getThemeColors = useCallback(() => {
131
+ if (!currentTheme || !currentMode) return null;
132
+ return themeRegistry.getTokens(currentTheme, currentMode) ?? null;
133
+ }, [currentTheme, currentMode]);
134
+ const availableModes = currentTheme ? themeRegistry.getModes(currentTheme) : [];
135
+ return /* @__PURE__ */ jsx(
136
+ ThemeContext.Provider,
137
+ {
138
+ value: {
139
+ currentTheme,
140
+ currentMode,
141
+ themes,
142
+ setTheme,
143
+ setMode,
144
+ availableModes,
145
+ getThemeColors
146
+ },
147
+ children
148
+ }
149
+ );
150
+ }
151
+ function useTheme() {
152
+ const context = useContext(ThemeContext);
153
+ if (context === void 0) {
154
+ throw new Error("useTheme must be used within a ThemeProvider");
155
+ }
156
+ return context;
157
+ }
158
+ export {
159
+ RUNTIME_THEME_REGISTRY_VERSION,
160
+ ThemeProvider,
161
+ applyThemeToDocument,
162
+ themeRegistry as default,
163
+ generateAllThemesCSS,
164
+ generateThemeCSS,
165
+ isThemeTokensV2,
166
+ paletteToCSSVariables,
167
+ themeRegistry,
168
+ tokenSetToCSSVariables,
169
+ useTheme,
170
+ validateTheme
171
+ };
172
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../theme-provider.tsx","../theme-utils.ts"],"sourcesContent":["'use client'\r\n\r\nimport React, { createContext, useContext, useEffect, useState, useCallback } from 'react'\r\nimport { themeRegistry } from './theme-registry'\r\nimport type { ThemeDefinition, ThemeTokenSet } from './theme-registry'\r\nimport { tokenSetToCSSVariables } from './theme-utils'\r\nimport './app_theme'\r\n\r\ninterface ThemeContextType {\r\n currentTheme: string | null\r\n currentMode: string | null\r\n themes: ThemeDefinition[]\r\n setTheme: (themeName: string, mode: string) => void\r\n setMode: (mode: string) => void\r\n availableModes: string[]\r\n getThemeColors: () => ThemeTokenSet | null\r\n}\r\n\r\nconst ThemeContext = createContext<ThemeContextType | undefined>(undefined)\r\n\r\nexport function ThemeProvider({ children }: { children: React.ReactNode }) {\r\n const [currentTheme, setCurrentTheme] = useState<string | null>(null)\r\n const [currentMode, setCurrentMode] = useState<string | null>(null)\r\n const [themes, setThemes] = useState<ThemeDefinition[]>([])\r\n\r\n useEffect(() => {\r\n // Initialize themes from registry\r\n const registeredThemes = themeRegistry.getAll()\r\n \r\n // If no themes are registered, try to dynamically import app_theme\r\n if (registeredThemes.length === 0) {\r\n import('./app_theme').then(() => {\r\n setThemes(themeRegistry.getAll())\r\n })\r\n } else {\r\n setThemes(registeredThemes)\r\n }\r\n\r\n if (registeredThemes.length > 0) {\r\n // Try to load from localStorage\r\n const saved = localStorage.getItem('fnp-theme') as string | null\r\n const savedMode = localStorage.getItem('fnp-mode') as string | null\r\n\r\n const resolved = themeRegistry.resolveSelection(saved, savedMode)\r\n if (!resolved.themeName || !resolved.modeName || !resolved.tokens) return\r\n\r\n setCurrentTheme(resolved.themeName)\r\n setCurrentMode(resolved.modeName)\r\n themeRegistry.setCurrent(resolved.themeName, resolved.modeName)\r\n applyTheme(resolved.tokens)\r\n\r\n localStorage.setItem('fnp-theme', resolved.themeName)\r\n localStorage.setItem('fnp-mode', resolved.modeName)\r\n }\r\n }, [])\r\n\r\n const applyTheme = useCallback((tokens: ThemeTokenSet) => {\r\n const root = document.documentElement\r\n const variables = tokenSetToCSSVariables(tokens)\r\n Object.entries(variables).forEach(([name, value]) => {\r\n root.style.setProperty(name, value)\r\n })\r\n }, [])\r\n\r\n const setTheme = useCallback((themeName: string, mode: string) => {\r\n const resolved = themeRegistry.resolveSelection(themeName, mode)\r\n if (!resolved.themeName || !resolved.modeName || !resolved.tokens) return\r\n\r\n setCurrentTheme(resolved.themeName)\r\n setCurrentMode(resolved.modeName)\r\n themeRegistry.setCurrent(resolved.themeName, resolved.modeName)\r\n applyTheme(resolved.tokens)\r\n\r\n localStorage.setItem('fnp-theme', resolved.themeName)\r\n localStorage.setItem('fnp-mode', resolved.modeName)\r\n }, [applyTheme])\r\n\r\n const setMode = useCallback((mode: string) => {\r\n if (!currentTheme) return\r\n setTheme(currentTheme, mode)\r\n }, [currentTheme, setTheme])\r\n\r\n const getThemeColors = useCallback(() => {\r\n if (!currentTheme || !currentMode) return null\r\n return themeRegistry.getTokens(currentTheme, currentMode) ?? null\r\n }, [currentTheme, currentMode])\r\n\r\n const availableModes = currentTheme ? themeRegistry.getModes(currentTheme) : []\r\n\r\n return (\r\n <ThemeContext.Provider\r\n value={{\r\n currentTheme,\r\n currentMode,\r\n themes,\r\n setTheme,\r\n setMode,\r\n availableModes,\r\n getThemeColors,\r\n }}\r\n >\r\n {children}\r\n </ThemeContext.Provider>\r\n )\r\n}\r\n\r\nexport function useTheme() {\r\n const context = useContext(ThemeContext)\r\n if (context === undefined) {\r\n throw new Error('useTheme must be used within a ThemeProvider')\r\n }\r\n return context\r\n}","import type { ThemePalette, ThemeDefinition, ThemeTokenSet, ThemeTokensV2 } from './theme-registry'\r\nimport { isThemeTokensV2 } from './theme-registry'\r\n\r\nexport function paletteToCSSVariables(palette: ThemePalette): Record<string, string> {\r\n return tokenSetToCSSVariables(palette)\r\n}\r\n\r\nfunction semanticTokensToCSSVariables(tokens: ThemeTokensV2): Record<string, string> {\r\n const variables: Record<string, string> = {}\r\n\r\n const appendCategory = <T extends { [K in keyof T]: string }>(category: string, categoryValues: T) => {\r\n ;(Object.entries(categoryValues) as Array<[keyof T, string]>).forEach(([key, value]) => {\r\n const keyName = String(key)\r\n variables[`--theme-${category}-${keyName}`] = value\r\n\r\n // Keep color alias vars for easy migration from legacy palettes.\r\n if (category === 'colors') {\r\n variables[`--color-theme-${keyName}`] = value\r\n }\r\n })\r\n }\r\n\r\n appendCategory('colors', tokens.colors)\r\n appendCategory('surface', tokens.surface)\r\n appendCategory('text', tokens.text)\r\n appendCategory('border', tokens.border)\r\n\r\n return variables\r\n}\r\n\r\nfunction legacyTokensToCSSVariables(tokens: ThemePalette): Record<string, string> {\r\n const variables: Record<string, string> = {}\r\n\r\n ;(Object.entries(tokens) as Array<[keyof ThemePalette, string | undefined]>).forEach(([key, value]) => {\r\n if (key === 'tertiary' && !value) return\r\n if (!value) return\r\n variables[`--color-theme-${String(key)}`] = value\r\n })\r\n\r\n return variables\r\n}\r\n\r\nexport function tokenSetToCSSVariables(tokens: ThemeTokenSet): Record<string, string> {\r\n if (isThemeTokensV2(tokens)) {\r\n return semanticTokensToCSSVariables(tokens)\r\n }\r\n\r\n return legacyTokensToCSSVariables(tokens)\r\n}\r\n\r\nexport function generateThemeCSS(theme: ThemeDefinition): string {\r\n const modes = Object.entries(theme.modes)\r\n \r\n let css = `/* Theme: ${theme.name} */\\n`\r\n \r\n modes.forEach(([modeName, tokens]) => {\r\n css += `\\n[data-theme=\"${theme.name}\"][data-mode=\"${modeName}\"] {\\n`\r\n\r\n const variables = tokenSetToCSSVariables(tokens)\r\n Object.entries(variables).forEach(([name, value]) => {\r\n css += ` ${name}: ${value};\\n`\r\n })\r\n\r\n css += `}\\n`\r\n })\r\n\r\n return css\r\n}\r\n\r\nexport function generateAllThemesCSS(themes: ThemeDefinition[]): string {\r\n return themes.map(generateThemeCSS).join('\\n')\r\n}\r\n\r\nexport function applyThemeToDocument(theme: ThemeDefinition, mode: string) {\r\n const tokens = theme.modes[mode]\r\n if (!tokens) return\r\n\r\n document.documentElement.setAttribute('data-theme', theme.name)\r\n document.documentElement.setAttribute('data-mode', mode)\r\n\r\n const variables = tokenSetToCSSVariables(tokens)\r\n Object.entries(variables).forEach(([name, value]) => {\r\n document.documentElement.style.setProperty(name, value)\r\n })\r\n}"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@12nil/theme-registry-package",
3
+ "version": "0.1.0",
4
+ "description": "Theme registry and React provider utilities",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.cjs",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md",
20
+ "LICENSE"
21
+ ],
22
+ "sideEffects": [
23
+ "./app_theme.ts"
24
+ ],
25
+ "scripts": {
26
+ "build": "tsup index.ts --format esm,cjs --dts --sourcemap --clean",
27
+ "dev": "tsup index.ts --format esm,cjs --dts --watch",
28
+ "prepublishOnly": "npm run build"
29
+ },
30
+ "peerDependencies": {
31
+ "react": ">=18"
32
+ },
33
+ "devDependencies": {
34
+ "@types/react": "^18.3.3",
35
+ "tsup": "^8.2.4",
36
+ "typescript": "^5.8.3"
37
+ },
38
+ "publishConfig": {
39
+ "access": "public"
40
+ }
41
+ }