{"_id":"@12nil/theme-registry-package","_rev":"6-b0518e3314438a890a338a996949f3ee","name":"@12nil/theme-registry-package","dist-tags":{"latest":"0.1.5"},"versions":{"0.1.0":{"name":"@12nil/theme-registry-package","version":"0.1.0","license":"MIT","_id":"@12nil/theme-registry-package@0.1.0","maintainers":[{"name":"dev-jeff","email":"agadiajeffrey@gmail.com"}],"dist":{"shasum":"d99cda54db65fbb5203367d3cfa181a620aac576","tarball":"https://registry.npmjs.org/@12nil/theme-registry-package/-/theme-registry-package-0.1.0.tgz","fileCount":13,"integrity":"sha512-xe0PzWxZPHKcZnSz28+471atR+RJoYLyDcBywIMALLY1FhS2ysDEIbMLCkN2bZJ+rc9M6XNzn84Y2iH6vkgNDw==","signatures":[{"sig":"MEQCICSh6VjUt3PcVlhtihbGaecLlrDewkKJtaFvDpr95HaMAiAbLMciXUXDn85DbQHY1Xy4JqsqgyY3og9E+vnuPWP7hg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":223027},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"scripts":{"dev":"tsup index.ts --format esm,cjs --dts --watch","build":"tsup index.ts --format esm,cjs --dts --sourcemap --clean","prepublishOnly":"npm run build"},"_npmUser":{"name":"dev-jeff","email":"agadiajeffrey@gmail.com"},"_npmVersion":"11.16.0","description":"Theme registry and React provider utilities","directories":{},"sideEffects":["./app_theme.ts"],"_nodeVersion":"24.18.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.2.4","typescript":"^5.8.3","@types/react":"^18.3.3"},"peerDependencies":{"react":">=18"},"_npmOperationalInternal":{"tmp":"tmp/theme-registry-package_0.1.0_1783171590728_0.02208717017730577","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@12nil/theme-registry-package","version":"0.1.1","license":"MIT","_id":"@12nil/theme-registry-package@0.1.1","maintainers":[{"name":"dev-jeff","email":"agadiajeffrey@gmail.com"}],"bin":{"runtime-theme-registry":"bin/runtime-theme-registry.mjs"},"dist":{"shasum":"fae00b184581b4bacae067c6389f99907b875fe6","tarball":"https://registry.npmjs.org/@12nil/theme-registry-package/-/theme-registry-package-0.1.1.tgz","fileCount":14,"integrity":"sha512-PgFBc7oh87DELJpidRLaXdsVm4XbfCZArelg+G9MnEVlSIUxtvlDWAHJk9EU0TZfKhQgvktfA/BFL0s9IZYDxQ==","signatures":[{"sig":"MEUCIQCC4Q3uQ2OCXCldzpGBSyss7g5vxndU3gSf9g+NrWmgPAIgRICONd5VJ+KHvKiGk4MR+iecQubvDiujNxIIpZuiwsw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":283248},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"scripts":{"dev":"tsup index.ts --format esm,cjs --dts --watch","build":"tsup index.ts --format esm,cjs --dts --sourcemap --clean","prepublishOnly":"npm run build"},"_npmUser":{"name":"dev-jeff","email":"agadiajeffrey@gmail.com"},"_npmVersion":"11.16.0","description":"Theme registry and React provider utilities","directories":{},"sideEffects":["./app_theme.ts"],"_nodeVersion":"24.18.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.2.4","typescript":"^5.8.3","@types/react":"^18.3.3"},"peerDependencies":{"react":">=18"},"_npmOperationalInternal":{"tmp":"tmp/theme-registry-package_0.1.1_1783174623811_0.5792819703318881","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@12nil/theme-registry-package","version":"0.1.2","license":"MIT","_id":"@12nil/theme-registry-package@0.1.2","maintainers":[{"name":"dev-jeff","email":"agadiajeffrey@gmail.com"}],"bin":{"runtime-theme-registry":"bin/runtime-theme-registry.mjs"},"dist":{"shasum":"d3541e6561de98bda606a2f38f01c87043b7fd39","tarball":"https://registry.npmjs.org/@12nil/theme-registry-package/-/theme-registry-package-0.1.2.tgz","fileCount":10,"integrity":"sha512-5OC6GORVWAHuGQmp0WlCFPtNdAbZaK5I19Q/Gxij6sCeUsOyJUoVmA8mzX6jj4pyQ1/72TeroO3K+F3lTEs5fA==","signatures":[{"sig":"MEUCIBZ8PeLK2b8g08+K9ai1/ilmr7aoqSZPPm3e2tDnuLQUAiEArHUru3QmdyIdQJSdUYuVbV2dC8PySmJpz8Q/NwZOJVw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":275436},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"scripts":{"dev":"tsup index.ts --format esm,cjs --dts --watch","build":"tsup index.ts --format esm,cjs --dts --sourcemap --clean","prepublishOnly":"npm run build"},"_npmUser":{"name":"dev-jeff","email":"agadiajeffrey@gmail.com"},"_npmVersion":"11.16.0","description":"Theme registry and React provider utilities","directories":{},"sideEffects":false,"_nodeVersion":"24.18.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.2.4","typescript":"^5.8.3","@types/react":"^18.3.3"},"peerDependencies":{"react":">=18"},"_npmOperationalInternal":{"tmp":"tmp/theme-registry-package_0.1.2_1783259644233_0.3517089793957815","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@12nil/theme-registry-package","version":"0.1.3","license":"MIT","_id":"@12nil/theme-registry-package@0.1.3","maintainers":[{"name":"dev-jeff","email":"agadiajeffrey@gmail.com"}],"bin":{"runtime-theme-registry":"bin/runtime-theme-registry.mjs"},"dist":{"shasum":"a3c6acb0f82375e22f5e06d49c63e3dd05909af9","tarball":"https://registry.npmjs.org/@12nil/theme-registry-package/-/theme-registry-package-0.1.3.tgz","fileCount":10,"integrity":"sha512-1cw8W+zuRcv7hbHs+mz0YVsqcy8xkRbQFDei+buXZWUyL5ipeTGNE9aZVm/0Mr13LlWU454zi6chEltjM5veIg==","signatures":[{"sig":"MEYCIQDOHdZjeFs5rFj4qlQ7o1MFHYvEjOvlps77WvuaO+UhfwIhALeZaohDjsLes92XXr0LeCu3/2yVSzVHcg7S+3J1/Pl/","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":281919},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"scripts":{"dev":"tsup index.ts --format esm,cjs --dts --watch","build":"tsup index.ts --format esm,cjs --dts --sourcemap --clean","prepublishOnly":"npm run build"},"_npmUser":{"name":"dev-jeff","email":"agadiajeffrey@gmail.com"},"_npmVersion":"11.16.0","description":"Theme registry and React provider utilities","directories":{},"sideEffects":false,"_nodeVersion":"24.18.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.2.4","typescript":"^5.8.3","@types/react":"^18.3.3"},"peerDependencies":{"react":">=18"},"_npmOperationalInternal":{"tmp":"tmp/theme-registry-package_0.1.3_1783260289171_0.43201443932282335","host":"s3://npm-registry-packages-npm-production"}},"0.1.4":{"name":"@12nil/theme-registry-package","version":"0.1.4","license":"MIT","_id":"@12nil/theme-registry-package@0.1.4","maintainers":[{"name":"dev-jeff","email":"agadiajeffrey@gmail.com"}],"bin":{"runtime-theme-registry":"bin/runtime-theme-registry.mjs"},"dist":{"shasum":"1f0e0434634144f989ed9320aaa9f9e1ab6bfb76","tarball":"https://registry.npmjs.org/@12nil/theme-registry-package/-/theme-registry-package-0.1.4.tgz","fileCount":10,"integrity":"sha512-HARWu0NkZG3arIgdlODIwtyO+orGz+7NoLxITbOyfXlqXttylkvwiJCAI7FTBfSCV59hpY4XDybgSH8VYKUHYA==","signatures":[{"sig":"MEQCIF0AMFyZzSntBmgnIR15gVZpbhow5ShrP68gYW2Zz7nXAiA1gZEYLXPS0LkDXrw3WVMfHLKISY993yMXbjJeTB0X4A==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":397988},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"scripts":{"dev":"tsup index.ts --format esm,cjs --dts --watch","build":"tsup index.ts --format esm,cjs --dts --sourcemap --clean","prepublishOnly":"npm run build"},"_npmUser":{"name":"dev-jeff","email":"agadiajeffrey@gmail.com"},"_npmVersion":"11.16.0","description":"Theme registry and React provider utilities","directories":{},"sideEffects":false,"_nodeVersion":"24.18.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.2.4","typescript":"^5.8.3","@types/react":"^18.3.3"},"peerDependencies":{"react":">=18"},"_npmOperationalInternal":{"tmp":"tmp/theme-registry-package_0.1.4_1783327115187_0.3815807344768014","host":"s3://npm-registry-packages-npm-production"}},"0.1.5":{"name":"@12nil/theme-registry-package","version":"0.1.5","description":"Theme registry and React provider utilities","license":"MIT","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","bin":{"runtime-theme-registry":"bin/runtime-theme-registry.mjs"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./eslint":{"import":"./eslint/index.cjs","require":"./eslint/index.cjs"}},"sideEffects":false,"scripts":{"build":"tsup index.ts --format esm,cjs --dts --sourcemap --clean","dev":"tsup index.ts --format esm,cjs --dts --watch","prepublishOnly":"npm run build"},"peerDependencies":{"react":">=18"},"devDependencies":{"@types/react":"^18.3.3","tsup":"^8.2.4","typescript":"^5.8.3"},"publishConfig":{"access":"public"},"_id":"@12nil/theme-registry-package@0.1.5","_nodeVersion":"24.18.0","_npmVersion":"11.16.0","dist":{"integrity":"sha512-mmz2NuUalfiuGCIsvO8/RkxVR18TK4bvG7naxe4j5KkIX+bIH6fXP1NshNfYRsj2FaJf7KfRqO1gnGQrqbdL1g==","shasum":"bded03d47fdc0096caa6bec8eebb8cb1c7d7b4ab","tarball":"https://registry.npmjs.org/@12nil/theme-registry-package/-/theme-registry-package-0.1.5.tgz","fileCount":11,"unpackedSize":411837,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCMHzCqT2K3+17H8efYTKggVrJcjwIW8gI2sI9KQZCz9AIgVIvOLuX8OzC7+xnDA64Dd9ijxZNKYzv3N0YYmEwjjPY="}]},"_npmUser":{"name":"dev-jeff","email":"agadiajeffrey@gmail.com"},"directories":{},"maintainers":[{"name":"dev-jeff","email":"agadiajeffrey@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/theme-registry-package_0.1.5_1783330823216_0.5017905955870305"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-04T13:26:30.543Z","modified":"2026-07-06T09:40:23.508Z","0.1.0":"2026-07-04T13:26:30.882Z","0.1.1":"2026-07-04T14:17:03.983Z","0.1.2":"2026-07-05T13:54:04.383Z","0.1.3":"2026-07-05T14:04:49.300Z","0.1.4":"2026-07-06T08:38:35.324Z","0.1.5":"2026-07-06T09:40:23.360Z"},"license":"MIT","description":"Theme registry and React provider utilities","maintainers":[{"name":"dev-jeff","email":"agadiajeffrey@gmail.com"}],"readme":"# Runtime Theme Registry\r\n\r\nRuntime Theme Registry is a TypeScript-first theming runtime for React applications.\r\n\r\nIt provides:\r\n\r\n- Runtime theme registration and switching\r\n- React integration with ThemeProvider and useTheme\r\n- Legacy and semantic token schemas\r\n- Plugin contributions (icons, fonts, spacing, radii, typography)\r\n- Brand governance with strict registration, runtime guardrails, and CI audits\r\n- Composition layers, async loading, cache helpers, and hydration helpers\r\n\r\n## Table of Contents\r\n\r\n- Installation\r\n- Quick Start\r\n- Theme Models\r\n- React APIs\r\n- Theme Switcher Components\r\n- Registry APIs\r\n- Brand Governance Toolkit\r\n- CLI\r\n- Tailwind v4 Integration\r\n- Advanced Workflows\r\n- API Overview\r\n- Development\r\n- Related Docs\r\n\r\n## Installation\r\n\r\n```bash\r\nnpm install @12nil/theme-registry-package\r\n```\r\n\r\nPeer dependency:\r\n\r\n- react >= 18\r\n\r\n## Quick Start\r\n\r\nCreate and register at least one theme before rendering ThemeProvider.\r\n\r\n```ts\r\nimport { themeRegistry } from '@12nil/theme-registry-package'\r\n\r\nthemeRegistry.register({\r\n  name: 'default',\r\n  modes: {\r\n    light: {\r\n      primary: '#111827',\r\n      secondary: '#374151',\r\n      background: '#ffffff',\r\n      text: '#111827',\r\n      accent: '#2563eb',\r\n      muted: '#6b7280',\r\n      error: '#dc2626',\r\n      warning: '#f59e0b',\r\n      success: '#16a34a',\r\n      info: '#0ea5e9',\r\n    },\r\n    dark: {\r\n      primary: '#e5e7eb',\r\n      secondary: '#9ca3af',\r\n      background: '#030712',\r\n      text: '#e5e7eb',\r\n      accent: '#60a5fa',\r\n      muted: '#6b7280',\r\n      error: '#f87171',\r\n      warning: '#fbbf24',\r\n      success: '#4ade80',\r\n      info: '#38bdf8',\r\n    },\r\n  },\r\n})\r\n\r\nthemeRegistry.setFallback({\r\n  themeName: 'default',\r\n  modeName: 'light',\r\n})\r\n```\r\n\r\n```tsx\r\nimport React from 'react'\r\nimport { ThemeProvider, ThemeSwitcherStyled } from '@12nil/theme-registry-package'\r\n\r\nexport default function App() {\r\n  return (\r\n    <ThemeProvider>\r\n      <div style={{ padding: 24 }}>\r\n        <ThemeSwitcherStyled title=\"Theme Controls\" subtitle=\"Choose theme and mode\" />\r\n        <h1 style={{ color: 'var(--color-theme-text)' }}>Hello Theme</h1>\r\n      </div>\r\n    </ThemeProvider>\r\n  )\r\n}\r\n```\r\n\r\n## Theme Models\r\n\r\nTheme tokens support two schemas.\r\n\r\n1. Legacy palette\r\n\r\nRequired keys:\r\n\r\n- primary\r\n- secondary\r\n- background\r\n- text\r\n- accent\r\n- muted\r\n- error\r\n- warning\r\n- success\r\n- info\r\n\r\nOptional key:\r\n\r\n- tertiary\r\n\r\n2. Semantic tokens (v2)\r\n\r\nRequired categories:\r\n\r\n- colors\r\n- surface\r\n- text\r\n- border\r\n\r\nEach category has required keys validated by the registry.\r\n\r\n## React APIs\r\n\r\nThemeProvider responsibilities:\r\n\r\n- Loads registered themes from themeRegistry\r\n- Restores persisted selection from localStorage\r\n- Resolves invalid selections via fallback rules\r\n- Applies resolved CSS variables to document root\r\n\r\nPersistence keys:\r\n\r\n- runtime-theme-registry-theme\r\n- runtime-theme-registry-mode\r\n\r\nThemeProvider props:\r\n\r\n- injectUtilityClasses?: boolean (default true)\r\n\r\nuseTheme returns:\r\n\r\n- currentTheme\r\n- currentMode\r\n- themes\r\n- availableModes\r\n- setTheme(themeName, modeName)\r\n- setMode(modeName)\r\n- getThemeColors()\r\n\r\n## Theme Switcher Components\r\n\r\nExports:\r\n\r\n- ThemeSwitcher\r\n- ThemeSwitcherStyled\r\n\r\nCommon switcher props:\r\n\r\n- themeLabel\r\n- modeLabel\r\n- showThemeSelector (default true)\r\n- showModeSelector (default true)\r\n- modeSelectorVariant: select | buttons\r\n- modeIcons?: Partial<Record<string, React.ReactNode>>\r\n- modeIconOnly?: boolean\r\n- onThemeChanged\r\n- onModeChanged\r\n\r\nMode-only icon example:\r\n\r\n```tsx\r\nimport { ThemeProvider, ThemeSwitcherStyled } from '@12nil/theme-registry-package'\r\nimport { Moon, Sun } from 'lucide-react'\r\n\r\nexport default function App() {\r\n  return (\r\n    <ThemeProvider>\r\n      <ThemeSwitcherStyled\r\n        showThemeSelector={false}\r\n        showModeSelector\r\n        modeSelectorVariant=\"buttons\"\r\n        modeIconOnly\r\n        modeIcons={{\r\n          light: <Sun size={16} />,\r\n          dark: <Moon size={16} />,\r\n        }}\r\n      />\r\n    </ThemeProvider>\r\n  )\r\n}\r\n```\r\n\r\n## Registry APIs\r\n\r\nCore registration and lookup:\r\n\r\n- register, replace, merge, unregister\r\n- get, getAll, has\r\n- getTokens, getModes, getVariants\r\n- setCurrent, getCurrent\r\n- setFallback, resolveSelection\r\n\r\nDynamic theme extension:\r\n\r\n- createTheme\r\n- registerMode\r\n- registerModes\r\n\r\nPlugins:\r\n\r\n- registerPlugin, unregisterPlugin\r\n- getPlugin, getPlugins\r\n- getPluginContributions, getMergedPluginContributions\r\n\r\nComposition:\r\n\r\n- registerCompositionLayer\r\n- getCompositionLayer, getCompositionLayers\r\n- compose\r\n\r\nLoading and cache:\r\n\r\n- load(source, options)\r\n- clearLoadCache(cacheKey?)\r\n\r\nHydration helpers:\r\n\r\n- getInitialThemeAttributes\r\n- hydrateThemeOnDocument\r\n\r\nEvents:\r\n\r\n- on(eventName, handler)\r\n- onRegistered\r\n- onThemeChanged\r\n- onVariantAdded\r\n- onLoaded\r\n- onComposed\r\n- onBrandViolation\r\n- onDestroyed\r\n\r\n## Brand Governance Toolkit\r\n\r\nThis package supports three governance layers.\r\n\r\n### Phase 1: Contract Strictness at Registration\r\n\r\nDefine a BrandContract and enforce it during register and registerPlugin calls.\r\n\r\n```ts\r\nimport { themeRegistry, type BrandContract } from '@12nil/theme-registry-package'\r\n\r\nconst contract: BrandContract = {\r\n  name: 'Acme Brand v1',\r\n  defaultLevel: 'error',\r\n  colors: {\r\n    requireCssVariable: true,\r\n    allowedByToken: {\r\n      'colors.primary': ['var(--brand-primary)'],\r\n      primary: ['var(--brand-primary)'],\r\n    },\r\n  },\r\n  fonts: {\r\n    allowedValues: ['Inter, sans-serif', 'Merriweather, serif'],\r\n  },\r\n  spacing: {\r\n    allowedValues: ['4px', '8px', '12px', '16px', '24px', '32px'],\r\n  },\r\n  borderRadius: {\r\n    allowedValues: ['0', '4px', '8px', '12px', '9999px'],\r\n  },\r\n}\r\n\r\nthemeRegistry.setBrandContract(contract, { enforce: true })\r\n```\r\n\r\nSeverity behavior:\r\n\r\n- error: throws and blocks registration\r\n- warn: logs warning and allows registration\r\n- off: skips the rule\r\n\r\nOptional per-call override:\r\n\r\n```ts\r\nthemeRegistry.register(theme, { enforceBrand: true })\r\nthemeRegistry.registerPlugin(plugin, { enforceBrand: true })\r\n```\r\n\r\n### Phase 2: Runtime Guardrails\r\n\r\nEnable runtime checks in development to catch drift during load and theme application.\r\n\r\n```ts\r\nthemeRegistry.setBrandRuntimeGuard({\r\n  enabled: true,\r\n  devOnly: true,\r\n  treatWarningsAsErrors: false,\r\n  onViolation: (payload) => {\r\n    console.log(payload.context, payload.issues)\r\n  },\r\n})\r\n\r\nconst unsubscribe = themeRegistry.onBrandViolation((payload) => {\r\n  console.log('brand violation', payload)\r\n})\r\n\r\n// Call unsubscribe() when you no longer need the listener.\r\n```\r\n\r\nRuntime guard checks run during:\r\n\r\n- load\r\n- compose\r\n- setCurrent\r\n- hydrateThemeOnDocument\r\n\r\n### Phase 3: CI Audit\r\n\r\nUse the CLI to scan JSON themes and source files against the same contract.\r\n\r\n```bash\r\nnpx runtime-theme-registry audit-brand ./brand-contract.json ./themes\r\n```\r\n\r\n### ESLint Rule for Off-Brand Values\r\n\r\nThis package includes an ESLint rule that reports off-brand values in:\r\n\r\n- style object literals (for example color, backgroundColor, spacing, borderRadius, fontFamily)\r\n- className arbitrary color syntax (for example `bg-[#ff0000]`)\r\n\r\nRule id:\r\n\r\n- `runtime-theme-registry/brand-contract`\r\n- `runtime-theme-registry/tailwind-brand-classes`\r\n\r\nFlat config example (`eslint.config.js`):\r\n\r\n```js\r\nimport runtimeThemeRegistryEslint from '@12nil/theme-registry-package/eslint'\r\n\r\nexport default [\r\n  {\r\n    files: ['**/*.{js,jsx,ts,tsx}'],\r\n    plugins: {\r\n      'runtime-theme-registry': runtimeThemeRegistryEslint,\r\n    },\r\n    rules: {\r\n      'runtime-theme-registry/brand-contract': ['error', {\r\n        contractPath: './brand-contract.json',\r\n      }],\r\n      'runtime-theme-registry/tailwind-brand-classes': ['error', {\r\n        contractPath: './brand-contract.json',\r\n      }],\r\n    },\r\n  },\r\n]\r\n```\r\n\r\nRule options:\r\n\r\n- `contractPath` path to BrandContract JSON (default `./brand-contract.json`)\r\n- `rootDir` base path for resolving `contractPath` (default process cwd)\r\n\r\n`tailwind-brand-classes` options:\r\n\r\n- `allowedClasses` extra Tailwind color utilities to allow\r\n- `allowArbitraryValues` allow classes like `bg-[#ff0000]` (default `false`)\r\n\r\nThe `tailwind-brand-classes` rule enforces allowlisted color utility usage such as:\r\n\r\n- `bg-primary`, `text-muted`, `border-border-subtle`\r\n- `fill-primary`, `stroke-info`\r\n\r\nand reports non-approved classes such as:\r\n\r\n- `bg-red-500`\r\n- `text-blue-600`\r\n- `border-emerald-400`\r\n\r\nSeverity behavior:\r\n\r\n- ESLint controls final severity (`error` or `warn`) based on your rule config.\r\n- Contract `warn` and `error` levels are preserved in the message prefix for context.\r\n- Contract `off` disables checks for that rule.\r\n\r\n## CLI\r\n\r\n### Inject utility classes into CSS\r\n\r\n```bash\r\nnpx runtime-theme-registry inject-css ./src/app/globals.css\r\n```\r\n\r\nThis adds or updates a marked utility block.\r\n\r\n### Audit brand compliance\r\n\r\n```bash\r\nnpx runtime-theme-registry audit-brand ./brand-contract.json ./themes\r\n```\r\n\r\nFlags:\r\n\r\n- --format json|text (default text)\r\n- --fail-on error|warn (default error)\r\n- --source-path <dir-or-file>\r\n- --no-source-scan\r\n- --source-scope all|strict (default all)\r\n- --source-include <csv-roots>\r\n\r\nStrict source scope defaults:\r\n\r\n- src\r\n- app\r\n- components\r\n- pages\r\n- styles\r\n\r\nExamples:\r\n\r\n```bash\r\n# Fail only on error-level issues.\r\nnpx runtime-theme-registry audit-brand ./brand-contract.json ./themes --format text --fail-on error\r\n\r\n# Fail on warnings too, output JSON for CI parsers.\r\nnpx runtime-theme-registry audit-brand ./brand-contract.json ./themes --format json --fail-on warn\r\n\r\n# Audit theme JSON under ./themes but scan source files in ./src.\r\nnpx runtime-theme-registry audit-brand ./brand-contract.json ./themes --source-path ./src\r\n\r\n# Strict source scope using defaults.\r\nnpx runtime-theme-registry audit-brand ./brand-contract.json . --source-scope strict\r\n\r\n# Strict source scope with explicit roots.\r\nnpx runtime-theme-registry audit-brand ./brand-contract.json . --source-scope strict --source-include src,app,components\r\n```\r\n\r\nAudit summary fields:\r\n\r\n- filesScanned\r\n- sourceFilesScanned\r\n- themesScanned\r\n- errorCount\r\n- warningCount\r\n- sourceScope\r\n- sourceRoots\r\n- status\r\n\r\n## Tailwind v4 Integration\r\n\r\nThemeProvider can inject utility classes at runtime.\r\n\r\nIf you prefer CSS-first mapping in global styles:\r\n\r\n```css\r\n@import \"tailwindcss\";\r\n\r\n@theme inline {\r\n  --color-primary: var(--theme-primary);\r\n  --color-secondary: var(--theme-secondary);\r\n  --color-background: var(--theme-background);\r\n  --color-text: var(--theme-text);\r\n  --color-accent: var(--theme-accent);\r\n  --color-muted: var(--theme-muted);\r\n  --color-error: var(--theme-error);\r\n  --color-warning: var(--theme-warning);\r\n  --color-success: var(--theme-success);\r\n  --color-info: var(--theme-info);\r\n  --color-surface-card: var(--surface-card);\r\n  --color-border-subtle: var(--border-subtle);\r\n}\r\n```\r\n\r\nUse mapped utilities:\r\n\r\n```tsx\r\n<button className=\"bg-primary text-background border border-border-subtle\">Save</button>\r\n<p className=\"text-muted\">Secondary text</p>\r\n<div className=\"bg-surface-card\">Card</div>\r\n```\r\n\r\nIf you inject utility classes at build-time via CLI, disable runtime injection:\r\n\r\n```tsx\r\n<ThemeProvider injectUtilityClasses={false}>\r\n  <App />\r\n</ThemeProvider>\r\n```\r\n\r\n## Advanced Workflows\r\n\r\n### Plugin registration and contributions\r\n\r\n```ts\r\nimport { themeRegistry } from '@12nil/theme-registry-package'\r\n\r\nthemeRegistry.registerPlugin({\r\n  name: 'CRM',\r\n  version: '1.0.0',\r\n  themes: [\r\n    {\r\n      name: 'crm',\r\n      modes: {\r\n        light: {\r\n          primary: '#1d4ed8',\r\n          secondary: '#2563eb',\r\n          background: '#ffffff',\r\n          text: '#0f172a',\r\n          accent: '#1d4ed8',\r\n          muted: '#64748b',\r\n          error: '#dc2626',\r\n          warning: '#f59e0b',\r\n          success: '#16a34a',\r\n          info: '#0ea5e9',\r\n        },\r\n      },\r\n    },\r\n  ],\r\n  fonts: {\r\n    body: 'Inter, sans-serif',\r\n  },\r\n  spacing: {\r\n    md: '16px',\r\n  },\r\n  radii: {\r\n    card: '12px',\r\n  },\r\n})\r\n\r\nconst merged = themeRegistry.getMergedPluginContributions()\r\nconsole.log(merged.fonts)\r\n```\r\n\r\n### Composition and loading\r\n\r\n```ts\r\nimport { themeRegistry } from '@12nil/theme-registry-package'\r\n\r\nthemeRegistry.registerCompositionLayer({\r\n  type: 'brand',\r\n  name: 'fnp',\r\n  tokens: {\r\n    primary: '#031011',\r\n    secondary: '#153e46',\r\n    background: '#ffffff',\r\n    text: '#031011',\r\n    accent: '#378d93',\r\n    muted: '#667085',\r\n    error: '#DF1C41',\r\n    warning: '#F4C790',\r\n    success: '#27AE60',\r\n    info: '#378d93',\r\n  },\r\n})\r\n\r\nthemeRegistry.registerCompositionLayer({\r\n  type: 'appearance',\r\n  name: 'dark',\r\n  tokens: {\r\n    background: '#031011',\r\n    text: '#c4e8ee',\r\n    secondary: '#378d93',\r\n    primary: '#c4e8ee',\r\n    accent: '#378d93',\r\n    muted: '#667085',\r\n    error: '#DF1C41',\r\n    warning: '#F4C790',\r\n    success: '#27AE60',\r\n    info: '#378d93',\r\n  },\r\n})\r\n\r\nconst composed = themeRegistry.compose({ brand: 'fnp', appearance: 'dark' })\r\n\r\nawait themeRegistry.load('/api/themes', {\r\n  ifExists: 'merge',\r\n  cacheKey: 'customer-themes',\r\n  maxAgeMs: 5 * 60 * 1000,\r\n})\r\n\r\nconst attrs = themeRegistry.getInitialThemeAttributes('fnp', 'dark')\r\nconst hydrated = themeRegistry.hydrateThemeOnDocument('fnp', 'dark')\r\n\r\nthemeRegistry.clearLoadCache('customer-themes')\r\n```\r\n\r\n## API Overview\r\n\r\nMain exports include:\r\n\r\n- ThemeProvider, useTheme\r\n- ThemeSwitcher, ThemeSwitcherStyled\r\n- themeRegistry\r\n- validateTheme, isThemeTokensV2\r\n- validateThemeAgainstBrand, validatePluginAgainstBrand\r\n- paletteToCSSVariables, tokenSetToCSSVariables\r\n- createTailwindThemeColorMap\r\n- generateThemeCSS, generateAllThemesCSS, applyThemeToDocument\r\n- Comprehensive types for themes, plugins, governance, composition, events, and loading\r\n\r\n## Development\r\n\r\n```bash\r\nnpm.cmd install\r\nnpm.cmd run build\r\n```\r\n\r\nPublish checklist:\r\n\r\n1. Update package name/version in package.json.\r\n2. Run npm.cmd run build.\r\n3. Run npm.cmd pack --dry-run.\r\n4. Publish with npm.cmd publish --access public.\r\n\r\n## Related Docs\r\n\r\n- Full reference: [DOCUMENTATION.md](DOCUMENTATION.md)\r\n- Practical app setup: [REAL_PROJECT_GUIDE.md](REAL_PROJECT_GUIDE.md)\r\n- Fast onboarding: [QUICKSTART.md](QUICKSTART.md)\r\n- Roadmap: [ROADMAP.md](ROADMAP.md)\r\n","readmeFilename":"README.md"}