{"record":{"id":"6eec0354502e389e","repo":"CherryHQ/cherry-studio","slug":"theme-contract-renderer-theme-must-use-the-share","errorCode":null,"errorMessage":"[theme-contract] renderer theme must use the shared generated Tailwind adapter","messagePattern":"\\[theme-contract\\] renderer theme must use the shared generated Tailwind adapter","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/ui/scripts/validate-migration-contract.ts","lineNumber":187,"sourceCode":"      throw new Error(`[theme-contract] migration ${rule.source} points outside the canonical contract: ${rule.target}`)\n    }\n  }\n\n  if (sources.legacyAliases.trim() !== '') {\n    throw new Error('[theme-contract] legacy compatibility layer must remain removed')\n  }\n\n  const rendererTheme = stripComments(sources.rendererTheme)\n  if (rendererTheme.includes('legacy-vars.css')) {\n    throw new Error('[theme-contract] renderer theme cannot import the removed legacy compatibility layer')\n  }\n  if (rendererTheme.includes('--app-')) {\n    throw new Error(\n      '[theme-contract] renderer theme entry cannot own --app-* variables; keep host-local values in a dedicated stylesheet'\n    )\n  }\n  if (/@theme(?:\\s+inline)?\\s*\\{/.test(rendererTheme)) {\n    throw new Error('[theme-contract] renderer theme must use the shared generated Tailwind adapter')\n  }\n\n  for (const [fileName, source] of Object.entries(sources.rendererStyles)) {\n    const adapterVariable = stripComments(source).match(TAILWIND_ADAPTER_VARIABLE_PATTERN)?.[0]\n\n    if (adapterVariable) {\n      throw new Error(\n        `[theme-contract] renderer stylesheet ${fileName} cannot use Tailwind adapter variable ${adapterVariable}; use runtime semantic variables directly`\n      )\n    }\n  }\n\n  for (const [fileName, source] of Object.entries(sources.rendererTypeScriptSources)) {\n    const adapterVariable = source.includes('--color-') ? findTypeScriptAdapterVariable(source, fileName) : undefined\n\n    if (adapterVariable) {\n      throw new Error(\n        `[theme-contract] renderer TypeScript source ${fileName} cannot use Tailwind adapter variable ${adapterVariable}; use runtime semantic variables or Tailwind utilities`","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/packages/ui/scripts/validate-migration-contract.ts#L169-L205","documentation":"Thrown by the theme migration contract validator when the renderer theme entry (src/renderer/assets/styles/tailwind.css) contains a @theme { } or @theme inline { } block. The contract requires all renderer themes to consume the shared generated Tailwind adapter rather than declaring their own @theme blocks, ensuring a single source of truth for Tailwind theme tokens. This is a CI/build-time validation, not a runtime error.","triggerScenarios":"A developer edits src/renderer/assets/styles/tailwind.css and adds a @theme { ... } or @theme inline { ... } directive to define custom Tailwind theme values locally. The validator scans the file (after stripping comments) with the regex /@theme(?:\\s+inline)?\\s*\\{/ and throws on match.","commonSituations":"A developer unfamiliar with the architecture contract tries to add custom design tokens via a local @theme block instead of the shared theme system. Copy-pasting a Tailwind CSS v4 tutorial that uses @theme. Attempting to override a theme value locally without understanding the centralized adapter requirement. A merge that reintroduces a previously removed @theme block.","solutions":["Remove the @theme / @theme inline block from the renderer theme entry (src/renderer/assets/styles/tailwind.css).","If you need custom theme tokens, add them to the shared theme system: declare them in packages/ui/src/styles/ (e.g., product.css or a token file) following the contract, not in the renderer entry.","If the block was added inadvertently (merge artifact), revert that section to use the shared Tailwind adapter import.","Run the validator locally: npx tsx packages/ui/scripts/validate-migration-contract.ts to confirm the fix."],"exampleFix":"// before — src/renderer/assets/styles/tailwind.css\n@import \"tailwindcss\";\n@theme {\n  --color-my-brand: #ff0000;\n}\n\n// after — remove @theme, rely on shared adapter\n@import \"tailwindcss\";\n@import \"@cherrystudio/ui/styles\";\n/* custom tokens go in packages/ui/src/styles/product.css */","handlingStrategy":"validation","validationCode":"// Run the migration contract validator before committing theme changes\n// In package.json scripts or as a pre-commit hook:\n// \"theme:migration-check\": \"tsx packages/ui/scripts/validate-migration-contract.ts\"\n// Then: pnpm theme:migration-check\n// The validator scans src/renderer/assets/styles/tailwind.css for @theme blocks","typeGuard":"// Regex check to run before committing renderer theme changes\nimport { readFileSync } from 'node:fs'\n\nfunction checkNoThemeBlock(filePath: string): void {\n  const source = readFileSync(filePath, 'utf8').replace(/\\/\\*[\\s\\S]*?\\*\\//g, '')\n  if (/@theme(?:\\s+inline)?\\s*\\{/.test(source)) {\n    throw new Error(`${filePath} contains a @theme block — use the shared Tailwind adapter instead`)\n  }\n}\n\ncheckNoThemeBlock('src/renderer/assets/styles/tailwind.css')","tryCatchPattern":null,"preventionTips":["Never add @theme or @theme inline blocks to renderer CSS — all theme tokens come from the shared adapter.","Add the contract validator to your pre-commit hooks or CI pipeline.","When onboarding new developers, document the theme architecture: renderer entries consume the shared adapter, they don't declare tokens.","If you need custom design tokens, add them to packages/ui/src/styles/ following the contract, not to renderer CSS."],"tags":["theme-contract","build-validation","tailwind","css","architecture"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}