cyhip-dynamic-themes 0.2.1 → 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
@@ -9,23 +9,21 @@ Implement dynamic color themes in your React apps with Tailwind CSS in a simple
9
9
 
10
10
  - **Vite + React-ts: [cyhip-dynamic-themes](https://cyhip-dynamic-themes.vercel.app/)**
11
11
 
12
- - **Nextjs + React-ts: [cyhip-dynamic-themes-nextjs-example](https://cyhip-dynamic-themes-nextjs-example.vercel.app/)**
13
-
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
23
  - **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.
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,192 +53,229 @@ This command generates the following files in the `/themes/` folder:
55
53
 
56
54
  ```bash
57
55
  /themes/
58
- ├── theme.config.ts.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
-
88
-
89
62
  ### Import `root.css`
90
63
 
91
- 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.
92
65
 
93
66
  ```tsx
94
- // src/main.tsx
95
- import { StrictMode } from "react";
96
- import { createRoot } from "react-dom/client";
97
- import App from "./App.tsx";
67
+ // src/main.tsx (Vite)
68
+ import { StrictMode } from 'react';
69
+ import { createRoot } from 'react-dom/client';
70
+ import App from './App.tsx';
71
+ import { AppThemeProvider } from './themes/app-theme-provider.tsx';
72
+ import './themes/root.css';
98
73
 
99
- // Import CSS
100
- import "./themes/root.css";
101
-
102
- createRoot(document.getElementById("root")!).render(
74
+ createRoot(document.getElementById('root')!).render(
103
75
  <StrictMode>
104
- <App />
105
- </StrictMode>
76
+ <AppThemeProvider>
77
+ <App />
78
+ </AppThemeProvider>
79
+ </StrictMode>,
106
80
  );
107
81
  ```
108
82
 
109
-
110
83
  ### Theme Provider
111
84
 
112
- Use the `ThemeProvider` to initialize a default theme.
85
+ Use the `ThemeProvider` to initialize a default theme and optional storage.
113
86
 
114
87
  ```tsx
115
- // src/main.tsx
116
- import { ThemeConfig, ThemeProvider } from 'cyhip-dynamic-themes';
117
- import { chromaData, hueScheme } from './themes/theme.config.ts';
88
+ // themes/app-theme-provider.tsx
89
+ 'use client';
118
90
 
119
- import './index.css';
91
+ import { ThemeProvider } from 'cyhip-dynamic-themes';
92
+ import { defaultTheme, themeStorage } from './theme.config';
120
93
 
121
- createRoot(document.getElementById('root')!).render(
122
- <StrictMode>
123
- <ThemeProvider
124
- themeConfig={
125
- {
126
- hue: hueScheme.default,
127
- mode: 'light',
128
- chromaData: chromaData,
129
- } as ThemeConfig
130
- }
131
- >
132
- <App />
94
+ export function AppThemeProvider({ children }: { children: React.ReactNode }) {
95
+ return (
96
+ <ThemeProvider themeConfig={defaultTheme} storage={themeStorage}>
97
+ {children}
133
98
  </ThemeProvider>
134
- </StrictMode>
135
- );
136
-
99
+ );
100
+ }
137
101
  ```
138
102
 
139
103
  ### Switching Themes Dynamically
140
104
 
141
- 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
+ ```
142
180
 
143
181
  ```tsx
144
- // App.tsx
145
- import { ThemeSwitcher } from "./themes/theme-switcher";
146
- function App() {
182
+ // src/App.tsx
183
+ import { ThemeSwitcher } from './themes/theme-switcher';
184
+
185
+ export default function App() {
147
186
  return (
148
187
  <>
149
- <main className="h-screen flex flex-col justify-center items-center gap-y-14">
150
- <h1 className="text-4xl font-bold text-center">
151
- 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
152
191
  </h1>
153
192
  <ThemeSwitcher />
154
- <div className="bg-accent-200/40 dark:bg-accent-700/40 grid grid-cols-1 gap-6 p-4">
155
- <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">
156
195
  Button
157
196
  </button>
158
- <samp className="bg-accent-950/80 text-accent-100/90 text-sm rounded-sm px-4 py-1 shadow">
159
- className="bg-primary text-primary-foreground ..."
160
- </samp>
161
197
  </div>
162
198
  </main>
163
199
  </>
164
200
  );
165
201
  }
166
-
167
- export default App;
168
202
  ```
169
203
 
170
- 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.
171
205
 
172
- 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>`.
173
207
 
174
208
  ## Defining Color Palettes Based on Hue
175
209
 
176
- 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`:
177
211
 
178
212
  ```ts
179
- // themes/hue-palettes.ts
213
+ // themes/theme.config.ts
180
214
  /**
181
215
  * HUE THEMES
182
216
  * Define the available color palettes here!
183
217
  */
184
218
 
185
- const hueScheme: Record<string, string> = {
186
- white: "-1",
187
- blue: "250",
188
- green: "150",
189
- orange: "35",
190
- pink: "0",
191
- 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,
192
226
  };
193
-
194
- export { hueScheme };
195
227
  ```
196
228
 
197
229
  ## API
198
230
 
199
231
  ### `Type ThemeConfig`
232
+
200
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.
201
234
 
202
- - **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.
203
238
 
204
- - `hue`: `number`
205
- It determines the primary color base; if set to -1, the theme uses a white color based scheme.
239
+ ### `Type ThemeMode`
206
240
 
207
- - `mode`: `'light' | 'dark'`
208
- Defines whether the theme is in "light" or "dark" mode. Acceptable
241
+ Type alias for `'light' | 'dark'`.
209
242
 
210
- - `chromaData`: `Record<number,number>`
211
- 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`
212
244
 
213
- ### `useColorTheme(theme: ThemeConfig)`
245
+ Type alias for the numeric hue value.
214
246
 
215
- A custom hook that manages the application of color themes based on the provided HUE value and dark mode setting.
247
+ ### `useTheme()`
216
248
 
249
+ State hook with `setTheme`, plus granular setters `setThemeHue` and `setThemeMode`.
217
250
 
218
- ### `getThemeProperties(hue: number, darkMode: boolean, chromaData: Record<number,number>)`
251
+ ### `useThemeHue()`
219
252
 
220
- Defines CSS class and style properties based on the provided HUE value and dark mode setting.
253
+ Derived hook to read and update only the hue.
221
254
 
222
- - **Parameters:**
255
+ ### `useThemeMode()`
223
256
 
224
- - `hue`
257
+ Derived hook to read and update only the mode.
225
258
 
226
- - `darkMode`: A boolean indicating if dark mode is active.
259
+ ### `ThemeProvider`
227
260
 
228
- - `chromaData`
229
- - **Returns:**
261
+ React provider that applies theme settings and optionally loads/saves user preferences via storage.
230
262
 
231
- - An object containing:
232
- - `className`: A string for dark mode ("dark") or an empty string for light mode.
233
- - `style`: A record of dynamically generated CSS variables for accent colors.
263
+ ### `ThemeStorage` and `defaultThemeStorage`
234
264
 
235
- ### `currentAccentValue(variableName: string)`
265
+ `ThemeStorage` defines `load()` and `save()` for persisting user theme settings. `defaultThemeStorage` uses `localStorage`.
236
266
 
237
- Retrieves the current value of a specified CSS variable from the root element.
267
+ ### `getThemeProperties(themeConfig: ThemeConfig)`
268
+
269
+ Returns `{ dataTheme, style }` where `dataTheme` is `'light' | 'dark'` and `style` maps `--accent-*` CSS variables.
270
+
271
+ ### `currentAccentValue(variableName: string)`
238
272
 
239
- - **Parameters**:
273
+ Returns the current OKLCH value for a CSS variable from `:root`, or `null` if not available.
240
274
 
241
- - `variableName`: The name of the CSS variable to retrieve (e.g., "--accent-500").
275
+ ## Tailwind v4 and Dark Mode Notes
242
276
 
243
- - **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"`.
244
279
 
245
280
  ## License
246
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.1",
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,20 +1,36 @@
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
+ };
1
13
  import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
- // 'use client'; // enable on Nextjs
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
- /**@TODO Get this values from user cookie */
8
- var theme = useTheme().theme;
9
- var _b = useState(false), isInitialized = _b[0], setIsInitialized = _b[1];
10
- useEffect(function () {
11
- if (!isInitialized) {
12
- setIsInitialized(true);
13
- 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
+ }
14
28
  }
15
- }, [theme, isInitialized]);
16
- if (!isInitialized)
29
+ setTheme(theme);
30
+ setReady(true);
31
+ }, [themeConfig, storage, setTheme]);
32
+ if (!ready)
17
33
  return null;
18
34
  return _jsx(_Fragment, { children: children });
19
- };
35
+ }
20
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.1",
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";