{"_id":"@abdelamrah/theme-engine","name":"@abdelamrah/theme-engine","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@abdelamrah/theme-engine","version":"1.0.0","description":"A production-ready frontend theme engine for React / Next.js with runtime theme switching, CSS variables integration, and shadcn/ui + Tailwind CSS compatibility.","keywords":["theme","theme-engine","design-system","react","next.js","tailwind","shadcn","css-variables","dark-mode"],"author":{"name":"Abdel"},"license":"MIT","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./studio":{"import":{"types":"./dist/studio.d.ts","default":"./dist/studio.js"},"require":{"types":"./dist/studio.d.cts","default":"./dist/studio.cjs"}}},"scripts":{"build":"tsup","build:watch":"tsup --watch","typecheck":"tsc --noEmit","clean":"rm -rf dist","prepublishOnly":"npm run clean && npm run build && npm run typecheck"},"peerDependencies":{"react":"^18.0.0 || ^19.0.0","react-dom":"^18.0.0 || ^19.0.0"},"devDependencies":{"@types/react":"^18.3.0","@types/react-dom":"^18.3.0","react":"^18.3.0","react-dom":"^18.3.0","tsup":"^8.0.0","typescript":"^5.4.0"},"sideEffects":false,"publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/abdel/theme-engine.git"},"gitHead":"ff0fd46d568f94427deeb6848a09c90b66ed5fcf","_id":"@abdelamrah/theme-engine@1.0.0","bugs":{"url":"https://github.com/abdel/theme-engine/issues"},"homepage":"https://github.com/abdel/theme-engine#readme","_nodeVersion":"24.13.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-CwUbskWsJ8yZc86fTbdccNFMV1pJfjhZuxffBnnHQHEWIbc7DHiDHttVlx75RiGZ+Em5iBzNkC7sJxbsFCoROw==","shasum":"f9c4a330776b2ea87031f11857159bc24d92e0fd","tarball":"https://registry.npmjs.org/@abdelamrah/theme-engine/-/theme-engine-1.0.0.tgz","fileCount":16,"unpackedSize":922370,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIBMgxcg12MzXbaHYjTJS2MaEg7QfEz9NMOCM6Msgju7rAiEAsD/OI8/9ybmMLAOg3X+/bWlYZZLG06fi9/in9ofxmR0="}]},"_npmUser":{"name":"abdelamrah","email":"abdela.abdelamrah31@gmail.com"},"directories":{},"maintainers":[{"name":"abdelamrah","email":"abdela.abdelamrah31@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/theme-engine_1.0.0_1777912552540_0.6210581617264572"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-04T16:35:52.303Z","1.0.0":"2026-05-04T16:35:52.760Z","modified":"2026-05-04T16:35:53.151Z"},"maintainers":[{"name":"abdelamrah","email":"abdela.abdelamrah31@gmail.com"}],"description":"A production-ready frontend theme engine for React / Next.js with runtime theme switching, CSS variables integration, and shadcn/ui + Tailwind CSS compatibility.","homepage":"https://github.com/abdel/theme-engine#readme","keywords":["theme","theme-engine","design-system","react","next.js","tailwind","shadcn","css-variables","dark-mode"],"repository":{"type":"git","url":"git+https://github.com/abdel/theme-engine.git"},"author":{"name":"Abdel"},"bugs":{"url":"https://github.com/abdel/theme-engine/issues"},"license":"MIT","readme":"# @abdelamrah/theme-engine\n\nA production-ready **frontend theme engine** for React / Next.js applications.\n\nRuntime theme switching · CSS variables · shadcn/ui + Tailwind compatible · localStorage persistence · Zero backend dependencies\n\n---\n\n## Features\n\n- Define themes with colors, radius, typography, and shadow tokens\n- Switch themes at runtime with instant CSS variable updates\n- Add / remove custom themes dynamically through a client-side API\n- Persist themes in `localStorage` across page reloads\n- React hook + context provider with optimised re-renders\n- Four built-in presets: `modern`, `minimal`, `glass`, `dark`\n- Full TypeScript, strict mode\n- ESM + CJS dual build\n- Compatible with **Next.js App Router**, **shadcn/ui**, and **Tailwind CSS**\n\n---\n\n## Installation\n\n```bash\nnpm install @abdelamrah/theme-engine\n# or\npnpm add @abdelamrah/theme-engine\n# or\nyarn add @abdelamrah/theme-engine\n```\n\nPeer dependencies: `react ^18 || ^19`, `react-dom ^18 || ^19`\n\n---\n\n## Quick start\n\n### 1. Wrap your app with `<ThemeProvider>`\n\n```tsx\n// app/layout.tsx  (Next.js App Router)\nimport { ThemeProvider } from \"@abdelamrah/theme-engine\";\n\nexport default function RootLayout({ children }: { children: React.ReactNode }) {\n  return (\n    <html lang=\"en\">\n      <body>\n        <ThemeProvider defaultTheme=\"modern\">\n          {children}\n        </ThemeProvider>\n      </body>\n    </html>\n  );\n}\n```\n\n### 2. Use the hook in any client component\n\n```tsx\n\"use client\";\nimport { useThemeEngine } from \"@abdelamrah/theme-engine\";\n\nexport function ThemeSwitcher() {\n  const { themes, activeTheme, setActiveTheme } = useThemeEngine();\n\n  return (\n    <select\n      value={activeTheme?.name ?? \"\"}\n      onChange={(e) => setActiveTheme(e.target.value)}\n    >\n      {themes.map((t) => (\n        <option key={t.name} value={t.name}>\n          {t.label ?? t.name}\n        </option>\n      ))}\n    </select>\n  );\n}\n```\n\n### 3. Use CSS variables in Tailwind\n\nIn your `tailwind.config.ts` extend the theme to consume the variables:\n\n```ts\n// tailwind.config.ts\nimport type { Config } from \"tailwindcss\";\n\nexport default {\n  darkMode: [\"class\"],\n  theme: {\n    extend: {\n      colors: {\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(--secondary-foreground)\",\n        },\n        muted: {\n          DEFAULT: \"var(--muted)\",\n          foreground: \"var(--muted-foreground)\",\n        },\n        accent: { DEFAULT: \"var(--accent)\" },\n        border: \"var(--border)\",\n        destructive: \"var(--destructive)\",\n        ring: \"var(--ring)\",\n      },\n      borderRadius: {\n        DEFAULT: \"var(--radius)\",\n        sm: \"var(--radius-sm)\",\n        md: \"var(--radius-md)\",\n        lg: \"var(--radius-lg)\",\n        xl: \"var(--radius-xl)\",\n      },\n      fontFamily: {\n        sans: [\"var(--font-sans)\"],\n        mono: [\"var(--font-mono)\"],\n      },\n      boxShadow: {\n        theme: \"var(--shadow)\",\n      },\n    },\n  },\n} satisfies Config;\n```\n\n---\n\n## API Reference\n\n### `<ThemeProvider>`\n\n| Prop | Type | Default | Description |\n|---|---|---|---|\n| `initialThemes` | `ThemeConfig[]` | presets | Base list; merged with the four built‑in presets and `localStorage` overrides |\n| `defaultTheme` | `string` | first preset | Bootstrap theme when no active theme is persisted yet; a value in `localStorage` wins after hydration |\n| `persistThemes` | `boolean` | `true` | Save custom themes to localStorage |\n| `applyOnChange` | `boolean` | `true` | Update CSS vars when active theme changes |\n| `backendAdapter` | `ThemeBackendAdapter` | `undefined` | Adapter used to fetch global theme config from your backend |\n| `backendSync` | `ThemeSyncOptions & { enabled?: boolean }` | `undefined` | Backend sync options (polling, merge/replace mode, callbacks) |\n\n---\n\n### `useThemeEngine()`\n\nReturns:\n\n| Field | Type | Description |\n|---|---|---|\n| `themes` | `ThemeConfig[]` | All registered themes |\n| `activeTheme` | `ThemeConfig \\| null` | Currently applied theme |\n| `setActiveTheme` | `(name: string) => void` | Switch + apply theme by name |\n| `api` | `IThemeAPI` | Direct access to ThemeAPI |\n\n---\n\n### `ThemeAPI`\n\nThe singleton client-side state API. Can be used outside React components.\n\n```ts\nimport { ThemeAPI } from \"@abdelamrah/theme-engine\";\n\n// Add a custom theme\nThemeAPI.addTheme({\n  name: \"ocean\",\n  label: \"Ocean\",\n  colors: {\n    background: \"#0a1628\",\n    foreground: \"#e2e8f0\",\n    primary: \"#38bdf8\",\n    primaryForeground: \"#0a1628\",\n    secondary: \"#1e3a5f\",\n    secondaryForeground: \"#bae6fd\",\n    border: \"#1e3a5f\",\n  },\n});\n\n// Subscribe to changes\nconst unsubscribe = ThemeAPI.subscribe((themes) => {\n  console.log(\"themes updated:\", [...themes.keys()]);\n});\n\n// Remove theme\nThemeAPI.removeTheme(\"ocean\");\n\n// Clean up\nunsubscribe();\n```\n\n**Export / import (JSON)**\n\n```ts\nconst json = ThemeAPI.exportThemesToJSON(true);\nconst result = ThemeAPI.importThemesFromJSON(json, \"merge\"); // or \"replace\"\nif (result.ok) console.log(`Imported ${result.count} themes`);\n\n// Dynamic load from a URL (browser; CORS must allow the origin)\nconst fromUrl = await ThemeAPI.importThemesFromURL(\"/api/themes.json\", \"merge\");\n```\n\nStandalone helpers (no store mutation): `serializeThemesToJSON`, `parseThemesFromJSON`, `downloadThemesJsonFile` from `@abdelamrah/theme-engine`.\n\n**Backend-agnostic global sync**\n\nUse a `ThemeBackendAdapter` to fetch remote theme config from any backend:\n\n```ts\nimport {\n  startThemeSync,\n  syncThemesFromBackend,\n  type ThemeBackendAdapter,\n} from \"@abdelamrah/theme-engine\";\n\nconst adapter: ThemeBackendAdapter = {\n  async fetchConfig() {\n    const res = await fetch(\"/api/theme-config\", { cache: \"no-store\" });\n    if (!res.ok) throw new Error(`HTTP ${res.status}`);\n    return {\n      version: \"v42\",\n      themes: [],\n      activeThemeName: \"modern\",\n      mode: \"merge\", // or \"replace\"\n    };\n  },\n};\n\n// one-shot\nawait syncThemesFromBackend(adapter, { mode: \"merge\" });\n\n// polling\nconst controller = startThemeSync(adapter, { intervalMs: 30000 });\n// later\ncontroller.stop();\n```\n\nBy default, backend sync **does not override** a user’s persisted active theme (`preferLocalActiveTheme` defaults to `true`). Return `forceActiveTheme: true` from `fetchConfig()` (or set `preferLocalActiveTheme: false` in sync options) when you need a fleet-wide forced theme.\n\n**Theme Studio → all users**: pass `onPublishGlobal` on `<ThemeStudio>` to `PUT` the current registry + active theme to your API; clients must still load it via `backendAdapter` polling. The publish payload sets `forceActiveTheme: true` so the rollout applies everywhere.\n\nYou can also use `<ThemeStudio publishGlobalEndpoint=\"/api/theme-config\" />` for a no-code callback setup (automatic JSON `PUT`).\n\n---\n\n### `applyTheme(theme)`\n\nImperatively applies CSS variables from a `ThemeConfig` to `document.documentElement`. Handles the `dark` class on `<html>` automatically.\n\n```ts\nimport { applyTheme } from \"@abdelamrah/theme-engine\";\nimport { dark } from \"@abdelamrah/theme-engine\";\n\napplyTheme(dark);\n```\n\n---\n\n### Built-in presets\n\n```ts\nimport { presets, modern, minimal, glass, dark } from \"@abdelamrah/theme-engine\";\n```\n\n| Name | Style | Dark |\n|---|---|---|\n| `modern` | Clean blue-accent | No |\n| `minimal` | High-contrast monochrome | No |\n| `glass` | Frosted glass + blur | No |\n| `dark` | Deep dark with indigo accent | Yes |\n\n---\n\n## ThemeConfig type\n\n```ts\ninterface ThemeConfig {\n  name: string;           // unique identifier\n  label?: string;         // display name\n  dark?: boolean;         // toggles Tailwind `dark` class on <html>\n  colors: {\n    background: string;\n    foreground: string;\n    primary: string;\n    primaryForeground?: string;\n    secondary: string;\n    secondaryForeground?: string;\n    muted?: string;\n    mutedForeground?: string;\n    accent?: string;\n    border: string;\n    destructive?: string;\n    ring?: string;\n  };\n  radius?: string | {\n    base: string;\n    sm?: string; md?: string; lg?: string; xl?: string;\n  };\n  typography?: {\n    fontSans?: string;\n    fontMono?: string;\n    fontDisplay?: string;\n  };\n  effects?: {\n    shadow?: string;\n    borderWidth?: string;\n    backdropBlur?: string;\n  };\n}\n```\n\n---\n\n## License\n\nMIT © Abdel\n# theme-engine\n","readmeFilename":"README.md","_rev":"1-7a261561687f0e9df23bf11d2b15f9f6"}