cyhip-dynamic-themes 0.2.2 → 1.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/README.md CHANGED
@@ -7,25 +7,23 @@ Implement dynamic color themes in your React apps with Tailwind CSS in a simple
7
7
 
8
8
  ## Examples
9
9
 
10
- - **Vite + React-ts: [cyhip-dynamic-themes](https://cyhip-dynamic-themes.vercel.app/)**
11
-
12
- - **Nextjs + React-ts: [cyhip-dynamic-themes-nextjs-example](https://cyhip-dynamic-themes-nextjs-example.vercel.app/)**
10
+ - **Vite + React-ts: [cyhip-dynamic-themes](https://cyhip-dynamic-themes.vercel.app/)**
13
11
 
14
12
  ## Features
15
13
 
16
- - **Dynamic Color Theming**: Allow your users to switch the color theme of your application in a simple and practical way.
17
- - **Dark Mode Support**: Easily switch between light and dark modes across your custom themes.
14
+ - **Dynamic Color Theming**: Allow your users to switch the color theme of your application in a simple and practical way.
15
+ - **Dark Mode Support**: Easily switch between light and dark modes across your custom themes.
18
16
 
19
- Inspired by the excellent [article](https://evilmartians.com/chronicles/better-dynamic-themes-in-tailwind-with-oklch-color-magic) by Dan Kozlov and Travis Turner, this package uses the library provided by them which provides a series of features for handling colors and defining dynamic css variables. Take a look at:. [https://github.com/dkzlv/tw-dynamic-themes](https://github.com/dkzlv/tw-dynamic-themes)
17
+ Inspired by the excellent [article](https://evilmartians.com/chronicles/better-dynamic-themes-in-tailwind-with-oklch-color-magic) by Dan Kozlov and Travis Turner, this package uses the library provided by them which provides a series of features for handling colors and defining dynamic CSS variables. Take a look at: [https://github.com/dkzlv/tw-dynamic-themes](https://github.com/dkzlv/tw-dynamic-themes)
20
18
 
21
19
  ## How It Works?
22
20
 
23
21
  `cyhip-dynamic-themes` simplifies theme setup with a unique approach:
24
22
 
25
- - **Single Hue Input**: Define just the **Hue** value, and the package automatically generates all color variants across the spectrum.
26
- - **Automatic Color Variants**: Unlike traditional methods, there’s no need to set up each shade manuallysimply select a hue, and the package takes care of the rest.
23
+ - **Single Hue Input**: Define just the **Hue** value, and the package automatically generates all color variants across the spectrum.
24
+ - **Automatic Color Variants**: Unlike traditional methods, there’s no need to set up each shade manually -- simply select a hue, and the package takes care of the rest.
27
25
 
28
- - **Custom Hook for Dynamic Theme Switching**: Allow your users to switch themes dynamically with the `useColorTheme` hook.
26
+ - **Custom Hooks for Dynamic Theme Switching**: Switch themes dynamically with `useTheme`, `useThemeHue`, and `useThemeMode`.
29
27
 
30
28
  ## Installation
31
29
 
@@ -41,9 +39,9 @@ yarn add cyhip-dynamic-themes
41
39
 
42
40
  ### Prerequisites
43
41
 
44
- Ensure you have Tailwind CSS installed in your project and the `tailwind.config.ts` and `postcss.config.mjs` files in your root directory.
42
+ Ensure you have Tailwind CSS v4 installed in your project.
45
43
 
46
- ### Initialize Theme basic files
44
+ ### Initialize theme files
47
45
 
48
46
  In your project's root directory, run:
49
47
 
@@ -55,139 +53,177 @@ This command generates the following files in the `/themes/` folder:
55
53
 
56
54
  ```bash
57
55
  /themes/
58
- ├── theme.config.ts # To set your available hue-based colors.
59
- ├── root.css # Main CSS file for styling.
60
- ├── theme-colors.ts # Includes color definitions for Tailwind.
61
- └── theme-switcher.tsx # Example component for theme switching.
62
- ```
63
-
64
- ### Update `tailwind.config.ts`
65
-
66
- To enable dynamic colors and dark mode, modify your `tailwind.config.ts` as follows:
67
-
68
- ```ts
69
- // tailwind.config.ts
70
- import type { Config } from 'tailwindcss';
71
- import { themeColors } from './src/themes/theme-colors';
72
-
73
- export default {
74
- content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
75
- darkMode: 'class',
76
- theme: {
77
- extend: {
78
- colors: themeColors,
79
- },
80
- },
81
- plugins: [],
82
- } satisfies Config;
56
+ ├── app-theme-provider.tsx # Wraps ThemeProvider for your app.
57
+ ├── root.css # Tailwind v4 tokens + theme mapping.
58
+ ├── theme.config.ts # Theme palette + defaults.
59
+ └── theme-switcher.tsx # Example component for theme switching.
83
60
  ```
84
61
 
85
- **Note:** After updating the configuration, restart your application to apply the changes.
86
-
87
62
  ### Import `root.css`
88
63
 
89
- To apply CSS styles linked to the defined themes, add `/themes/root.css` to your root TSX file:
64
+ To apply the Tailwind v4 tokens and theme mapping, import `/themes/root.css` in your root file.
90
65
 
91
66
  ```tsx
92
- // src/main.tsx
67
+ // src/main.tsx (Vite)
93
68
  import { StrictMode } from 'react';
94
69
  import { createRoot } from 'react-dom/client';
95
70
  import App from './App.tsx';
96
-
97
- // Import CSS
71
+ import { AppThemeProvider } from './themes/app-theme-provider.tsx';
98
72
  import './themes/root.css';
99
73
 
100
74
  createRoot(document.getElementById('root')!).render(
101
75
  <StrictMode>
102
- <App />
103
- </StrictMode>
76
+ <AppThemeProvider>
77
+ <App />
78
+ </AppThemeProvider>
79
+ </StrictMode>,
104
80
  );
105
81
  ```
106
82
 
107
83
  ### Theme Provider
108
84
 
109
- Use the `ThemeProvider` to initialize a default theme.
85
+ Use the `ThemeProvider` to initialize a default theme and optional storage.
110
86
 
111
87
  ```tsx
112
- // src/main.tsx
113
- import { ThemeConfig, ThemeProvider } from 'cyhip-dynamic-themes';
114
- import { chromaData, hueScheme } from './themes/theme.config.ts';
88
+ // themes/app-theme-provider.tsx
89
+ 'use client';
115
90
 
116
- import './index.css';
91
+ import { ThemeProvider } from 'cyhip-dynamic-themes';
92
+ import { defaultTheme, themeStorage } from './theme.config';
117
93
 
118
- createRoot(document.getElementById('root')!).render(
119
- <StrictMode>
120
- <ThemeProvider
121
- themeConfig={
122
- {
123
- hue: hueScheme.default,
124
- mode: 'light',
125
- chromaData: chromaData,
126
- } as ThemeConfig
127
- }
128
- >
129
- <App />
94
+ export function AppThemeProvider({ children }: { children: React.ReactNode }) {
95
+ return (
96
+ <ThemeProvider themeConfig={defaultTheme} storage={themeStorage}>
97
+ {children}
130
98
  </ThemeProvider>
131
- </StrictMode>
132
- );
99
+ );
100
+ }
133
101
  ```
134
102
 
135
103
  ### Switching Themes Dynamically
136
104
 
137
- Switching the main color palette can be done using the `ThemeSwitcher` component. Here's a basic example to illustrate its use:
105
+ Switch the main color palette and mode using `useThemeHue()` and `useThemeMode()`.
106
+
107
+ ```tsx
108
+ // src/themes/theme-switcher.tsx
109
+ import { availableThemes, hueScheme } from './theme.config';
110
+ import { useThemeHue, useThemeMode } from 'cyhip-dynamic-themes';
111
+ import { useMemo } from 'react';
112
+
113
+ export function ThemeSwitcher() {
114
+ return (
115
+ <div className="auto-cols grid grid-flow-row gap-2">
116
+ <div className="rounded border">
117
+ <ColorPaletteMenu />
118
+ </div>
119
+ <div>
120
+ <ThemeModeMenu />
121
+ </div>
122
+ </div>
123
+ );
124
+ }
125
+
126
+ function ColorPaletteMenu() {
127
+ const { hue, setThemeHue } = useThemeHue();
128
+
129
+ const selected = useMemo(
130
+ () => Object.keys(hueScheme).find((key) => hueScheme[key] === hue) ?? null,
131
+ [hue],
132
+ );
133
+
134
+ return (
135
+ <div className="m-3 grid auto-cols-max grid-flow-col gap-4">
136
+ {Object.keys(availableThemes).map((key) => (
137
+ <button
138
+ type="button"
139
+ key={key}
140
+ className="relative cursor-pointer"
141
+ onClick={() => setThemeHue(hueScheme[key])}
142
+ >
143
+ {selected === key && (
144
+ <span className="border-accent-400 absolute -inset-1 block h-8 w-8 rounded-full border-2" />
145
+ )}
146
+ <span
147
+ className="block h-6 w-6 rounded-full"
148
+ style={{ background: availableThemes[key] }}
149
+ />
150
+ </button>
151
+ ))}
152
+ </div>
153
+ );
154
+ }
155
+
156
+ function ThemeModeMenu() {
157
+ const { mode, setThemeMode } = useThemeMode();
158
+
159
+ const btnStyle = 'border px-2 py-2 rounded-md cursor-pointer hover:ring-1';
160
+ return (
161
+ <div className="m-2 flex justify-center gap-4">
162
+ <button
163
+ type="button"
164
+ className={`${btnStyle} ${mode === 'light' ? 'ring-1' : ''}`}
165
+ onClick={() => setThemeMode('light')}
166
+ >
167
+ Light
168
+ </button>
169
+ <button
170
+ type="button"
171
+ className={`${btnStyle} ${mode === 'dark' ? 'ring-1' : ''}`}
172
+ onClick={() => setThemeMode('dark')}
173
+ >
174
+ Dark
175
+ </button>
176
+ </div>
177
+ );
178
+ }
179
+ ```
138
180
 
139
181
  ```tsx
140
- // App.tsx
182
+ // src/App.tsx
141
183
  import { ThemeSwitcher } from './themes/theme-switcher';
142
- function App() {
184
+
185
+ export default function App() {
143
186
  return (
144
187
  <>
145
- <main className="h-screen flex flex-col justify-center items-center gap-y-14">
146
- <h1 className="text-4xl font-bold text-center">
147
- Cyhip Dynamic Themes - Basic Usage
188
+ <main className="flex h-screen flex-col items-center justify-center gap-y-14">
189
+ <h1 className="bg-accent-200/40 dark:bg-accent-700/40 grid grid-cols-1 gap-6 p-4">
190
+ Cyhip Dynamic Themes
148
191
  </h1>
149
192
  <ThemeSwitcher />
150
- <div className="bg-accent-200/40 dark:bg-accent-700/40 grid grid-cols-1 gap-6 p-4">
151
- <button className="bg-primary text-primary-foreground px-5 py-2 shadow rounded-sm font-medium mx-auto">
193
+ <div className="grid grid-cols-1 gap-6 p-4">
194
+ <button className="bg-primary text-primary-foreground mx-auto rounded-sm px-5 py-2 font-medium shadow">
152
195
  Button
153
196
  </button>
154
- <samp className="bg-accent-950/80 text-accent-100/90 text-sm rounded-sm px-4 py-1 shadow">
155
- className="bg-primary text-primary-foreground ..."
156
- </samp>
157
197
  </div>
158
198
  </main>
159
199
  </>
160
200
  );
161
201
  }
162
-
163
- export default App;
164
202
  ```
165
203
 
166
- Check the `/templates/theme-switcher.tsx` component to see how to initialize and alternate themes.
204
+ See `dev-apps/test-app/src/themes/theme-switcher.tsx` for a compact example with both hue and mode toggles.
167
205
 
168
- Finally, take a look on the last example and see how we can combine the accent variable with tailwind classes like `bg-accent-<value>` and `text-accent-<value>`.
206
+ Finally, take a look at the example above and see how we can combine the accent variable with tailwind classes like `bg-accent-<value>` and `text-accent-<value>`.
169
207
 
170
208
  ## Defining Color Palettes Based on Hue
171
209
 
172
- You can add or modify hue palettes by visiting [OKLCH Color Preview](https://oklch.com/). To change your hue values, edit the `/themes/hue-palettes.ts` file:
210
+ You can add or modify hue palettes by visiting [OKLCH Color Preview](https://oklch.com/). To change your hue values, edit `/themes/theme.config.ts`:
173
211
 
174
212
  ```ts
175
- // themes/hue-palettes.ts
213
+ // themes/theme.config.ts
176
214
  /**
177
215
  * HUE THEMES
178
216
  * Define the available color palettes here!
179
217
  */
180
218
 
181
- const hueScheme: Record<string, string> = {
182
- white: '-1',
183
- blue: '250',
184
- green: '150',
185
- orange: '35',
186
- pink: '0',
187
- purple: '316',
219
+ export const hueScheme: Record<string, number> = {
220
+ default: -1,
221
+ blue: 250,
222
+ green: 150,
223
+ orange: 35,
224
+ pink: 0,
225
+ purple: 316,
188
226
  };
189
-
190
- export { hueScheme };
191
227
  ```
192
228
 
193
229
  ## API
@@ -196,48 +232,50 @@ export { hueScheme };
196
232
 
197
233
  The `ThemeConfig` type represents the configuration settings for an application's theme, specifically controlling color and mode settings. This type provides key properties for determining color hues, dark or light mode, and chromatic adjustments based on a data record.
198
234
 
199
- - **Properties:**
235
+ - **`hue: number`** Determines the primary color base. If set to `-1`, the theme uses a white-based palette.
236
+ - **`mode: 'light' | 'dark'`** Controls light or dark mode.
237
+ - **`chromaData: Record<number, number>`** Maps shade numbers to chroma values for generating color scales.
200
238
 
201
- - `hue`: `number`
202
- It determines the primary color base; if set to -1, the theme uses a white color based scheme.
239
+ ### `Type ThemeMode`
203
240
 
204
- - `mode`: `'light' | 'dark'`
205
- Defines whether the theme is in "light" or "dark" mode. Acceptable
241
+ Type alias for `'light' | 'dark'`.
206
242
 
207
- - `chromaData`: `Record<number,number>`
208
- A record that maps specific numeric values to chroma levels. This data allows for dynamic chromatic adjustments, enabling fine-tuning of the theme's color saturation or intensity.
243
+ ### `Type ThemeColor`
209
244
 
210
- ### `useColorTheme(theme: ThemeConfig)`
245
+ Type alias for the numeric hue value.
211
246
 
212
- A custom hook that manages the application of color themes based on the provided HUE value and dark mode setting.
247
+ ### `useTheme()`
213
248
 
214
- ### `getThemeProperties(hue: number, darkMode: boolean, chromaData: Record<number,number>)`
249
+ State hook with `setTheme`, plus granular setters `setThemeHue` and `setThemeMode`.
215
250
 
216
- Defines CSS class and style properties based on the provided HUE value and dark mode setting.
251
+ ### `useThemeHue()`
217
252
 
218
- - **Parameters:**
253
+ Derived hook to read and update only the hue.
219
254
 
220
- - `hue`
255
+ ### `useThemeMode()`
221
256
 
222
- - `darkMode`: A boolean indicating if dark mode is active.
257
+ Derived hook to read and update only the mode.
223
258
 
224
- - `chromaData`
259
+ ### `ThemeProvider`
225
260
 
226
- - **Returns:**
261
+ React provider that applies theme settings and optionally loads/saves user preferences via storage.
227
262
 
228
- - An object containing:
229
- - `className`: A string for dark mode ("dark") or an empty string for light mode.
230
- - `style`: A record of dynamically generated CSS variables for accent colors.
263
+ ### `ThemeStorage` and `defaultThemeStorage`
231
264
 
232
- ### `currentAccentValue(variableName: string)`
265
+ `ThemeStorage` defines `load()` and `save()` for persisting user theme settings. `defaultThemeStorage` uses `localStorage`.
266
+
267
+ ### `getThemeProperties(themeConfig: ThemeConfig)`
233
268
 
234
- Retrieves the current value of a specified CSS variable from the root element.
269
+ Returns `{ dataTheme, style }` where `dataTheme` is `'light' | 'dark'` and `style` maps `--accent-*` CSS variables.
270
+
271
+ ### `currentAccentValue(variableName: string)`
235
272
 
236
- - **Parameters**:
273
+ Returns the current OKLCH value for a CSS variable from `:root`, or `null` if not available.
237
274
 
238
- - `variableName`: The name of the CSS variable to retrieve (e.g., "--accent-500").
275
+ ## Tailwind v4 and Dark Mode Notes
239
276
 
240
- - **Returns**: The OKLCH color value or `null` if not available.
277
+ - Theme tokens and semantic colors are defined in `themes/root.css` using `@theme inline`.
278
+ - Dark mode is controlled via `data-theme="dark" | "light"` on the root element, not `class="dark"`.
241
279
 
242
280
  ## License
243
281
 
package/dist/index.d.ts CHANGED
@@ -1,7 +1,9 @@
1
- export { version } from "./__version__";
2
- export { consistentChroma as getChroma } from "./lib/tw-dynamic-themes/runtime";
3
- export { dynamicTwClasses } from "./lib/tw-dynamic-themes/twPlugin";
4
- export { currentAccentValue, getThemeProperties } from "./src/theme-helpers";
5
- export { useTheme, updateTheme } from "./src/theme-hook";
6
- export { type ThemeConfig, type ThemeMode, type ThemeColor } from "./src/theme.config";
7
- export { ThemeProvider } from "./src/theme-provider";
1
+ export { version } from './__version__';
2
+ export { consistentChroma as getChroma } from './lib/tw-dynamic-themes/runtime';
3
+ export { dynamicTwClasses } from './lib/tw-dynamic-themes/twPlugin';
4
+ export { currentAccentValue, getThemeProperties } from './src/theme-helpers';
5
+ export { useTheme, useThemeHue, useThemeMode } from './src/theme-hook';
6
+ export { defaultThemeStorage } from './src/theme-storage';
7
+ export type { ThemeStorage } from './src/theme-storage';
8
+ export { type ThemeColor, type ThemeConfig, type ThemeMode } from './src/theme.config';
9
+ export { ThemeProvider } from './src/theme-provider';
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
- export { version } from "./__version__";
2
- export { consistentChroma as getChroma } from "./lib/tw-dynamic-themes/runtime";
3
- export { dynamicTwClasses } from "./lib/tw-dynamic-themes/twPlugin";
4
- export { currentAccentValue, getThemeProperties } from "./src/theme-helpers";
5
- export { useTheme, updateTheme } from "./src/theme-hook";
6
- export { ThemeProvider } from "./src/theme-provider";
1
+ export { version } from './__version__';
2
+ export { consistentChroma as getChroma } from './lib/tw-dynamic-themes/runtime';
3
+ export { dynamicTwClasses } from './lib/tw-dynamic-themes/twPlugin';
4
+ export { currentAccentValue, getThemeProperties } from './src/theme-helpers';
5
+ export { useTheme, useThemeHue, useThemeMode } from './src/theme-hook';
6
+ export { defaultThemeStorage } from './src/theme-storage';
7
+ export { ThemeProvider } from './src/theme-provider';
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cyhip-dynamic-themes",
3
- "version": "0.2.2",
3
+ "version": "1.0.0",
4
4
  "description": "Tailwind-powered dynamic color themes for React apps.",
5
5
  "author": "@KassioRF, @CyberHippie-io",
6
6
  "license": "MIT",
@@ -47,6 +47,7 @@
47
47
  },
48
48
  "scripts": {
49
49
  "build": "tsc --project tsconfig.json",
50
+ "dev": "tsc --watch -d",
50
51
  "start": "tsx ./index.ts",
51
52
  "prepublishOnly": "pnpm build"
52
53
  },
@@ -55,6 +56,8 @@
55
56
  "@types/node": "^20",
56
57
  "@types/react": "^18",
57
58
  "commander": "^12.1.0",
59
+ "react": "^19.2.3",
60
+ "react-dom": "^19.2.3",
58
61
  "ts-node": "^10.9.2",
59
62
  "tsx": "^4.19.1",
60
63
  "typescript": "^5.6.3"
@@ -63,14 +66,8 @@
63
66
  "chalk": "^5.3.0",
64
67
  "culori": "^4.0.1",
65
68
  "inquirer": "^12.0.0",
66
- "react": "^18",
67
- "react-dom": "^18",
68
69
  "zustand": "^5.0.1"
69
70
  },
70
- "peerDependencies": {
71
- "react": "^18",
72
- "react-dom": "^18"
73
- },
74
71
  "exports": {
75
72
  ".": {
76
73
  "import": {
@@ -82,5 +79,9 @@
82
79
  "default": "./dist/index.js"
83
80
  }
84
81
  }
82
+ },
83
+ "peerDependencies": {
84
+ "react": "^19.2.3",
85
+ "react-dom": "^19.2.3"
85
86
  }
86
87
  }
@@ -0,0 +1,2 @@
1
+ declare const appThemeProvider = "\n'use client';\nimport { ThemeProvider } from 'cyhip-dynamic-themes';\nimport { defaultTheme, themeStorage } from './theme.config';\n\nexport function AppThemeProvider({ children }: { children: React.ReactNode }) {\n return (\n <ThemeProvider themeConfig={defaultTheme} storage={themeStorage}>\n {children}\n </ThemeProvider>\n );\n}\n \n";
2
+ export default appThemeProvider;
@@ -0,0 +1,2 @@
1
+ var appThemeProvider = "\n'use client';\nimport { ThemeProvider } from 'cyhip-dynamic-themes';\nimport { defaultTheme, themeStorage } from './theme.config';\n\nexport function AppThemeProvider({ children }: { children: React.ReactNode }) {\n return (\n <ThemeProvider themeConfig={defaultTheme} storage={themeStorage}>\n {children}\n </ThemeProvider>\n );\n}\n \n";
2
+ export default appThemeProvider;
@@ -1,2 +1,2 @@
1
- declare const rootCss = "\n@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n/* \n * This is how your global.css or index.css should look like. \n *\n * To see how to apply this variables declared here on tailwind\n * check ./theme-colors.ts properties examples.\n * \n */\n\n@layer base {\n :root {\n /* oklch vars - Applied by dynamic accent colors */\n --background: var(--accent-50);\n --foreground: var(--accent-900);\n --primary: var(--accent-500);\n --primary-foreground: var(--accent-50);\n --secondary: var(--accent-200);\n --secondary-foreground: var(--accent-900);\n --ring: oklch(var(--accent-500) / 0.2);\n --box-shadow: oklch(var(--accent-800) / 0.15);\n --border: oklch(var(--accent-950) / 0.2);\n /* hsl vars */\n --muted: hsl(240 4.8% 95.9% / 0.6);\n --muted-foreground: hsl(240 3.8% 65%);\n --input: hsl(240 5.9% 90% / 0.5);\n }\n * {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n @apply outline-accent-500 dark:outline-accent-400;\n }\n *,\n :after,\n :before {\n border-color: theme('colors.border');\n }\n}\n\n.dark {\n /* oklch vars - Applied by dynamic accent colors */\n --background: var(--accent-950);\n --foreground: var(--accent-100);\n --primary: var(--accent-500);\n --primary-foreground: var(--accent-50);\n --secondary: var(--accent-800);\n --secondary-foreground: var(--accent-200);\n --box-shadow: oklch(var(--accent-100) / 0.1);\n --border: oklch(var(--accent-100) / 0.25);\n\n /* hsl vars */\n --muted: hsl(240 4.8% 25% / 0.7);\n --muted-foreground: hsl(240 3.8% 70%);\n --input: hsl(240 5.9% 80% / 0.8);\n}\n\nbody {\n @apply bg-background dark:bg-background;\n @apply text-foreground;\n overflow-x: hidden;\n}\n\n";
1
+ declare const rootCss = "\n@import 'tailwindcss';\n@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));\n/* \n --------------------------------------------------------------------------\n Tailwind Theme Tokens\n --------------------------------------------------------------------------\n\n Defines the design tokens used by Tailwind utilities.\n\n Example token:\n\n --color-primary\n\n Generates utilities like:\n\n bg-primary\n text-primary\n border-primary\n\n 'inline' is important because values depend on runtime CSS vars\n (--accent-*) injected by the dynamic theme library.\n\n Accent palette\n ----------------\n Maps the dynamic palette to Tailwind colors.\n\n Example utility usage:\n\n bg-accent-500\n text-accent-700\n\n Semantic tokens\n ----------------\n UI roles instead of raw colors:\n\n primary\n secondary\n background\n foreground\n\n Example:\n\n <button class=\"bg-primary text-primary-foreground\">\n\n Extend example:\n \n --color-success: oklch(var(--success));\n -> bg-success text-success\n\n*/\n\n@theme inline {\n /* dynamic accent palette */\n --color-accent-50: oklch(var(--accent-50));\n --color-accent-100: oklch(var(--accent-100));\n --color-accent-200: oklch(var(--accent-200));\n --color-accent-300: oklch(var(--accent-300));\n --color-accent-400: oklch(var(--accent-400));\n --color-accent-500: oklch(var(--accent-500));\n --color-accent-600: oklch(var(--accent-600));\n --color-accent-700: oklch(var(--accent-700));\n --color-accent-800: oklch(var(--accent-800));\n --color-accent-900: oklch(var(--accent-900));\n --color-accent-950: oklch(var(--accent-950));\n\n /* semantic tokens */\n --color-background: oklch(var(--background));\n --color-foreground: oklch(var(--foreground));\n\n --color-primary: oklch(var(--primary));\n --color-primary-foreground: oklch(var(--primary-foreground));\n\n --color-secondary: oklch(var(--secondary));\n --color-secondary-foreground: oklch(var(--secondary-foreground));\n\n --color-border: var(--border);\n --color-ring: var(--ring);\n}\n\n/*\n --------------------------------------------------------------------------\n Light Theme Mapping\n --------------------------------------------------------------------------\n\n Maps the accent palette to semantic UI roles.\n\n Example mapping:\n\n accent-500 \u2192 primary\n accent-200 \u2192 secondary\n accent-100 \u2192 background\n\n This allows changing the entire UI by only updating --accent-*\n variables injected by the theme library.\n\n Customize example:\n\n --background: var(--accent-50);\n --primary: var(--accent-600);\n\n*/\n:root {\n --background: var(--accent-50);\n --foreground: var(--accent-900);\n\n --primary: var(--accent-500);\n --primary-foreground: var(--accent-50);\n\n --secondary: var(--accent-200);\n --secondary-foreground: var(--accent-900);\n\n --ring: oklch(var(--accent-500) / 0.2);\n --border: oklch(var(--accent-950) / 0.2);\n}\n/*\n --------------------------------------------------------------------------\n Dark Theme Mapping\n --------------------------------------------------------------------------\n \n Overrides semantic tokens when .dark class is present.\n \n Example:\n \n <html class=\"dark\">\n \n Strategy: \n dark background -> darker accent\n dark text -> lighter accent\n \n Customize by remapping accent shades:\n \n --background: var(--accent-950);\n \n*/\n[data-theme='dark'] {\n --background: var(--accent-950);\n --foreground: var(--accent-100);\n\n --primary: var(--accent-500);\n --primary-foreground: var(--accent-50);\n\n --secondary: var(--accent-800);\n --secondary-foreground: var(--accent-200);\n\n --border: oklch(var(--accent-100) / 0.25);\n}\n\n/*\n --------------------------------------------------------------------------\n Base Layer\n --------------------------------------------------------------------------\n \n Global low-level styles applied before utilities.\n \n Includes:\n \n - border color from theme token\n - default outline color\n - box-sizing reset\n \n Example:\n \n border -> uses --color-border\n \n Extend here for global resets like:\n \n fonts\n scrollbar\n selection\n \n*/\n\n@layer base {\n *,\n ::before,\n ::after {\n box-sizing: border-box;\n border-color: var(--color-border);\n @apply outline-accent-500 dark:outline-accent-400;\n }\n}\n\n/*\n --------------------------------------------------------------------------\n App Defaults\n --------------------------------------------------------------------------\n \n Sets the main page background and text color using semantic tokens. \n \n*/\nbody {\n @apply bg-background text-foreground;\n overflow-x: hidden;\n}\n\n";
2
2
  export default rootCss;
@@ -1,2 +1,2 @@
1
- var rootCss = "\n@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n/* \n * This is how your global.css or index.css should look like. \n *\n * To see how to apply this variables declared here on tailwind\n * check ./theme-colors.ts properties examples.\n * \n */\n\n@layer base {\n :root {\n /* oklch vars - Applied by dynamic accent colors */\n --background: var(--accent-50);\n --foreground: var(--accent-900);\n --primary: var(--accent-500);\n --primary-foreground: var(--accent-50);\n --secondary: var(--accent-200);\n --secondary-foreground: var(--accent-900);\n --ring: oklch(var(--accent-500) / 0.2);\n --box-shadow: oklch(var(--accent-800) / 0.15);\n --border: oklch(var(--accent-950) / 0.2);\n /* hsl vars */\n --muted: hsl(240 4.8% 95.9% / 0.6);\n --muted-foreground: hsl(240 3.8% 65%);\n --input: hsl(240 5.9% 90% / 0.5);\n }\n * {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n @apply outline-accent-500 dark:outline-accent-400;\n }\n *,\n :after,\n :before {\n border-color: theme('colors.border');\n }\n}\n\n.dark {\n /* oklch vars - Applied by dynamic accent colors */\n --background: var(--accent-950);\n --foreground: var(--accent-100);\n --primary: var(--accent-500);\n --primary-foreground: var(--accent-50);\n --secondary: var(--accent-800);\n --secondary-foreground: var(--accent-200);\n --box-shadow: oklch(var(--accent-100) / 0.1);\n --border: oklch(var(--accent-100) / 0.25);\n\n /* hsl vars */\n --muted: hsl(240 4.8% 25% / 0.7);\n --muted-foreground: hsl(240 3.8% 70%);\n --input: hsl(240 5.9% 80% / 0.8);\n}\n\nbody {\n @apply bg-background dark:bg-background;\n @apply text-foreground;\n overflow-x: hidden;\n}\n\n";
1
+ var rootCss = "\n@import 'tailwindcss';\n@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));\n/* \n --------------------------------------------------------------------------\n Tailwind Theme Tokens\n --------------------------------------------------------------------------\n\n Defines the design tokens used by Tailwind utilities.\n\n Example token:\n\n --color-primary\n\n Generates utilities like:\n\n bg-primary\n text-primary\n border-primary\n\n 'inline' is important because values depend on runtime CSS vars\n (--accent-*) injected by the dynamic theme library.\n\n Accent palette\n ----------------\n Maps the dynamic palette to Tailwind colors.\n\n Example utility usage:\n\n bg-accent-500\n text-accent-700\n\n Semantic tokens\n ----------------\n UI roles instead of raw colors:\n\n primary\n secondary\n background\n foreground\n\n Example:\n\n <button class=\"bg-primary text-primary-foreground\">\n\n Extend example:\n \n --color-success: oklch(var(--success));\n -> bg-success text-success\n\n*/\n\n@theme inline {\n /* dynamic accent palette */\n --color-accent-50: oklch(var(--accent-50));\n --color-accent-100: oklch(var(--accent-100));\n --color-accent-200: oklch(var(--accent-200));\n --color-accent-300: oklch(var(--accent-300));\n --color-accent-400: oklch(var(--accent-400));\n --color-accent-500: oklch(var(--accent-500));\n --color-accent-600: oklch(var(--accent-600));\n --color-accent-700: oklch(var(--accent-700));\n --color-accent-800: oklch(var(--accent-800));\n --color-accent-900: oklch(var(--accent-900));\n --color-accent-950: oklch(var(--accent-950));\n\n /* semantic tokens */\n --color-background: oklch(var(--background));\n --color-foreground: oklch(var(--foreground));\n\n --color-primary: oklch(var(--primary));\n --color-primary-foreground: oklch(var(--primary-foreground));\n\n --color-secondary: oklch(var(--secondary));\n --color-secondary-foreground: oklch(var(--secondary-foreground));\n\n --color-border: var(--border);\n --color-ring: var(--ring);\n}\n\n/*\n --------------------------------------------------------------------------\n Light Theme Mapping\n --------------------------------------------------------------------------\n\n Maps the accent palette to semantic UI roles.\n\n Example mapping:\n\n accent-500 \u2192 primary\n accent-200 \u2192 secondary\n accent-100 \u2192 background\n\n This allows changing the entire UI by only updating --accent-*\n variables injected by the theme library.\n\n Customize example:\n\n --background: var(--accent-50);\n --primary: var(--accent-600);\n\n*/\n:root {\n --background: var(--accent-50);\n --foreground: var(--accent-900);\n\n --primary: var(--accent-500);\n --primary-foreground: var(--accent-50);\n\n --secondary: var(--accent-200);\n --secondary-foreground: var(--accent-900);\n\n --ring: oklch(var(--accent-500) / 0.2);\n --border: oklch(var(--accent-950) / 0.2);\n}\n/*\n --------------------------------------------------------------------------\n Dark Theme Mapping\n --------------------------------------------------------------------------\n \n Overrides semantic tokens when .dark class is present.\n \n Example:\n \n <html class=\"dark\">\n \n Strategy: \n dark background -> darker accent\n dark text -> lighter accent\n \n Customize by remapping accent shades:\n \n --background: var(--accent-950);\n \n*/\n[data-theme='dark'] {\n --background: var(--accent-950);\n --foreground: var(--accent-100);\n\n --primary: var(--accent-500);\n --primary-foreground: var(--accent-50);\n\n --secondary: var(--accent-800);\n --secondary-foreground: var(--accent-200);\n\n --border: oklch(var(--accent-100) / 0.25);\n}\n\n/*\n --------------------------------------------------------------------------\n Base Layer\n --------------------------------------------------------------------------\n \n Global low-level styles applied before utilities.\n \n Includes:\n \n - border color from theme token\n - default outline color\n - box-sizing reset\n \n Example:\n \n border -> uses --color-border\n \n Extend here for global resets like:\n \n fonts\n scrollbar\n selection\n \n*/\n\n@layer base {\n *,\n ::before,\n ::after {\n box-sizing: border-box;\n border-color: var(--color-border);\n @apply outline-accent-500 dark:outline-accent-400;\n }\n}\n\n/*\n --------------------------------------------------------------------------\n App Defaults\n --------------------------------------------------------------------------\n \n Sets the main page background and text color using semantic tokens. \n \n*/\nbody {\n @apply bg-background text-foreground;\n overflow-x: hidden;\n}\n\n";
2
2
  export default rootCss;
@@ -1,2 +1,2 @@
1
- declare const themeSwitcher = "\n// \"use client\"; // enable this if you are using Nextjs\n/**\n * ThemeSwitcher component\n *\n * A simlpe example about how you can define a ThemeSwitcher component.\n *\n */\n\nimport { getChroma, ThemeConfig, useTheme } from 'cyhip-dynamic-themes';\nimport { forwardRef, HTMLAttributes, useEffect, useState } from 'react';\nimport { chromaData, hueScheme } from './theme.config';\n\n/**\n * This methods are used only to build a gradient sample based on the hue value.\n * Used for a visual referrence as a \"icon\" of the theme on the buttons.\n */\nconst buildThemeSample = (hue: number) => {\n const oklch = 'oklch(' + getChroma(5, hue, chromaData) + ')';\n return oklch;\n};\n\nconst availableThemes: Record<string, string> = Object.keys(hueScheme).reduce(\n (acc, key) => {\n const value = hueScheme[key];\n acc[key] = buildThemeSample(value);\n return acc;\n },\n {} as Record<string, string>\n);\n\nconst ThemeSwitcher = forwardRef<\n HTMLDivElement,\n HTMLAttributes<HTMLDivElement>\n>(({ ...props }, ref) => {\n /** To initialize here you can manage cookie values to reminder user preferences. */\n const { theme, setTheme } = useTheme();\n const [hue, setHue] = useState(theme.hue);\n const [darkMode, setDarkMode] = useState(theme.mode === 'light');\n\n useEffect(() => {\n setTheme({\n hue,\n mode: darkMode ? 'dark' : 'light',\n chromaData: chromaData,\n } as ThemeConfig);\n }, [hue, darkMode, setTheme]);\n\n return (\n <>\n <div\n ref={ref}\n className=\"bg-accent-200/40 dark:bg-accent-700/40 grid grid-cols-3 rounded-sm gap-2 p-6\"\n {...props}\n >\n {Object.keys(availableThemes).map((key) => (\n <button\n key={key}\n className=\"bg-background px-4 py-1 text-sm font-medium rounded-ms border flex space-x-2 hover:ring-1 hover:ring-offset-2 hover:ring-offset-background hover:ring-primary\"\n onClick={() => setHue(hueScheme[key])}\n >\n <span\n className=\"w-4 h-4 rounded-full\"\n style={{\n background: availableThemes[key],\n }}\n ></span>\n <span>{key}</span>\n </button>\n ))}\n <div className=\"col-span-3 grid grid-cols-2 gap-x-2 mx-auto\">\n <button\n className=\"bg-background border px-4 py-1 text-sm font-medium rounded-ms hover:ring-1 hover:ring-offset-2 hover:ring-offset-background hover:ring-primary\"\n onClick={() => setDarkMode(false)}\n >\n Light\n </button>\n <button\n className=\"bg-background border px-4 py-1 text-sm font-medium rounded-ms hover:ring-1 hover:ring-offset-2 hover:ring-offset-background hover:ring-primary\"\n onClick={() => setDarkMode(true)}\n >\n Dark\n </button>\n </div>\n </div>\n </>\n );\n});\n\nThemeSwitcher.displayName = 'ThemeSwitcher';\n\nexport { ThemeSwitcher };\n\n\n";
1
+ declare const themeSwitcher = "\n'use client'; // enable this if you are using Nextjs\n/**\n * ThemeSwitcher component\n *\n * A simlpe example about how you can define a ThemeSwitcher component.\n *\n */\nimport { availableThemes, hueScheme } from '@/themes/theme.config';\nimport { useThemeHue, useThemeMode } from 'cyhip-dynamic-themes';\nimport { useMemo } from 'react';\n\nexport function ThemeSwitcher() {\n return (\n <div className=\"auto-cols grid grid-flow-row gap-2\">\n <div className=\"rounded border\">\n <ColorPaletteMenu />\n </div>\n <div>\n <ThemeModeMenu />\n </div>\n </div>\n );\n}\n\nfunction ColorPaletteMenu() {\n const { hue, setThemeHue } = useThemeHue();\n\n const selected = useMemo(\n () => Object.keys(hueScheme).find((key) => hueScheme[key] === hue) ?? null,\n [hue],\n );\n\n return (\n <div className=\"m-3 grid auto-cols-max grid-flow-col gap-4\">\n {Object.keys(availableThemes).map((key) => (\n <button\n type=\"button\"\n key={key}\n className=\"relative cursor-pointer\"\n onClick={() => setThemeHue(hueScheme[key])}\n >\n {selected === key && (\n <span className=\"border-accent-400 absolute -inset-1 block h-8 w-8 rounded-full border-2\" />\n )}\n <span\n className=\"block h-6 w-6 rounded-full\"\n style={{ background: availableThemes[key] }}\n />\n </button>\n ))}\n </div>\n );\n}\n\nfunction ThemeModeMenu() {\n const { mode, setThemeMode } = useThemeMode();\n\n const btnStyle = 'border px-2 py-2 rounded-md cursor-pointer hover:ring-1';\n return (\n <div className=\"m-2 flex justify-center gap-4\">\n <button\n type=\"button\"\n className={`${btnStyle} ${mode === 'light' ? 'ring-1' : ''}`}\n onClick={() => setThemeMode('light')}\n >\n Light\n </button>\n <button\n type=\"button\"\n className={`${btnStyle} ${mode === 'dark' ? 'ring-1' : ''}`}\n onClick={() => setThemeMode('dark')}\n >\n Dark\n </button>\n </div>\n );\n}\n\n\n";
2
2
  export default themeSwitcher;
@@ -1,2 +1,2 @@
1
- var themeSwitcher = "\n// \"use client\"; // enable this if you are using Nextjs\n/**\n * ThemeSwitcher component\n *\n * A simlpe example about how you can define a ThemeSwitcher component.\n *\n */\n\nimport { getChroma, ThemeConfig, useTheme } from 'cyhip-dynamic-themes';\nimport { forwardRef, HTMLAttributes, useEffect, useState } from 'react';\nimport { chromaData, hueScheme } from './theme.config';\n\n/**\n * This methods are used only to build a gradient sample based on the hue value.\n * Used for a visual referrence as a \"icon\" of the theme on the buttons.\n */\nconst buildThemeSample = (hue: number) => {\n const oklch = 'oklch(' + getChroma(5, hue, chromaData) + ')';\n return oklch;\n};\n\nconst availableThemes: Record<string, string> = Object.keys(hueScheme).reduce(\n (acc, key) => {\n const value = hueScheme[key];\n acc[key] = buildThemeSample(value);\n return acc;\n },\n {} as Record<string, string>\n);\n\nconst ThemeSwitcher = forwardRef<\n HTMLDivElement,\n HTMLAttributes<HTMLDivElement>\n>(({ ...props }, ref) => {\n /** To initialize here you can manage cookie values to reminder user preferences. */\n const { theme, setTheme } = useTheme();\n const [hue, setHue] = useState(theme.hue);\n const [darkMode, setDarkMode] = useState(theme.mode === 'light');\n\n useEffect(() => {\n setTheme({\n hue,\n mode: darkMode ? 'dark' : 'light',\n chromaData: chromaData,\n } as ThemeConfig);\n }, [hue, darkMode, setTheme]);\n\n return (\n <>\n <div\n ref={ref}\n className=\"bg-accent-200/40 dark:bg-accent-700/40 grid grid-cols-3 rounded-sm gap-2 p-6\"\n {...props}\n >\n {Object.keys(availableThemes).map((key) => (\n <button\n key={key}\n className=\"bg-background px-4 py-1 text-sm font-medium rounded-ms border flex space-x-2 hover:ring-1 hover:ring-offset-2 hover:ring-offset-background hover:ring-primary\"\n onClick={() => setHue(hueScheme[key])}\n >\n <span\n className=\"w-4 h-4 rounded-full\"\n style={{\n background: availableThemes[key],\n }}\n ></span>\n <span>{key}</span>\n </button>\n ))}\n <div className=\"col-span-3 grid grid-cols-2 gap-x-2 mx-auto\">\n <button\n className=\"bg-background border px-4 py-1 text-sm font-medium rounded-ms hover:ring-1 hover:ring-offset-2 hover:ring-offset-background hover:ring-primary\"\n onClick={() => setDarkMode(false)}\n >\n Light\n </button>\n <button\n className=\"bg-background border px-4 py-1 text-sm font-medium rounded-ms hover:ring-1 hover:ring-offset-2 hover:ring-offset-background hover:ring-primary\"\n onClick={() => setDarkMode(true)}\n >\n Dark\n </button>\n </div>\n </div>\n </>\n );\n});\n\nThemeSwitcher.displayName = 'ThemeSwitcher';\n\nexport { ThemeSwitcher };\n\n\n";
1
+ var themeSwitcher = "\n'use client'; // enable this if you are using Nextjs\n/**\n * ThemeSwitcher component\n *\n * A simlpe example about how you can define a ThemeSwitcher component.\n *\n */\nimport { availableThemes, hueScheme } from '@/themes/theme.config';\nimport { useThemeHue, useThemeMode } from 'cyhip-dynamic-themes';\nimport { useMemo } from 'react';\n\nexport function ThemeSwitcher() {\n return (\n <div className=\"auto-cols grid grid-flow-row gap-2\">\n <div className=\"rounded border\">\n <ColorPaletteMenu />\n </div>\n <div>\n <ThemeModeMenu />\n </div>\n </div>\n );\n}\n\nfunction ColorPaletteMenu() {\n const { hue, setThemeHue } = useThemeHue();\n\n const selected = useMemo(\n () => Object.keys(hueScheme).find((key) => hueScheme[key] === hue) ?? null,\n [hue],\n );\n\n return (\n <div className=\"m-3 grid auto-cols-max grid-flow-col gap-4\">\n {Object.keys(availableThemes).map((key) => (\n <button\n type=\"button\"\n key={key}\n className=\"relative cursor-pointer\"\n onClick={() => setThemeHue(hueScheme[key])}\n >\n {selected === key && (\n <span className=\"border-accent-400 absolute -inset-1 block h-8 w-8 rounded-full border-2\" />\n )}\n <span\n className=\"block h-6 w-6 rounded-full\"\n style={{ background: availableThemes[key] }}\n />\n </button>\n ))}\n </div>\n );\n}\n\nfunction ThemeModeMenu() {\n const { mode, setThemeMode } = useThemeMode();\n\n const btnStyle = 'border px-2 py-2 rounded-md cursor-pointer hover:ring-1';\n return (\n <div className=\"m-2 flex justify-center gap-4\">\n <button\n type=\"button\"\n className={`${btnStyle} ${mode === 'light' ? 'ring-1' : ''}`}\n onClick={() => setThemeMode('light')}\n >\n Light\n </button>\n <button\n type=\"button\"\n className={`${btnStyle} ${mode === 'dark' ? 'ring-1' : ''}`}\n onClick={() => setThemeMode('dark')}\n >\n Dark\n </button>\n </div>\n );\n}\n\n\n";
2
2
  export default themeSwitcher;
@@ -1,2 +1,2 @@
1
- declare const themeConfig = "\n\n/**\n * HUE THEMES\n *\n * Define the available color palettes here!\n *\n * The palettes are based on HUE values.\n *\n * To add or modify a HUE palette, explore and preview colors at:\n * https://oklch.com/#70,0.1,250,100\n *\n */\n\nexport const hueScheme: Record<string, number> = {\n default: -1,\n blue: 250,\n green: 150,\n orange: 35,\n pink: 0,\n purple: 316,\n};\n\n/**\n *\n * Default Chroma for each shade available on theme. See more at:\n * https://evilmartians.com/chronicles/better-dynamic-themes-in-tailwind-with-oklch-color-magic\n *\n */\nexport const chromaData: Record<number, number> = {\n 0: 0.0108,\n 1: 0.0121,\n 2: 0.0609,\n 3: 0.0908,\n 4: 0.1398,\n 5: 0.1472,\n 6: 0.1299,\n 7: 0.1067,\n 8: 0.0898,\n 9: 0.0726,\n 10: 0.005,\n};\n\n";
1
+ declare const themeConfig = "\n/**\n * HUE THEMES\n *\n * Define the available color palettes here!\n *\n * The palettes are based on HUE values.\n *\n * To add or modify a HUE palette, explore and preview colors at:\n * https://oklch.com/#70,0.1,250,100\n *\n */\n\nimport {\n defaultThemeStorage,\n getChroma,\n type ThemeConfig,\n type ThemeStorage,\n} from 'cyhip-dynamic-themes';\n\nexport const hueScheme: Record<string, number> = {\n default: -1,\n blue: 250,\n green: 150,\n orange: 35,\n pink: 0,\n purple: 316,\n};\n\n/**\n *\n * Default Chroma for each shade available on theme. See more at:\n * https://evilmartians.com/chronicles/better-dynamic-themes-in-tailwind-with-oklch-color-magic\n *\n */\nexport const chromaData: Record<number, number> = {\n 0: 0.0108,\n 1: 0.0121,\n 2: 0.0609,\n 3: 0.0908,\n 4: 0.1398,\n 5: 0.1472,\n 6: 0.1299,\n 7: 0.1067,\n 8: 0.0898,\n 9: 0.0726,\n 10: 0.005,\n};\n\n/**\n * Declares the available color palette.\n */\n\ntype ITheme = {\n [name: string]: string;\n};\n\nexport const availableThemes: ITheme = Object.keys(hueScheme).reduce((acc, key) => {\n const hueValue = hueScheme[key];\n acc[key] = 'oklch(' + getChroma(5, hueValue, chromaData) + ')';\n return acc;\n}, {} as ITheme);\n\n/**\n * Default theme configuration used by the application.\n *\n * This serves as the base theme when the app initializes.\n * If theme storage is enabled and a user preference exists,\n * the stored values will override these defaults.\n *\n * You can customize this object to define the initial\n * color hue, mode (light/dark), and chroma settings\n * used throughout your application.\n */\nexport const defaultTheme: ThemeConfig = {\n hue: 250,\n mode: 'light',\n chromaData: chromaData,\n};\n\n/**\n * Theme persistence handler.\n *\n * Defines how the user's theme preferences (hue and mode)\n * are loaded and saved.\n *\n * By default, the library provides a localStorage-based\n * implementation (defaultThemeStorage).\n *\n * If you prefer to manage persistence using a different\n * strategy (cookies, API, indexedDB, etc.), you can replace\n * this with your own ThemeStorage implementation.\n */\nexport const themeStorage: ThemeStorage = defaultThemeStorage;\n\n";
2
2
  export default themeConfig;
@@ -1,2 +1,2 @@
1
- var themeConfig = "\n\n/**\n * HUE THEMES\n *\n * Define the available color palettes here!\n *\n * The palettes are based on HUE values.\n *\n * To add or modify a HUE palette, explore and preview colors at:\n * https://oklch.com/#70,0.1,250,100\n *\n */\n\nexport const hueScheme: Record<string, number> = {\n default: -1,\n blue: 250,\n green: 150,\n orange: 35,\n pink: 0,\n purple: 316,\n};\n\n/**\n *\n * Default Chroma for each shade available on theme. See more at:\n * https://evilmartians.com/chronicles/better-dynamic-themes-in-tailwind-with-oklch-color-magic\n *\n */\nexport const chromaData: Record<number, number> = {\n 0: 0.0108,\n 1: 0.0121,\n 2: 0.0609,\n 3: 0.0908,\n 4: 0.1398,\n 5: 0.1472,\n 6: 0.1299,\n 7: 0.1067,\n 8: 0.0898,\n 9: 0.0726,\n 10: 0.005,\n};\n\n";
1
+ var themeConfig = "\n/**\n * HUE THEMES\n *\n * Define the available color palettes here!\n *\n * The palettes are based on HUE values.\n *\n * To add or modify a HUE palette, explore and preview colors at:\n * https://oklch.com/#70,0.1,250,100\n *\n */\n\nimport {\n defaultThemeStorage,\n getChroma,\n type ThemeConfig,\n type ThemeStorage,\n} from 'cyhip-dynamic-themes';\n\nexport const hueScheme: Record<string, number> = {\n default: -1,\n blue: 250,\n green: 150,\n orange: 35,\n pink: 0,\n purple: 316,\n};\n\n/**\n *\n * Default Chroma for each shade available on theme. See more at:\n * https://evilmartians.com/chronicles/better-dynamic-themes-in-tailwind-with-oklch-color-magic\n *\n */\nexport const chromaData: Record<number, number> = {\n 0: 0.0108,\n 1: 0.0121,\n 2: 0.0609,\n 3: 0.0908,\n 4: 0.1398,\n 5: 0.1472,\n 6: 0.1299,\n 7: 0.1067,\n 8: 0.0898,\n 9: 0.0726,\n 10: 0.005,\n};\n\n/**\n * Declares the available color palette.\n */\n\ntype ITheme = {\n [name: string]: string;\n};\n\nexport const availableThemes: ITheme = Object.keys(hueScheme).reduce((acc, key) => {\n const hueValue = hueScheme[key];\n acc[key] = 'oklch(' + getChroma(5, hueValue, chromaData) + ')';\n return acc;\n}, {} as ITheme);\n\n/**\n * Default theme configuration used by the application.\n *\n * This serves as the base theme when the app initializes.\n * If theme storage is enabled and a user preference exists,\n * the stored values will override these defaults.\n *\n * You can customize this object to define the initial\n * color hue, mode (light/dark), and chroma settings\n * used throughout your application.\n */\nexport const defaultTheme: ThemeConfig = {\n hue: 250,\n mode: 'light',\n chromaData: chromaData,\n};\n\n/**\n * Theme persistence handler.\n *\n * Defines how the user's theme preferences (hue and mode)\n * are loaded and saved.\n *\n * By default, the library provides a localStorage-based\n * implementation (defaultThemeStorage).\n *\n * If you prefer to manage persistence using a different\n * strategy (cookies, API, indexedDB, etc.), you can replace\n * this with your own ThemeStorage implementation.\n */\nexport const themeStorage: ThemeStorage = defaultThemeStorage;\n\n";
2
2
  export default themeConfig;
package/dist/src/cli.js CHANGED
@@ -35,38 +35,39 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
35
35
  if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
36
  }
37
37
  };
38
- import chalk from "chalk";
39
- import fs from "fs";
40
- import inquirer from "inquirer";
41
- import path from "path";
42
- import rootCss from "./_templates/root-css.js";
43
- import themeColors from "./_templates/theme-colors.js";
44
- import themeConfig from "./_templates/theme.config.js";
45
- import themeSwitcher from "./_templates/theme-switcher.js";
38
+ import chalk from 'chalk';
39
+ import fs from 'fs';
40
+ import inquirer from 'inquirer';
41
+ import path from 'path';
42
+ import appThemeProvider from './_templates/app-theme-provider.js';
43
+ import rootCss from './_templates/root-css.js';
44
+ import themeSwitcher from './_templates/theme-switcher.js';
45
+ import themeConfig from './_templates/theme.config.js';
46
46
  var initThemesDirectory = function (themesDir) {
47
47
  if (!fs.existsSync(themesDir)) {
48
48
  fs.mkdirSync(themesDir);
49
- fs.writeFileSync(path.join(themesDir, "theme.config.ts"), themeConfig.trim());
50
- fs.writeFileSync(path.join(themesDir, "root.css"), rootCss.trim());
51
- fs.writeFileSync(path.join(themesDir, "theme-colors.ts"), themeColors.trim());
52
- fs.writeFileSync(path.join(themesDir, "theme-switcher.tsx"), themeSwitcher.trim());
49
+ fs.writeFileSync(path.join(themesDir, 'theme.config.ts'), themeConfig.trim());
50
+ fs.writeFileSync(path.join(themesDir, 'root.css'), rootCss.trim());
51
+ fs.writeFileSync(path.join(themesDir, 'app-theme-provider.tsx'), appThemeProvider.trim());
52
+ fs.writeFileSync(path.join(themesDir, 'theme-switcher.tsx'), themeSwitcher.trim());
53
53
  console.log(chalk.green("Default files created in \"".concat(themesDir, "\".")));
54
- console.log(chalk.cyan("\n\t /themes/\n" +
55
- "\t ├── theme.config.ts\n" +
56
- "\t ├── root.css\n" +
57
- "\t ├── theme-colors.ts\n" +
58
- "\t └── theme-switcher.tsx\n"));
59
- console.log(chalk.cyan(" 1. Update your tailwind.conf.ts as described in /themes/theme-colors.ts"));
60
- console.log(chalk.cyan(" 2. Import /themes/root.css into the base tsx of your application, e.g.: [ Main.tsx or App.tsx or Layout.tsx ]"));
61
- console.log(chalk.cyan(" 3. Add the /themes/theme-switcher.tsx component to your application and to see how it works."));
54
+ console.log(chalk.cyan('\n\t /themes/\n' +
55
+ '\t ├── app-theme-provider.tsx\n' +
56
+ '\t ├── root.css\n' +
57
+ '\t ├── theme.config.ts\n' +
58
+ '\t └── theme-switcher.tsx\n'));
59
+ console.log(chalk.cyan(" 1. Import /themes/root.css into the base tsx of your application, e.g.: (Main.tsx or App.tsx or Layout.tsx)"));
60
+ console.log(chalk.cyan(" 2. Wrap your application with the AppThemeProvider in your base TSX file (Main.tsx, App.tsx, or Layout.tsx)."));
61
+ console.log(chalk.cyan(" 3. Customize the color palette in /themes/theme.config.ts."));
62
+ console.log(chalk.cyan(" 4. Add the /themes/theme-switcher.tsx component to your application and to see how it works."));
62
63
  }
63
64
  else {
64
65
  console.error(chalk.red("A \"/themes/ folder already exists, it was not possible to initialize the files."));
65
66
  }
66
67
  };
67
68
  var checkSrcDirectory = function () {
68
- var srcPath = path.join(process.cwd(), "src");
69
- return fs.existsSync(srcPath) ? "src" : ".";
69
+ var srcPath = path.join(process.cwd(), 'src');
70
+ return fs.existsSync(srcPath) ? 'src' : '.';
70
71
  };
71
72
  var askDirectory = function () { return __awaiter(void 0, void 0, void 0, function () {
72
73
  var srcExists, selectedDir;
@@ -76,16 +77,16 @@ var askDirectory = function () { return __awaiter(void 0, void 0, void 0, functi
76
77
  srcExists = checkSrcDirectory();
77
78
  return [4 /*yield*/, inquirer.prompt([
78
79
  {
79
- type: "list",
80
- name: "selectedDir",
81
- message: "Where would you like to initialize the themes directory?",
80
+ type: 'list',
81
+ name: 'selectedDir',
82
+ message: 'Where would you like to initialize the themes directory?',
82
83
  choices: [
83
84
  {
84
- name: "./src (if exists)",
85
- value: "src",
86
- disabled: !fs.existsSync(path.join(process.cwd(), "src")),
85
+ name: './src (if exists)',
86
+ value: 'src',
87
+ disabled: !fs.existsSync(path.join(process.cwd(), 'src')),
87
88
  },
88
- { name: "./ (project root)", value: "." },
89
+ { name: './ (project root)', value: '.' },
89
90
  ],
90
91
  default: srcExists,
91
92
  },
@@ -103,13 +104,13 @@ var runInit = function () { return __awaiter(void 0, void 0, void 0, function ()
103
104
  case 0: return [4 /*yield*/, askDirectory()];
104
105
  case 1:
105
106
  directory = _a.sent();
106
- themesDir = path.join(process.cwd(), directory, "themes");
107
+ themesDir = path.join(process.cwd(), directory, 'themes');
107
108
  initThemesDirectory(themesDir);
108
109
  return [2 /*return*/];
109
110
  }
110
111
  });
111
112
  }); };
112
113
  var _a = process.argv, command = _a[2];
113
- if (command === "init") {
114
+ if (command === 'init') {
114
115
  runInit();
115
116
  }
@@ -1,3 +1,4 @@
1
+ import { ThemeConfig } from './theme.config';
1
2
  /**
2
3
  * getThemeProperties
3
4
  *
@@ -5,12 +6,12 @@
5
6
  *
6
7
  * - If `hue` is "-1", the theme is monochromatic, which affects the accent color behavior.
7
8
  * - Returns an object with:
8
- * - `className`: A string containing the class for dark mode ("dark") or an empty string for light mode.
9
+ * - `dataTheme`: A string containing the class for dark mode ("dark") or an empty string for light mode.
9
10
  * - `style`: A record with dynamically generated CSS variables for the accent colors.
10
11
  *
11
12
  */
12
- export declare const getThemeProperties: (hue: number, darkMode: boolean, chromaData?: Record<number, number>) => {
13
- className: string;
13
+ export declare const getThemeProperties: (themeConfig: ThemeConfig) => {
14
+ dataTheme: string;
14
15
  style: Record<string, string>;
15
16
  };
16
17
  /**
@@ -1,5 +1,4 @@
1
1
  import { getVariables } from '../lib/tw-dynamic-themes/runtime';
2
- import { chromaData as defaultChromaData } from './theme.config';
3
2
  /**
4
3
  * getThemeProperties
5
4
  *
@@ -7,31 +6,25 @@ import { chromaData as defaultChromaData } from './theme.config';
7
6
  *
8
7
  * - If `hue` is "-1", the theme is monochromatic, which affects the accent color behavior.
9
8
  * - Returns an object with:
10
- * - `className`: A string containing the class for dark mode ("dark") or an empty string for light mode.
9
+ * - `dataTheme`: A string containing the class for dark mode ("dark") or an empty string for light mode.
11
10
  * - `style`: A record with dynamically generated CSS variables for the accent colors.
12
11
  *
13
12
  */
14
- export var getThemeProperties = function (hue, darkMode, chromaData) {
15
- if (chromaData === void 0) { chromaData = defaultChromaData; }
16
- var whitePalette = hue == -1;
13
+ export var getThemeProperties = function (themeConfig) {
14
+ var whitePalette = themeConfig.hue == -1;
15
+ var darkMode = themeConfig.mode == 'dark';
17
16
  var accent = getVariables({
18
17
  baseName: 'accent',
19
- hue: hue,
20
- chromaData: chromaData,
18
+ hue: themeConfig.hue,
19
+ chromaData: themeConfig.chromaData,
21
20
  });
22
- // whitePalette have a different accent behavior for accent values
21
+ // white pallet have a different accent behavior
23
22
  if (whitePalette) {
24
- accent.push([
25
- '--accent-500',
26
- darkMode ? '1.000 0.000 89.876' : '0.212 0.000 359.000',
27
- ]);
28
- accent.push([
29
- '--accent-50',
30
- darkMode ? '0.212 0.000 359.000' : '1.000 0.000 89.876',
31
- ]);
23
+ accent.push(['--accent-500', darkMode ? '1.000 0.000 89.876' : '0.212 0.000 359.000']);
24
+ accent.push(['--accent-50', darkMode ? '0.212 0.000 359.000' : '1.000 0.000 89.876']);
32
25
  }
33
26
  return {
34
- className: darkMode ? 'dark' : '',
27
+ dataTheme: darkMode ? 'dark' : 'light',
35
28
  style: Object.fromEntries(accent),
36
29
  };
37
30
  };
@@ -1,8 +1,20 @@
1
- import { ThemeConfig } from "./theme.config";
2
- declare const updateTheme: (themeConfig: ThemeConfig) => void;
1
+ import { ThemeStorage } from './theme-storage';
2
+ import { ThemeConfig, ThemeMode } from './theme.config';
3
3
  type ThemeState = {
4
4
  theme: ThemeConfig;
5
+ storage: ThemeStorage | null;
6
+ setStorage: (storage: ThemeStorage | null) => void;
5
7
  setTheme: (theme: ThemeConfig) => void;
8
+ setThemeHue: (hue: number) => void;
9
+ setThemeMode: (mode: ThemeMode) => void;
6
10
  };
7
11
  declare const useTheme: import("zustand").UseBoundStore<import("zustand").StoreApi<ThemeState>>;
8
- export { updateTheme, useTheme };
12
+ declare const useThemeMode: () => {
13
+ mode: ThemeMode;
14
+ setThemeMode: (mode: ThemeMode) => void;
15
+ };
16
+ declare const useThemeHue: () => {
17
+ hue: number;
18
+ setThemeHue: (hue: number) => void;
19
+ };
20
+ export { useTheme, useThemeHue, useThemeMode };
@@ -1,20 +1,65 @@
1
- import { getThemeProperties } from "./theme-helpers";
1
+ var __assign = (this && this.__assign) || function () {
2
+ __assign = Object.assign || function(t) {
3
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
4
+ s = arguments[i];
5
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
+ t[p] = s[p];
7
+ }
8
+ return t;
9
+ };
10
+ return __assign.apply(this, arguments);
11
+ };
2
12
  import { create } from 'zustand';
3
- import { chromaData } from "./theme.config";
4
- var updateTheme = function (themeConfig) {
5
- var _a = getThemeProperties(themeConfig.hue, themeConfig.mode === 'dark', themeConfig.chromaData), className = _a.className, style = _a.style;
6
- document.documentElement.className = className;
7
- document.documentElement.style.setProperty('--color-scheme', themeConfig.mode);
13
+ import { useShallow } from 'zustand/react/shallow';
14
+ import { getThemeProperties } from './theme-helpers';
15
+ import { chromaData } from './theme.config';
16
+ var applyTheme = function (themeConfig, storage) {
17
+ var _a = getThemeProperties(themeConfig), dataTheme = _a.dataTheme, style = _a.style;
18
+ document.documentElement.setAttribute('data-theme', dataTheme);
8
19
  Object.entries(style).forEach(function (_a) {
9
20
  var key = _a[0], value = _a[1];
10
21
  document.documentElement.style.setProperty(key, value);
11
22
  });
23
+ if (storage) {
24
+ storage.save(themeConfig);
25
+ }
12
26
  };
13
- var useTheme = create(function (set, get) { return ({
27
+ var useTheme = create(function (set) { return ({
14
28
  theme: { hue: 250, mode: 'light', chromaData: chromaData },
29
+ storage: null,
15
30
  setTheme: function (theme) {
16
- set({ theme: theme });
17
- updateTheme(theme);
31
+ set(function (state) {
32
+ applyTheme(theme, state.storage);
33
+ return { theme: theme };
34
+ });
35
+ },
36
+ setStorage: function (storage) {
37
+ set({ storage: storage });
38
+ },
39
+ setThemeHue: function (hue) {
40
+ set(function (state) {
41
+ var theme = __assign(__assign({}, state.theme), { hue: hue });
42
+ applyTheme(theme, state.storage);
43
+ return { theme: theme };
44
+ });
45
+ },
46
+ setThemeMode: function (mode) {
47
+ set(function (state) {
48
+ var theme = __assign(__assign({}, state.theme), { mode: mode });
49
+ applyTheme(theme, state.storage);
50
+ return { theme: theme };
51
+ });
18
52
  },
19
53
  }); });
20
- export { updateTheme, useTheme };
54
+ /* selectors (derived hooks) */
55
+ var themeModeSelector = function (s) { return ({
56
+ mode: s.theme.mode,
57
+ setThemeMode: s.setThemeMode,
58
+ }); };
59
+ var themeHueSelector = function (s) { return ({
60
+ hue: s.theme.hue,
61
+ setThemeHue: s.setThemeHue,
62
+ }); };
63
+ var useThemeMode = function () { return useTheme(useShallow(themeModeSelector)); };
64
+ var useThemeHue = function () { return useTheme(useShallow(themeHueSelector)); };
65
+ export { useTheme, useThemeHue, useThemeMode };
@@ -1,8 +1,9 @@
1
- import React, { type ReactNode } from 'react';
1
+ import { ThemeStorage } from './theme-storage';
2
2
  import { ThemeConfig } from './theme.config';
3
3
  interface ThemeProviderProps {
4
- children: ReactNode;
4
+ children: React.ReactNode;
5
5
  themeConfig: ThemeConfig;
6
+ storage?: ThemeStorage;
6
7
  }
7
- declare const ThemeProvider: React.FC<ThemeProviderProps>;
8
+ declare function ThemeProvider({ children, themeConfig, storage }: ThemeProviderProps): import("react/jsx-runtime").JSX.Element | null;
8
9
  export { ThemeProvider };
@@ -1,19 +1,36 @@
1
- 'use client'; // enable on Nextjs
1
+ 'use client'; // enable for next
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
2
13
  import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
3
- import { updateTheme, useTheme } from './theme-hook';
4
- import { useEffect, useState } from 'react';
5
- var ThemeProvider = function (_a) {
6
- var children = _a.children, themeConfig = _a.themeConfig;
7
- var theme = useTheme().theme;
8
- var _b = useState(false), isInitialized = _b[0], setIsInitialized = _b[1];
9
- useEffect(function () {
10
- if (!isInitialized) {
11
- setIsInitialized(true);
12
- updateTheme(themeConfig);
14
+ import { useLayoutEffect, useState } from 'react';
15
+ import { useTheme } from './theme-hook';
16
+ function ThemeProvider(_a) {
17
+ var children = _a.children, themeConfig = _a.themeConfig, storage = _a.storage;
18
+ var _b = useTheme(), setStorage = _b.setStorage, setTheme = _b.setTheme;
19
+ var _c = useState(false), ready = _c[0], setReady = _c[1];
20
+ useLayoutEffect(function () {
21
+ var theme = themeConfig;
22
+ setStorage(storage !== null && storage !== void 0 ? storage : null);
23
+ if (storage) {
24
+ var stored = storage.load();
25
+ if (stored) {
26
+ theme = __assign(__assign({}, themeConfig), stored);
27
+ }
13
28
  }
14
- }, [theme, isInitialized]);
15
- if (!isInitialized)
29
+ setTheme(theme);
30
+ setReady(true);
31
+ }, [themeConfig, storage, setTheme]);
32
+ if (!ready)
16
33
  return null;
17
34
  return _jsx(_Fragment, { children: children });
18
- };
35
+ }
19
36
  export { ThemeProvider };
@@ -0,0 +1,11 @@
1
+ import { ThemeConfig } from './theme.config';
2
+ type ThemeStorageData = {
3
+ mode: 'light' | 'dark';
4
+ hue: number;
5
+ };
6
+ export type ThemeStorage = {
7
+ load: () => ThemeStorageData | null;
8
+ save: (theme: ThemeConfig) => void;
9
+ };
10
+ export declare const defaultThemeStorage: ThemeStorage;
11
+ export {};
@@ -0,0 +1,23 @@
1
+ var STORAGE_KEY = 'app-theme';
2
+ var loadStoredTheme = function () {
3
+ try {
4
+ var raw = localStorage.getItem(STORAGE_KEY);
5
+ if (!raw)
6
+ return null;
7
+ return JSON.parse(raw);
8
+ }
9
+ catch (_a) {
10
+ return null;
11
+ }
12
+ };
13
+ var saveStoredTheme = function (theme) {
14
+ var data = {
15
+ mode: theme.mode,
16
+ hue: theme.hue,
17
+ };
18
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
19
+ };
20
+ export var defaultThemeStorage = {
21
+ load: loadStoredTheme,
22
+ save: saveStoredTheme,
23
+ };
package/package.json CHANGED
@@ -1,85 +1,87 @@
1
1
  {
2
- "name": "cyhip-dynamic-themes",
3
- "version": "0.2.2",
4
- "description": "Tailwind-powered dynamic color themes for React apps.",
5
- "author": "@KassioRF, @CyberHippie-io",
6
- "license": "MIT",
7
- "repository": {
8
- "type": "git",
9
- "url": "https://github.com/CyberHippie-io/cyhip-dynamic-themes"
10
- },
11
- "homepage": "https://cyhip-dynamic-themes.vercel.app/",
12
- "keywords": [
13
- "color palette",
14
- "color system",
15
- "color themes",
16
- "css in js",
17
- "css variables",
18
- "custom themes",
19
- "dark mode",
20
- "dynamic themes",
21
- "frontend styling",
22
- "light mode",
23
- "nextjs",
24
- "OKLCH",
25
- "tailwindcss",
26
- "tailwindcss plugin",
27
- "tailwind themes",
28
- "theme customization",
29
- "theme switcher",
30
- "react",
31
- "react dynamic themes",
32
- "react typescipt",
33
- "typescript",
34
- "vite"
35
- ],
36
- "files": [
37
- "dist",
38
- "README.md",
39
- "LICENSE"
40
- ],
41
- "type": "module",
42
- "main": "./dist/index.js",
43
- "module": "./dist/index.esm.js",
44
- "types": "./dist/index.d.ts",
45
- "bin": {
46
- "cyhip-dynamic-themes": "./dist/src/cli.js"
47
- },
48
- "devDependencies": {
49
- "@types/culori": "^2.1.1",
50
- "@types/node": "^20",
51
- "@types/react": "^18",
52
- "commander": "^12.1.0",
53
- "ts-node": "^10.9.2",
54
- "tsx": "^4.19.1",
55
- "typescript": "^5.6.3"
56
- },
57
- "dependencies": {
58
- "chalk": "^5.3.0",
59
- "culori": "^4.0.1",
60
- "inquirer": "^12.0.0",
61
- "react": "^18",
62
- "react-dom": "^18",
63
- "zustand": "^5.0.1"
64
- },
65
- "peerDependencies": {
66
- "react": "^18",
67
- "react-dom": "^18"
68
- },
69
- "exports": {
70
- ".": {
71
- "import": {
72
- "types": "./dist/index.d.ts",
73
- "default": "./dist/index.js"
74
- },
75
- "require": {
76
- "types": "./dist/index.d.ts",
77
- "default": "./dist/index.js"
78
- }
2
+ "name": "cyhip-dynamic-themes",
3
+ "version": "1.0.0",
4
+ "description": "Tailwind-powered dynamic color themes for React apps.",
5
+ "author": "@KassioRF, @CyberHippie-io",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/CyberHippie-io/cyhip-dynamic-themes"
10
+ },
11
+ "homepage": "https://cyhip-dynamic-themes.vercel.app/",
12
+ "keywords": [
13
+ "color palette",
14
+ "color system",
15
+ "color themes",
16
+ "css in js",
17
+ "css variables",
18
+ "custom themes",
19
+ "dark mode",
20
+ "dynamic themes",
21
+ "frontend styling",
22
+ "light mode",
23
+ "nextjs",
24
+ "OKLCH",
25
+ "tailwindcss",
26
+ "tailwindcss plugin",
27
+ "tailwind themes",
28
+ "theme customization",
29
+ "theme switcher",
30
+ "react",
31
+ "react dynamic themes",
32
+ "react typescipt",
33
+ "typescript",
34
+ "vite"
35
+ ],
36
+ "files": [
37
+ "dist",
38
+ "README.md",
39
+ "LICENSE"
40
+ ],
41
+ "type": "module",
42
+ "main": "./dist/index.js",
43
+ "module": "./dist/index.esm.js",
44
+ "types": "./dist/index.d.ts",
45
+ "bin": {
46
+ "cyhip-dynamic-themes": "./dist/src/cli.js"
47
+ },
48
+ "scripts": {
49
+ "build": "tsc --project tsconfig.json",
50
+ "dev": "tsc --watch -d",
51
+ "start": "tsx ./index.ts",
52
+ "prepublishOnly": "pnpm build"
53
+ },
54
+ "devDependencies": {
55
+ "@types/culori": "^2.1.1",
56
+ "@types/node": "^20",
57
+ "@types/react": "^18",
58
+ "commander": "^12.1.0",
59
+ "react": "^19.2.3",
60
+ "react-dom": "^19.2.3",
61
+ "ts-node": "^10.9.2",
62
+ "tsx": "^4.19.1",
63
+ "typescript": "^5.6.3"
64
+ },
65
+ "dependencies": {
66
+ "chalk": "^5.3.0",
67
+ "culori": "^4.0.1",
68
+ "inquirer": "^12.0.0",
69
+ "zustand": "^5.0.1"
70
+ },
71
+ "exports": {
72
+ ".": {
73
+ "import": {
74
+ "types": "./dist/index.d.ts",
75
+ "default": "./dist/index.js"
76
+ },
77
+ "require": {
78
+ "types": "./dist/index.d.ts",
79
+ "default": "./dist/index.js"
80
+ }
81
+ }
82
+ },
83
+ "peerDependencies": {
84
+ "react": "^19.2.3",
85
+ "react-dom": "^19.2.3"
79
86
  }
80
- },
81
- "scripts": {
82
- "build": "tsc --project tsconfig.json",
83
- "start": "tsx ./index.ts"
84
- }
85
- }
87
+ }
@@ -1,2 +0,0 @@
1
- declare const huePalettes = "\n/**\n * HUE THEMES\n *\n * Define the available color palettes here!\n *\n * The palettes are based on HUE values.\n *\n * To add or modify a HUE palette, explore and preview colors at:\n * https://oklch.com/#70,0.1,250,100\n *\n */\n\nconst hueScheme: Record<string, number> = {\n white: -1,\n blue: 250,\n green: 150,\n orange: 35,\n pink: 0,\n purple: 316,\n};\n\nexport { hueScheme };\n\n";
2
- export default huePalettes;
@@ -1,2 +0,0 @@
1
- var huePalettes = "\n/**\n * HUE THEMES\n *\n * Define the available color palettes here!\n *\n * The palettes are based on HUE values.\n *\n * To add or modify a HUE palette, explore and preview colors at:\n * https://oklch.com/#70,0.1,250,100\n *\n */\n\nconst hueScheme: Record<string, number> = {\n white: -1,\n blue: 250,\n green: 150,\n orange: 35,\n pink: 0,\n purple: 316,\n};\n\nexport { hueScheme };\n\n";
2
- export default huePalettes;
@@ -1,2 +0,0 @@
1
- declare const themeColors = "\n\n/**\n * COLORS\n *\n * You can use this on tailwindcss.config.ts as follows:\n *\n * import type { Config } from \"tailwindcss\";\n * import { themeColors } from \"./src/themes/theme-colors\";\n *\n * export default {\n * content: [\"./index.html\", \".src***.{js,ts,jsx,tsx}\"],\n * darkMode: \"class\",\n * theme: {\n * extend: {\n * colors: themeColors,\n * },\n * },\n * plugins: [],\n * } satisfies Config;\n *\n *\n */\n\nimport { dynamicTwClasses } from 'cyhip-dynamic-themes';\nimport colors from 'tailwindcss/colors';\nimport { hueScheme } from './theme.config';\n\nexport const themeColors = {\n // accent vars to allow dynamic color changes\n accent: dynamicTwClasses('accent', hueScheme.default),\n // static colors as you wish...\n white: colors.white,\n destructive: colors.red,\n success: colors.green,\n /**\n * You can customize this css vars based on accent values.\n * Take a look at root.css\n */\n background: 'oklch(var(--background) / <alpha-value>)',\n\n foreground: 'oklch(var(--foreground) / <alpha-value>)',\n primary: {\n DEFAULT: 'oklch(var(--primary) / <alpha-value>)',\n foreground: 'oklch(var(--primary-foreground) / <alpha-value>)',\n },\n secondary: {\n DEFAULT: 'oklch(var(--secondary) / <alpha-value>)',\n foreground: 'oklch(var(--secondary-foreground) / <alpha-value>)',\n },\n muted: {\n DEFAULT: 'var(--muted)',\n foreground: 'var(--muted-foreground)',\n },\n border: 'var(--border)',\n ring: 'var(--ring)',\n input: 'var(--input)',\n};\n\n";
2
- export default themeColors;
@@ -1,2 +0,0 @@
1
- var themeColors = "\n\n/**\n * COLORS\n *\n * You can use this on tailwindcss.config.ts as follows:\n *\n * import type { Config } from \"tailwindcss\";\n * import { themeColors } from \"./src/themes/theme-colors\";\n *\n * export default {\n * content: [\"./index.html\", \".src***.{js,ts,jsx,tsx}\"],\n * darkMode: \"class\",\n * theme: {\n * extend: {\n * colors: themeColors,\n * },\n * },\n * plugins: [],\n * } satisfies Config;\n *\n *\n */\n\nimport { dynamicTwClasses } from 'cyhip-dynamic-themes';\nimport colors from 'tailwindcss/colors';\nimport { hueScheme } from './theme.config';\n\nexport const themeColors = {\n // accent vars to allow dynamic color changes\n accent: dynamicTwClasses('accent', hueScheme.default),\n // static colors as you wish...\n white: colors.white,\n destructive: colors.red,\n success: colors.green,\n /**\n * You can customize this css vars based on accent values.\n * Take a look at root.css\n */\n background: 'oklch(var(--background) / <alpha-value>)',\n\n foreground: 'oklch(var(--foreground) / <alpha-value>)',\n primary: {\n DEFAULT: 'oklch(var(--primary) / <alpha-value>)',\n foreground: 'oklch(var(--primary-foreground) / <alpha-value>)',\n },\n secondary: {\n DEFAULT: 'oklch(var(--secondary) / <alpha-value>)',\n foreground: 'oklch(var(--secondary-foreground) / <alpha-value>)',\n },\n muted: {\n DEFAULT: 'var(--muted)',\n foreground: 'var(--muted-foreground)',\n },\n border: 'var(--border)',\n ring: 'var(--ring)',\n input: 'var(--input)',\n};\n\n";
2
- export default themeColors;
@@ -1,9 +0,0 @@
1
- /**
2
- * Theme Configuration
3
- * Required params to init and switch the theme.
4
- */
5
- type ThemeConfig = {
6
- hue: number;
7
- mode: 'light' | 'dark';
8
- chromaData: Record<number, number>;
9
- };
@@ -1 +0,0 @@
1
- "use strict";