shadcn-ui/ui · warning

Skipped ${skippedTokens.size} token${skippedTokens.size ===

Error message

Skipped ${skippedTokens.size} token${skippedTokens.size === 1 ? "" : "s"}. These were left untouched:

What it means

Not a thrown exception — a logger.warn emitted after the base-color migration finishes. getBaseColorMigration only rewrites tokens that currently still hold the source palette's stock value; tokens missing from your CSS ('not found in your CSS') or manually customized ('does not match the source base color') are collected and reported here, left untouched on purpose so your overrides survive.

Source

Thrown at packages/shadcn/src/migrations/migrate-base-color.ts:186

    })
  }

  migrationSpinner.succeed("Migration complete.")

  // Keep components.json in sync so future `shadcn add` installs use the new
  // base color.
  await updateConfigBaseColor(config, targetBaseColor)

  const skippedTokens = new Map<string, string>()
  for (const { token, reason } of skipped) {
    if (!skippedTokens.has(token)) {
      skippedTokens.set(token, reason)
    }
  }

  if (skippedTokens.size > 0) {
    logger.break()
    logger.warn(
      `Skipped ${skippedTokens.size} token${
        skippedTokens.size === 1 ? "" : "s"
      }. These were left untouched:`
    )
    for (const [token, reason] of Array.from(skippedTokens)) {
      logger.warn(`  - ${token}: ${reason}`)
    }
  }
}

export function getBaseColorMigration(
  css: string,
  sourceVars: CssVars,
  targetVars: CssVars,
  tailwindVersion: TailwindVersion
): {
  cssVars: { light: Record<string, string>; dark: Record<string, string> }
  skipped: SkippedToken[]

View on GitHub (pinned to c06da1d0e9)

Solutions

  1. Treat the warning as a review list: open your CSS and update each listed token to the target palette's value (or confirm you want to keep the override).
  2. If a listed token should have migrated, reset it to the source palette's stock value in your CSS and re-run `shadcn migrate base-color` — it will then match and be replaced.
  3. Fetch the target palette values from the registry (colors/<to>.json) to copy exact values for manual updates.

Example fix

/* before — hand-tuned token blocked the swap; migration warned:
   Skipped 1 token. These were left untouched:
     - --primary: does not match the source base color */
:root { --primary: 142 76% 36%; }

/* after — manually port the token to the target palette (neutral) */
:root { --primary: 0 0% 9%; }
Defensive patterns

Strategy: validation

Validate before calling

import { readFileSync } from 'fs'
import postcss from 'postcss'

// Dry-run the migration's own matcher before writing anything:
// tokens absent from :root/.dark or diverging from the stock source values will be skipped.
function findAtRiskTokens(cssPath: string, stockSourceVars: Record<string, string>): string[] {
  const current: Record<string, string> = {}
  postcss.parse(readFileSync(cssPath, 'utf-8')).walkRules((rule) => {
    if (rule.selector !== ':root' && rule.selector !== '.dark') return
    rule.walkDecls((d) => { if (d.prop.startsWith('--')) current[d.prop] = d.value.trim().replace(/\s+/g, ' ') })
  })
  return Object.entries(stockSourceVars)
    .filter(([name, value]) => current[`--${name}`] !== value.trim().replace(/\s+/g, ' '))
    .map(([name]) => `--${name}`)
}

Prevention

When it happens

Trigger: Running `shadcn migrate base-color` on a stylesheet where some source-palette tokens are absent (partial :root/.dark blocks) or hold hand-edited values — e.g. you tweaked --primary or --radius yourself — plus tokens identical in both palettes are silently skipped without being listed.

Common situations: Hand-tuned themes; Tailwind v3 to v4 transitions where values are written differently (hsl() wrapping); partially adopted variable sets; brand color overrides that developers actually want preserved.

Related errors


AI-assisted analysis of shadcn-ui/ui@c06da1d0e9 (2026-08-21). Data as JSON: /api/errors/d05c95345a3becff. Report an issue: GitHub.