cyhip-dynamic-themes 0.1.2 → 0.1.3

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,10 +7,10 @@
7
7
 
8
8
  ## Features
9
9
 
10
- - **Dynamic Color Theming**: Utilize OKLCH colors for vibrant and accurate color representations.
10
+ - **Dynamic Color Theming**: Allow your users to switch the color theme of your application in a simple and practical way.
11
11
  - **Dark Mode Support**: Easily switch between light and dark modes across your custom themes.
12
12
 
13
- 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 integrates seamlessly with their [tw-dynamic-themes](https://github.com/dkzlv/tw-dynamic-themes) library to manage dynamic CSS variables effectively.
13
+ 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)
14
14
 
15
15
  ## Installation
16
16
 
@@ -26,7 +26,7 @@ yarn add cyhip-dynamic-themes
26
26
 
27
27
  ### Prerequisites
28
28
 
29
- Ensure you have Tailwind CSS installed in your project and a `tailwind.config.ts `file in your root directory.
29
+ Ensure you have Tailwind CSS installed in your project and the `tailwind.config.ts` and `postcss.config.mjs` files in your root directory.
30
30
 
31
31
  ### Initialize Theme basic files
32
32
 
@@ -136,7 +136,7 @@ You can add or modify hue palettes by visiting [OKLCH Color Preview](https://okl
136
136
  */
137
137
 
138
138
  const hueScheme: Record<string, string> = {
139
- monoCromatic: "-1",
139
+ white: "-1",
140
140
  blue: "250",
141
141
  green: "150",
142
142
  orange: "35",
@@ -149,19 +149,19 @@ export { hueScheme };
149
149
 
150
150
  ## API
151
151
 
152
- ### `useColorTheme(hue: string, darkMode: boolean)`
152
+ ### `useColorTheme(hue: number, darkMode: boolean)`
153
153
 
154
154
  A custom hook that manages the application of color themes based on the provided HUE value and dark mode setting.
155
155
 
156
- - **Note**: Dispatches a custom event themeChange when the theme changes.
156
+ - **Note**: Dispatches a custom event `themeChange` when the theme changes.
157
157
 
158
- ### `getThemeProperties(hue: string, darkMode: boolean)`
158
+ ### `getThemeProperties(hue: number, darkMode: boolean)`
159
159
 
160
160
  Defines CSS class and style properties based on the provided HUE value and dark mode setting.
161
161
 
162
162
  - **Parameters:**
163
163
 
164
- - `hue`: A string representing the hue value. If -1, the theme is monochromatic.
164
+ - `hue`: A number representing the hue value. If -1, the theme is monochromatic.
165
165
 
166
166
  - `darkMode`: A boolean indicating if dark mode is active.
167
167
 
package/dist/index.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  export { version } from "./__version__";
2
2
  export { consistentChroma } from "./lib/tw-dynamic-themes/runtime";
3
3
  export { dynamicTwClasses } from "./lib/tw-dynamic-themes/twPlugin";
4
- export { hueScheme as defaultHueScheme } from "./src/hue-scheme";
5
4
  export { currentAccentValue, getThemeProperties } from "./src/theme-helpers";
6
5
  export { useColorTheme } from "./src/theme-hook";
package/dist/index.js CHANGED
@@ -1,6 +1,5 @@
1
1
  export { version } from "./__version__";
2
2
  export { consistentChroma } from "./lib/tw-dynamic-themes/runtime";
3
3
  export { dynamicTwClasses } from "./lib/tw-dynamic-themes/twPlugin";
4
- export { hueScheme as defaultHueScheme } from "./src/hue-scheme";
5
4
  export { currentAccentValue, getThemeProperties } from "./src/theme-helpers";
6
5
  export { useColorTheme } from "./src/theme-hook";
@@ -2,13 +2,12 @@
2
2
  * A map of CSS varable name to color
3
3
  */
4
4
  type SingleVariable = [string, string];
5
- export declare function getVariables({ baseName, hue, mode, monoCromatic, }: {
5
+ export declare function getVariables({ baseName, hue, mode, }: {
6
6
  baseName: string;
7
7
  hue: number;
8
8
  mode?: "bright" | "consistent";
9
- monoCromatic?: boolean;
10
9
  }): SingleVariable[];
11
10
  export declare function updateVariables(variables: SingleVariable[], el?: HTMLElement): void;
12
11
  export declare const highestChroma: (shadeIndex: number, hue: number) => string;
13
- export declare const consistentChroma: (i: number, hue: number, monoCromatic?: boolean) => string;
12
+ export declare const consistentChroma: (i: number, hue: number, whitePalette?: boolean) => string;
14
13
  export {};
@@ -2,11 +2,12 @@ import { toGamut as _toGamut, converter, differenceEuclidean, } from "culori";
2
2
  import { makeVariable, shades } from "./common";
3
3
  var toGamut = _toGamut;
4
4
  export function getVariables(_a) {
5
- var baseName = _a.baseName, hue = _a.hue, _b = _a.mode, mode = _b === void 0 ? "consistent" : _b, _c = _a.monoCromatic, monoCromatic = _c === void 0 ? false : _c;
5
+ var baseName = _a.baseName, hue = _a.hue, _b = _a.mode, mode = _b === void 0 ? "consistent" : _b;
6
+ var whitePalette = hue == -1;
6
7
  var calculator = mode === "bright" ? highestChroma : consistentChroma;
7
8
  return shades.map(function (shade, shadeIndex) { return [
8
9
  makeVariable({ name: baseName, shade: shade }),
9
- calculator(shadeIndex, hue, monoCromatic),
10
+ calculator(shadeIndex, +hue, whitePalette),
10
11
  ]; });
11
12
  }
12
13
  export function updateVariables(variables, el) {
@@ -33,16 +34,12 @@ export var highestChroma = function (shadeIndex, hue) {
33
34
  // Clamping it to the highest chroma possible
34
35
  return serializeColor(oklch(toGamut("p3", "oklch", differenceEuclidean("oklch"), 0)(color)));
35
36
  };
36
- export var consistentChroma = function (i, hue, monoCromatic) {
37
- if (monoCromatic === void 0) { monoCromatic = false; }
37
+ export var consistentChroma = function (i, hue, whitePalette) {
38
+ if (whitePalette === void 0) { whitePalette = false; }
38
39
  var oklch = converter("oklch");
39
40
  // Using a pinned chroma
40
- var chroma = monoCromatic ? chromaData[i] * 0.00001 : chromaData[i];
41
- var light = monoCromatic ? lightness[i] * 1.0 : lightness[i] * 0.95;
42
- // if (monoCromatic && i > 4) {
43
- // light = 0.24 + i / 10;
44
- // chroma = 0;
45
- // }
41
+ var chroma = whitePalette ? chromaData[i] * 0.00001 : chromaData[i];
42
+ var light = whitePalette ? lightness[i] * 1.0 : lightness[i] * 0.95;
46
43
  var color = "oklch(".concat(light, " ").concat(chroma, " ").concat(hue, ")");
47
44
  return serializeColor(oklch(toGamut("p3", "oklch", differenceEuclidean("oklch"), 0)(color)));
48
45
  };
@@ -72,17 +69,4 @@ var chromaData = {
72
69
  9: 0.0588,
73
70
  10: 0.0491,
74
71
  };
75
- // const chromaData: Record<number, number> = {
76
- // 0: 0.0,
77
- // 1: 0.0,
78
- // 2: 0.0,
79
- // 3: 0.0,
80
- // 4: 0.0,
81
- // 5: 0.0,
82
- // 6: 0.0,
83
- // 7: 0.0,
84
- // 8: 0.0,
85
- // 9: 0.0,
86
- // 10: 0.0,
87
- // };
88
72
  var serializeColor = function (c) { var _a; return "".concat(c.l.toFixed(3), " ").concat(c.c.toFixed(3), " ").concat((_a = c.h) === null || _a === void 0 ? void 0 : _a.toFixed(3)); };
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cyhip-dynamic-themes",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Tailwind-powered dynamic color themes for React apps.",
5
5
  "author": "@KassioRF, @CyberHippie-io",
6
6
  "license": "MIT",
@@ -8,12 +8,30 @@
8
8
  "type": "git",
9
9
  "url": "https://github.com/CyberHippie-io/cyhip-dynamic-themes"
10
10
  },
11
+ "homepage": "https://cyhip-dynamic-themes.vercel.app/",
11
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",
12
30
  "react",
13
- "tailwind",
14
- "theme",
15
- "color",
16
- "dynamic"
31
+ "react dynamic themes",
32
+ "react typescipt",
33
+ "typescript",
34
+ "vite"
17
35
  ],
18
36
  "files": [
19
37
  "dist",
@@ -29,7 +47,7 @@
29
47
  },
30
48
  "scripts": {
31
49
  "build": "tsc --project tsconfig.json",
32
- "start": "tsx src/index.ts",
50
+ "start": "tsx ./index.ts",
33
51
  "prepublishOnly": "pnpm build"
34
52
  },
35
53
  "devDependencies": {
@@ -37,6 +55,7 @@
37
55
  "@types/node": "^22.7.7",
38
56
  "@types/react": "^18.3.11",
39
57
  "commander": "^12.1.0",
58
+ "ts-node": "^10.9.2",
40
59
  "tsx": "^4.19.1",
41
60
  "typescript": "^5.6.3"
42
61
  },
@@ -1,2 +1,2 @@
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";
1
2
  export default huePalettes;
2
- 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, string> = {\n monoCromatic: \"-1\",\n blue: \"250\",\n green: \"150\",\n orange: \"35\",\n pink: \"0\",\n purple: \"316\",\n};\n\nexport { hueScheme };\n";
@@ -1,2 +1,2 @@
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, string> = {\n monoCromatic: \"-1\",\n blue: \"250\",\n green: \"150\",\n orange: \"35\",\n pink: \"0\",\n purple: \"316\",\n};\n\nexport { hueScheme };\n";
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
2
  export default huePalettes;
@@ -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: oklch(var(--accent-100));\n --foreground: oklch(var(--accent-900));\n --primary: oklch(var(--accent-500));\n --primary-foreground: oklch(var(--accent-50));\n --secondary: oklch(var(--accent-800));\n --secondary-foreground: oklch(var(--accent-200));\n --ring: oklch(var(--accent-500));\n --box-shadow: oklch(var(--accent-800) / 0.15);\n --border: oklch(var(--accent-900) / 0.2);\n /* hsl vars */\n --muted: hsl(240 4.8% 95.9%);\n --muted-foreground: hsl(240 3.8% 45%);\n --input: hsl(240 5.9% 80%);\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: oklch(var(--accent-900));\n --foreground: oklch(var(--accent-100));\n --primary: oklch(var(--accent-500));\n --primary-foreground: oklch(var(--accent-50));\n --box-shadow: oklch(var(--accent-100) / 0.15);\n --border: oklch(var(--accent-100) / 0.4);\n\n /* hsl vars */\n --muted: hsl(240 4.8% 30%);\n --muted-foreground: hsl(240 3.8% 70%);\n}\n\nbody {\n background-color: var(--background);\n color: var(--foreground);\n}\n";
1
2
  export default rootCss;
2
- 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: oklch(var(--accent-100));\n --foreground: oklch(var(--accent-900));\n --primary: oklch(var(--accent-500));\n --primary-foreground: oklch(var(--accent-50));\n --secondary: oklch(var(--accent-800));\n --secondary-foreground: oklch(var(--accent-200));\n --ring: oklch(var(--accent-500));\n --box-shadow: oklch(var(--accent-800) / 0.15);\n --border: oklch(var(--accent-900) / 0.2);\n /* hsl vars */\n --muted: hsl(240 4.8% 95.9%);\n --muted-foreground: hsl(240 3.8% 45%);\n --input: hsl(240 5.9% 80%);\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: oklch(var(--accent-900));\n --foreground: oklch(var(--accent-100));\n --primary: oklch(var(--accent-500));\n --primary-foreground: oklch(var(--accent-50));\n --box-shadow: oklch(var(--accent-100) / 0.15);\n --border: oklch(var(--accent-100) / 0.4);\n\n /* hsl vars */\n --muted: hsl(240 4.8% 30%);\n --muted-foreground: hsl(240 3.8% 70%);\n}\n\nbody {\n background-color: var(--background);\n color: var(--foreground);\n}\n";
@@ -1,2 +1,2 @@
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 colors from \"tailwindcss/colors\";\nimport { dynamicTwClasses } from \"cyhip-dynamic-themes\";\n\nexport const themeColors = {\n // accent vars to allow dynamic color changes\n accent: dynamicTwClasses(\"accent\", 250),\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: \"var(--background)\",\n foreground: \"var(--foreground)\",\n primary: {\n DEFAULT: \"var(--primary)\",\n foreground: \"var(--primary-foreground)\",\n },\n secondary: {\n DEFAULT: \"var(--secondary)\",\n foreground: \"var(--foreground)\",\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};";
1
2
  export default themeColors;
2
- 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 colors from \"tailwindcss/colors\";\nimport { dynamicTwClasses } from \"cyhip-dynamic-themes\";\n\nexport const themeColors = {\n // accent vars to allow dynamic color changes\n accent: dynamicTwClasses(\"accent\", 250),\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: \"var(--background)\",\n foreground: \"var(--foreground)\",\n primary: {\n DEFAULT: \"var(--primary)\",\n foreground: \"var(--primary-foreground)\",\n },\n secondary: {\n DEFAULT: \"var(--secondary)\",\n foreground: \"var(--foreground)\",\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};";
@@ -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 { consistentChroma, useColorTheme } from \"cyhip-dynamic-themes\";\nimport { forwardRef, HTMLAttributes, useEffect, useState } from \"react\";\nimport { hueScheme } from \"./hue-palettes\";\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, whitePalette: boolean = false) => {\n const oklchA = 'oklch(' + consistentChroma(4, +hue, whitePalette) + ')';\n const oklchB = 'oklch(' + consistentChroma(5, +hue, whitePalette) + ')';\n const oklchC = 'oklch(' + consistentChroma(6, +hue, whitePalette) + ')';\n const gradient = 'linear-gradient(70deg, ' + oklchA + ', ' + oklchB + ', ' + oklchC + ')';\n return gradient;\n};\n\nconst availableThemes: Record<string, string> = Object.keys(hueScheme).reduce(\n (acc, key) => {\n const value = hueScheme[key];\n acc[key] = buildThemeSample(value, value === -1);\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 [isMounted, setIsMounted] = useState(false);\n\n const [darkMode, setDarkMode] = useState(false);\n const [hue, setHue] = useState(hueScheme.blue);\n const { setTheme } = useColorTheme(hue, darkMode);\n\n useEffect(() => {\n if (!isMounted) return;\n setTheme(hue, darkMode);\n }, [hue, darkMode, setTheme, isMounted]);\n\n useEffect(() => {\n setIsMounted(true);\n }, []);\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";
1
2
  export default themeSwitcher;
2
- 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 { consistentChroma, useColorTheme } from \"cyhip-dynamic-themes\";\nimport { forwardRef, HTMLAttributes, useEffect, useState } from \"react\";\nimport { hueScheme } from \"./hue-palettes\";\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: string, monoCromatic: boolean = false) => {\n const oklchA = 'oklch(' + consistentChroma(4, +hue, monoCromatic) + ')';\n const oklchB = 'oklch(' + consistentChroma(5, +hue, monoCromatic) + ')';\n const oklchC = 'oklch(' + consistentChroma(6, +hue, monoCromatic) + ')';\n const gradient = 'linear-gradient(70deg, ' + oklchA + ', ' + oklchB + ', ' + oklchC + ')';\n return gradient;\n};\n\nconst availableThemes: Record<string, string> = Object.keys(hueScheme).reduce(\n (acc, key) => {\n const value = hueScheme[key];\n acc[key] = buildThemeSample(value, value === \"-1\");\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 [isMounted, setIsMounted] = useState(false);\n\n const [darkMode, setDarkMode] = useState(false);\n const [hue, setHue] = useState(hueScheme.blue);\n const { setTheme } = useColorTheme(hue, darkMode);\n\n useEffect(() => {\n if (!isMounted) return;\n setTheme(hue, darkMode);\n }, [hue, darkMode, setTheme, isMounted]);\n\n useEffect(() => {\n setIsMounted(true);\n }, []);\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";
@@ -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 { consistentChroma, useColorTheme } from \"cyhip-dynamic-themes\";\nimport { forwardRef, HTMLAttributes, useEffect, useState } from \"react\";\nimport { hueScheme } from \"./hue-palettes\";\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: string, monoCromatic: boolean = false) => {\n const oklchA = 'oklch(' + consistentChroma(4, +hue, monoCromatic) + ')';\n const oklchB = 'oklch(' + consistentChroma(5, +hue, monoCromatic) + ')';\n const oklchC = 'oklch(' + consistentChroma(6, +hue, monoCromatic) + ')';\n const gradient = 'linear-gradient(70deg, ' + oklchA + ', ' + oklchB + ', ' + oklchC + ')';\n return gradient;\n};\n\nconst availableThemes: Record<string, string> = Object.keys(hueScheme).reduce(\n (acc, key) => {\n const value = hueScheme[key];\n acc[key] = buildThemeSample(value, value === \"-1\");\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 [isMounted, setIsMounted] = useState(false);\n\n const [darkMode, setDarkMode] = useState(false);\n const [hue, setHue] = useState(hueScheme.blue);\n const { setTheme } = useColorTheme(hue, darkMode);\n\n useEffect(() => {\n if (!isMounted) return;\n setTheme(hue, darkMode);\n }, [hue, darkMode, setTheme, isMounted]);\n\n useEffect(() => {\n setIsMounted(true);\n }, []);\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";
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 { consistentChroma, useColorTheme } from \"cyhip-dynamic-themes\";\nimport { forwardRef, HTMLAttributes, useEffect, useState } from \"react\";\nimport { hueScheme } from \"./hue-palettes\";\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, whitePalette: boolean = false) => {\n const oklchA = 'oklch(' + consistentChroma(4, +hue, whitePalette) + ')';\n const oklchB = 'oklch(' + consistentChroma(5, +hue, whitePalette) + ')';\n const oklchC = 'oklch(' + consistentChroma(6, +hue, whitePalette) + ')';\n const gradient = 'linear-gradient(70deg, ' + oklchA + ', ' + oklchB + ', ' + oklchC + ')';\n return gradient;\n};\n\nconst availableThemes: Record<string, string> = Object.keys(hueScheme).reduce(\n (acc, key) => {\n const value = hueScheme[key];\n acc[key] = buildThemeSample(value, value === -1);\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 [isMounted, setIsMounted] = useState(false);\n\n const [darkMode, setDarkMode] = useState(false);\n const [hue, setHue] = useState(hueScheme.blue);\n const { setTheme } = useColorTheme(hue, darkMode);\n\n useEffect(() => {\n if (!isMounted) return;\n setTheme(hue, darkMode);\n }, [hue, darkMode, setTheme, isMounted]);\n\n useEffect(() => {\n setIsMounted(true);\n }, []);\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";
2
2
  export default themeSwitcher;
package/dist/src/cli.js CHANGED
@@ -51,6 +51,11 @@ var initThemesDirectory = function (themesDir) {
51
51
  fs.writeFileSync(path.join(themesDir, "theme-colors.ts"), themeColors.trim());
52
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 ├── hue-palettes.ts\n" +
56
+ "\t ├── root.css\n" +
57
+ "\t ├── theme-colors.ts\n" +
58
+ "\t └── theme-switcher.tsx\n"));
54
59
  console.log(chalk.cyan(" 1. Update your tailwind.conf.ts as described in /themes/theme-colors.ts"));
55
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 ]"));
56
61
  console.log(chalk.cyan(" 3. Add the /themes/theme-switcher.tsx component to your application and to see how it works."));
@@ -9,7 +9,7 @@
9
9
  * - `style`: A record with dynamically generated CSS variables for the accent colors.
10
10
  *
11
11
  */
12
- export declare const getThemeProperties: (hue: string, darkMode: boolean) => {
12
+ export declare const getThemeProperties: (hue: number, darkMode: boolean) => {
13
13
  className: string;
14
14
  style: Record<string, string>;
15
15
  };
@@ -11,14 +11,13 @@ import { getVariables } from "../lib/tw-dynamic-themes/runtime";
11
11
  *
12
12
  */
13
13
  export var getThemeProperties = function (hue, darkMode) {
14
- var monoCromatic = hue == "-1";
14
+ var whitePalette = hue == -1;
15
15
  var accent = getVariables({
16
16
  baseName: "accent",
17
- hue: +hue,
18
- monoCromatic: monoCromatic,
17
+ hue: hue,
19
18
  });
20
- // MonoCromatic have a different accent behavior for accent values
21
- if (monoCromatic) {
19
+ // whitePalette have a different accent behavior for accent values
20
+ if (whitePalette) {
22
21
  accent.push([
23
22
  "--accent-500",
24
23
  darkMode ? "1.000 0.000 89.876" : "0.212 0.000 359.000",
@@ -11,7 +11,7 @@
11
11
  * You can listen for this event to update components that depend on the theme.
12
12
  *
13
13
  */
14
- declare const useColorTheme: (hue: string, darkMode: boolean) => {
15
- setTheme: (newHue: string, newDarkMode: boolean) => void;
14
+ declare const useColorTheme: (hue: number, darkMode: boolean) => {
15
+ setTheme: (newHue: number, newDarkMode: boolean) => void;
16
16
  };
17
17
  export { useColorTheme };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cyhip-dynamic-themes",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Tailwind-powered dynamic color themes for React apps.",
5
5
  "author": "@KassioRF, @CyberHippie-io",
6
6
  "license": "MIT",
@@ -8,12 +8,30 @@
8
8
  "type": "git",
9
9
  "url": "https://github.com/CyberHippie-io/cyhip-dynamic-themes"
10
10
  },
11
+ "homepage": "https://cyhip-dynamic-themes.vercel.app/",
11
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",
12
30
  "react",
13
- "tailwind",
14
- "theme",
15
- "color",
16
- "dynamic"
31
+ "react dynamic themes",
32
+ "react typescipt",
33
+ "typescript",
34
+ "vite"
17
35
  ],
18
36
  "files": [
19
37
  "dist",
@@ -32,6 +50,7 @@
32
50
  "@types/node": "^22.7.7",
33
51
  "@types/react": "^18.3.11",
34
52
  "commander": "^12.1.0",
53
+ "ts-node": "^10.9.2",
35
54
  "tsx": "^4.19.1",
36
55
  "typescript": "^5.6.3"
37
56
  },
@@ -60,6 +79,6 @@
60
79
  },
61
80
  "scripts": {
62
81
  "build": "tsc --project tsconfig.json",
63
- "start": "tsx src/index.ts"
82
+ "start": "tsx ./index.ts"
64
83
  }
65
84
  }
@@ -1,2 +0,0 @@
1
- export default huePalettes;
2
- 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, string> = {\n monoCromatic: \"-1\",\n blue: \"250\",\n green: \"150\",\n orange: \"35\",\n pink: \"0\",\n purple: \"316\",\n};\n\nexport { hueScheme };\n";
@@ -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, string> = {\n monoCromatic: \"-1\",\n blue: \"250\",\n green: \"150\",\n orange: \"35\",\n pink: \"0\",\n purple: \"316\",\n};\n\nexport { hueScheme };\n";
2
- export default huePalettes;
@@ -1,13 +0,0 @@
1
- /**
2
- * HUE THEMES
3
- *
4
- * Define the available color palettes here!
5
- *
6
- * The palettes are based on HUE values.
7
- *
8
- * To add or modify a HUE palette, explore and preview colors at:
9
- * https://oklch.com/#70,0.1,250,100
10
- *
11
- */
12
- declare const hueScheme: Record<string, string>;
13
- export { hueScheme };
@@ -1,20 +0,0 @@
1
- /**
2
- * HUE THEMES
3
- *
4
- * Define the available color palettes here!
5
- *
6
- * The palettes are based on HUE values.
7
- *
8
- * To add or modify a HUE palette, explore and preview colors at:
9
- * https://oklch.com/#70,0.1,250,100
10
- *
11
- */
12
- var hueScheme = {
13
- monoCromatic: "-1",
14
- blue: "250",
15
- green: "150",
16
- orange: "35",
17
- pink: "0",
18
- purple: "316",
19
- };
20
- export { hueScheme };
@@ -1,5 +0,0 @@
1
- export { consistentChroma } from "../lib/tw-dynamic-themes/runtime";
2
- export { dynamicTwClasses } from "../lib/tw-dynamic-themes/twPlugin";
3
- export { hueScheme as defaultHueScheme } from "./hue-scheme";
4
- export { currentAccentValue, getThemeProperties } from "./theme-helpers";
5
- export { useColorTheme } from "./theme-hook";
package/dist/src/index.js DELETED
@@ -1,5 +0,0 @@
1
- export { consistentChroma } from "../lib/tw-dynamic-themes/runtime";
2
- export { dynamicTwClasses } from "../lib/tw-dynamic-themes/twPlugin";
3
- export { hueScheme as defaultHueScheme } from "./hue-scheme";
4
- export { currentAccentValue, getThemeProperties } from "./theme-helpers";
5
- export { useColorTheme } from "./theme-hook";
@@ -1,8 +0,0 @@
1
- import { HTMLAttributes } from "react";
2
- declare const ThemeSwitcher: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & {
3
- hueScheme: Record<string, string>;
4
- availableThemes: Record<string, string>;
5
- setHue: (hue: string) => void;
6
- setDarkMode: (darkMode: boolean) => void;
7
- } & import("react").RefAttributes<HTMLDivElement>>;
8
- export { ThemeSwitcher };
@@ -1,33 +0,0 @@
1
- "use client";
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
- };
13
- var __rest = (this && this.__rest) || function (s, e) {
14
- var t = {};
15
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
16
- t[p] = s[p];
17
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
18
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
19
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
20
- t[p[i]] = s[p[i]];
21
- }
22
- return t;
23
- };
24
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
25
- import { forwardRef } from "react";
26
- var ThemeSwitcher = forwardRef(function (_a, ref) {
27
- var hueScheme = _a.hueScheme, availableThemes = _a.availableThemes, setHue = _a.setHue, setDarkMode = _a.setDarkMode, className = _a.className, props = __rest(_a, ["hueScheme", "availableThemes", "setHue", "setDarkMode", "className"]);
28
- return (_jsx(_Fragment, { children: _jsxs("div", __assign({ ref: ref, className: "bg-accent-200/40 dark:bg-accent-700/40 grid grid-cols-3 rounded-sm gap-2 p-6" }, props, { children: [Object.keys(availableThemes).map(function (key) { return (_jsxs("button", { 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", onClick: function () { return setHue(hueScheme[key]); }, children: [_jsx("span", { className: "w-4 h-4 rounded-full", style: {
29
- background: availableThemes[key],
30
- } }), _jsx("span", { children: key })] }, key)); }), _jsxs("div", { className: "col-span-3 grid grid-cols-2 gap-x-2 mx-auto", children: [_jsx("button", { 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", onClick: function () { return setDarkMode(false); }, children: "Light" }), _jsx("button", { 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", onClick: function () { return setDarkMode(true); }, children: "Dark" })] })] })) }));
31
- });
32
- ThemeSwitcher.displayName = "ThemeSwitcher";
33
- export { ThemeSwitcher };
@@ -1,36 +0,0 @@
1
- "use client";
2
- var __rest = (this && this.__rest) || function (s, e) {
3
- var t = {};
4
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
- t[p] = s[p];
6
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
- t[p[i]] = s[p[i]];
10
- }
11
- return t;
12
- };
13
- import { forwardRef } from "react";
14
- var ThemeSwitcher = forwardRef(function (_a, ref) {
15
- var hueScheme = _a.hueScheme, availableThemes = _a.availableThemes, setHue = _a.setHue, setDarkMode = _a.setDarkMode, className = _a.className, props = __rest(_a, ["hueScheme", "availableThemes", "setHue", "setDarkMode", "className"]);
16
- return (<>
17
- <div ref={ref} className="bg-accent-200/40 dark:bg-accent-700/40 grid grid-cols-3 rounded-sm gap-2 p-6" {...props}>
18
- {Object.keys(availableThemes).map(function (key) { return (<button key={key} 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" onClick={function () { return setHue(hueScheme[key]); }}>
19
- <span className="w-4 h-4 rounded-full" style={{
20
- background: availableThemes[key],
21
- }}></span>
22
- <span>{key}</span>
23
- </button>); })}
24
- <div className="col-span-3 grid grid-cols-2 gap-x-2 mx-auto">
25
- <button 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" onClick={function () { return setDarkMode(false); }}>
26
- Light
27
- </button>
28
- <button 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" onClick={function () { return setDarkMode(true); }}>
29
- Dark
30
- </button>
31
- </div>
32
- </div>
33
- </>);
34
- });
35
- ThemeSwitcher.displayName = "ThemeSwitcher";
36
- export { ThemeSwitcher };