@jslibkit/tailwind-theme-engine
v1.0.5
Published
Dynamic runtime theming for Tailwind CSS using semantic CSS variable color scales.
Maintainers
Readme
tailwind-theme-engine
Dynamic theming for Tailwind CSS using semantic color classes and CSS variables.
<div class="bg-primary-500 text-primary-100"></div>Themes can change at runtime, but Tailwind stays static. No Tailwind rebuild is needed when switching themes.
Prerequisites
- Node.js
18or newer - Tailwind CSS
3or newer - React
18or newer when using@jslibkit/tailwind-theme-engine/react - An ESM-compatible app or bundler
Recommended setup:
- Tailwind v4: use
theme.cssplus generated theme CSS. - Tailwind v3: use the Tailwind preset plus generated theme CSS.
- React apps: use generated themes with
useThemeEngine.
Install
npm install @jslibkit/tailwind-theme-engineDuring install, the package creates this file in your project if it does not already exist:
tailwind-theme-engine.config.jsExisting config files are never overwritten.
Local package testing:
npm install "D:\Projects\Bun\tailwind-theme-engine"If npm fails with a Windows EPERM cache error:
npm install "D:\Projects\Bun\tailwind-theme-engine" --cache ./.npm-cacheQuick Start: React + Tailwind v4
Edit tailwind-theme-engine.config.js:
export const themeDefinitions = {
purple: {
label: "Purple",
colors: {
primary: "#8b5cf6",
secondary: "#ec4899",
tertiary: "#111827",
success: "#22c55e",
warning: "#f59e0b",
info: "#0ea5e9",
danger: "#ef4444"
}
},
green: {
label: "Green",
colors: {
primary: "#10b981",
secondary: "#84cc16",
tertiary: "#111827",
success: "#22c55e",
warning: "#f59e0b",
info: "#0ea5e9",
danger: "#ef4444"
}
}
};Generate theme files:
npx tte buildThis creates:
theme/
purple.css
purple.js
green.css
green.js
index.jsImport Tailwind, the package color bridge, and one initial theme CSS file:
@import "tailwindcss";
@import "@jslibkit/tailwind-theme-engine/theme.css";
@import "../theme/purple.css";Initialize the theme engine once before React renders:
import React from "react";
import ReactDOM from "react-dom/client";
import { initializeThemeEngine } from "@jslibkit/tailwind-theme-engine/react";
import { themes } from "../theme";
import "./index.css";
import App from "./App";
initializeThemeEngine({
initialTheme: "purple",
themes,
storageKey: "app-theme"
});
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<App />
</React.StrictMode>
);Use the theme state anywhere:
import { useThemeEngine } from "@jslibkit/tailwind-theme-engine/react";
export default function App() {
const { themeName, setTheme } = useThemeEngine();
return (
<main className="min-h-screen bg-primary-50 p-8 text-primary-950">
<button
className="rounded-md bg-primary-500 px-4 py-2 text-white hover:bg-primary-600"
onClick={() => setTheme(themeName === "purple" ? "green" : "purple")}
>
Switch Theme
</button>
</main>
);
}The imported CSS file gives instant first paint. The shared theme engine handles switching after the app loads.
Tailwind v4 Setup
In your app CSS:
@import "tailwindcss";
@import "@jslibkit/tailwind-theme-engine/theme.css";
@import "../theme/purple.css";@jslibkit/tailwind-theme-engine/theme.css registers static Tailwind color tokens through @theme inline.
The generated theme CSS file provides initial CSS variable values:
:root {
--primary-500: 133 88 238;
--secondary-500: 232 72 153;
}Tailwind v3 Setup
Create or update tailwind.config.js:
import preset from "@jslibkit/tailwind-theme-engine/preset";
export default {
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
presets: [preset]
};Then import one generated theme CSS file in your app CSS:
@import "../theme/purple.css";Theme Config
tailwind-theme-engine.config.js should export themeDefinitions.
Each theme can be a plain color object or an object with label and colors.
export const themeDefinitions = {
brand: {
label: "Brand",
colors: {
primary: "#3b82f6",
secondary: "#06b6d4",
tertiary: "#111827",
success: "#22c55e",
warning: "#f59e0b",
info: "#0ea5e9",
danger: "#ef4444"
}
}
};The library generates the full Tailwind-style shade scale:
50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950Supported Color Names
Out of the box, Tailwind classes are registered for:
primarysecondarytertiarysuccesswarninginfodanger
Examples:
<div class="bg-primary-500 text-primary-50"></div>
<button class="bg-secondary-600 hover:bg-secondary-700"></button>
<p class="text-tertiary-900"></p>
<div class="bg-warning-100 text-warning-800"></div>
<div class="bg-info-100 text-info-800"></div>
<p class="text-danger-600"></p>Runtime CSS variables can be generated for any color name, but Tailwind classes only work for names registered in the Tailwind bridge.
Build Command
npx tte buildOptions:
npx tte build --config tailwind-theme-engine.config.js
npx tte build --out-dir src/generated-themes
npx tte build --forceBehavior:
- Reads theme definitions from config.
- Creates one
.cssfile per theme. - Creates one
.jsfile per theme. - Creates
index.jswith named exports,themes,themeMetadata, andthemeOptions. - Refuses to overwrite files unless
--forceis passed.
Generated JS usage:
import { themes, themeOptions } from "../theme";
import { green } from "../theme/green.js";Generated CSS usage:
@import "../theme/purple.css";If you remove a generated JS file, also remove its export from theme/index.js, or import individual generated files directly.
React Hook
import { useThemeEngine } from "@jslibkit/tailwind-theme-engine/react";Recommended with generated themes:
import { initializeThemeEngine } from "@jslibkit/tailwind-theme-engine/react";
import { themes } from "../theme";
initializeThemeEngine({
initialTheme: "purple",
themes,
storageKey: "app-theme"
});Then use it anywhere:
import { useThemeEngine } from "@jslibkit/tailwind-theme-engine/react";
const { themeName, setTheme } = useThemeEngine();The hook also supports runtime generation from base colors:
const { setTheme } = useThemeEngine({
initialTheme: "brand",
includeDefaultThemes: false,
themeDefinitions: {
brand: {
primary: "#3b82f6",
secondary: "#06b6d4",
tertiary: "#111827",
success: "#22c55e",
warning: "#f59e0b",
info: "#0ea5e9",
danger: "#ef4444"
}
}
});Use themes for production when possible. Use themeDefinitions when you want browser-side scale generation.
You do not need a React context provider. The React module keeps a small shared external store, so root initialization and later consumption are separate.
Hook options:
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| initialTheme | string | first available theme | Theme applied on first render. |
| themes | object | undefined | Prebuilt theme scales, usually from tte build. |
| themeDefinitions | object | built-ins | Base HEX colors for runtime generation. |
| includeDefaultThemes | boolean | true | Merge custom definitions with built-ins. Ignored when themes is passed. |
| root | HTMLElement | document.documentElement | Element that receives CSS variables. |
| storageKey | string | undefined | Persist selected theme in localStorage. |
Hook result:
| Property | Description |
| --- | --- |
| themeName | Current theme name. |
| theme | Current generated theme scale. |
| themes | All available theme scales. |
| setTheme(name) | Switch theme by name. |
| setThemeName(name) | Alias for setTheme. |
Non-hook helpers:
| Helper | Description |
| --- | --- |
| initializeThemeEngine(options) | Initialize shared themes once, usually before React renders. |
| getThemeEngine() | Read the current theme engine snapshot outside React. |
| setTheme(name) | Switch theme outside React. |
Core API
import { createTheme, applyTheme } from "@jslibkit/tailwind-theme-engine";
const theme = createTheme({
colors: {
primary: "#3b82f6",
secondary: "#06b6d4",
tertiary: "#111827"
}
});
applyTheme(theme);Output shape:
{
primary: {
50: "237 239 255",
100: "215 220 254",
500: "58 125 236",
950: "53 100 186"
}
}Applied CSS variables:
:root {
--primary-500: 58 125 236;
--secondary-500: 6 182 212;
}Prebuilt Palettes
import {
getDefaultPalettes,
getThemePalettesAsScales
} from "@jslibkit/tailwind-theme-engine/palettes";
const palettes = getDefaultPalettes();
const themes = getThemePalettesAsScales();Available palettes:
blueredgreenpurpledarklight
Legacy Single Theme Generation
For simple cases:
npx tte generate --primary "#3b82f6" --secondary "#06b6d4"Output:
theme/theme.generated.js
theme/index.jsUse tte build for multi-theme projects.
Public Modules
import {
applyTheme,
createTheme,
createThemeCollection,
defaultThemeDefinitions,
defaultThemes,
extendThemeDefinitions,
generateScale,
getDefaultPalettes,
getThemePalettesAsScales
} from "@jslibkit/tailwind-theme-engine";
import preset from "@jslibkit/tailwind-theme-engine/preset";
import { initializeThemeEngine, useThemeEngine } from "@jslibkit/tailwind-theme-engine/react";
import { applyTheme } from "@jslibkit/tailwind-theme-engine/runtime";
import { createTheme } from "@jslibkit/tailwind-theme-engine/createTheme";
import { generateScale } from "@jslibkit/tailwind-theme-engine/generator";CSS:
@import "@jslibkit/tailwind-theme-engine/theme.css";TypeScript
Type definitions are included.
import type { Theme, ThemeDefinitions } from "@jslibkit/tailwind-theme-engine";
import { createTheme } from "@jslibkit/tailwind-theme-engine";
const theme: Theme = createTheme({
colors: {
primary: "#3b82f6",
tertiary: "#111827"
}
});Important Notes
- Tailwind classes must remain static.
- Do not build class names like
bg-${color}-500. - Runtime switching only updates CSS variables.
- Tailwind config is not mutated at runtime.
- Tailwind does not rebuild when themes change.
Troubleshooting
bg-primary-500 flashes after refresh
Import one generated theme CSS file before React loads:
@import "tailwindcss";
@import "@jslibkit/tailwind-theme-engine/theme.css";
@import "../theme/purple.css";TypeError: Expected color to be a HEX string
You probably passed generated themes as themeDefinitions.
Use this:
useThemeEngine({ themes });Not this:
useThemeEngine({ themeDefinitions: themes });Classes are missing
Make sure the class names are static:
<div className="bg-primary-500 text-primary-50" />Tailwind v4 colors are missing
Make sure this CSS is present:
@import "tailwindcss";
@import "@jslibkit/tailwind-theme-engine/theme.css";npm install fails with EPERM on Windows
Use a local npm cache:
npm install "D:\Projects\Bun\tailwind-theme-engine" --cache ./.npm-cacheDevelopment
npm install
npm run build
npm testProject layout:
src/core color validation, scale generation, theme creation
src/runtime DOM CSS variable application
src/react React hook
src/tailwind Tailwind preset
src/themes built-in themes and palettes
src/cli CLI parsing and file generation