CherryHQ/cherry-studio · error · Error

[theme-contract] shadcn.css declares unregistered Shadcn var

Error message

[theme-contract] shadcn.css declares unregistered Shadcn variable ${declaration.name}

What it means

Thrown by validateThemeContractSources when shadcn.css declares a --<name> variable that is not in the SHADCN_VARIABLE_TOKENS registry. The Shadcn contract is a closed set; only registered official variables may be declared there. This catches product roles (e.g. --success) or invented Shadcn roles leaking into shadcn.css.

Source

Thrown at packages/ui/scripts/validate-theme-contract.ts:355

  }
  assertCompatibilityTokensDeclared(
    'compatibility semantic colors',
    COMPATIBILITY_SEMANTIC_COLOR_TOKENS,
    sources.providerColors,
    'tokens/colors/providers.css'
  )
  assertCompatibilityTokensDeclared(
    'compatibility status colors',
    COMPATIBILITY_STATUS_COLOR_TOKENS,
    sources.statusLegacyColors,
    'tokens/colors/status-legacy.css'
  )

  const shadcnRootDeclarations = buildDeclarationMap([['shadcn.css', sources.shadcn]], ':root')
  assertRequiredDeclarations('Shadcn contract in shadcn.css', shadcnRootDeclarations, SHADCN_VARIABLE_TOKENS, '--')
  for (const declaration of extractDeclarations(sources.shadcn, 'shadcn.css')) {
    if (!shadcnVariableNames.has(declaration.name)) {
      throw new Error(`[theme-contract] shadcn.css declares unregistered Shadcn variable ${declaration.name}`)
    }
  }

  const productRootDeclarations = buildDeclarationMap([['product.css', sources.product]], ':root')
  assertRequiredDeclarations(
    'product contract in product.css',
    productRootDeclarations,
    CHERRY_PRODUCT_VARIABLE_TOKENS,
    '--'
  )

  assertSurfacePairs('Shadcn contract', SHADCN_SURFACE_PAIRS, shadcnVariables)
  assertSurfacePairs('product contract', CHERRY_PRODUCT_SURFACE_PAIRS, productVariables)

  assertExactImports('tokens.css', sources.tokensEntry, ['./tokens/index.css'])
  assertExactImports('tokens/index.css', sources.tokensIndex, [
    './colors/primitive.css',
    './colors/status-legacy.css',

View on GitHub (pinned to 726446b54c)

Solutions

  1. If it is a genuine new official Shadcn variable, register the unprefixed name in SHADCN_VARIABLE_TOKENS in scripts/theme-contract.ts, declare it in shadcn.css :root, and document it in variable-catalog.md.
  2. If it is a product role (e.g. --success), move the declaration to product.css and ensure it is in CHERRY_PRODUCT_VARIABLE_TOKENS.
  3. Re-run `pnpm --filter @cherrystudio/ui theme:check`.

Example fix

// before (shadcn.css)
:root { --success: hotpink; }
// after — move to product.css (success is a product token)
// product.css
:root { --success: var(--cs-green-600); }
Defensive patterns

Strategy: validation

Validate before calling

// Every variable declared in shadcn.css must be a registered Shadcn variable.
const allowed = new Set(SHADCN_VARIABLE_TOKENS.map((t) => `--${t}`))
const offenders = [...sources.shadcn.matchAll(/(?:^|[;{])\s*(--[a-z0-9-]+)\s*:/g)].map((m) => m[1]).filter((n) => !allowed.has(n))
if (offenders.length) throw new Error(`shadcn.css declares unregistered vars: ${offenders.join(', ')}`)

Try / catch

try {
  validateThemeContractSources(sources)
} catch (error) {
  if (error instanceof Error && /shadcn\.css declares unregistered Shadcn variable/.test(error.message)) {
    console.error(error.message)
    process.exitCode = 1
    return
  }
  throw error
}

Prevention

When it happens

Trigger: Adding `:root { --success: hotpink; }` to shadcn.css (success is a product variable); inventing a new official role `--new-role` without registering it; pasting a product declaration into shadcn.css.

Common situations: Adding a Shadcn component's variable to the wrong file; extending the Shadcn surface and forgetting the registry.

Related errors


AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12). Data as JSON: /api/errors/aa9c8d2966f0c73b. Report an issue: GitHub.