Skip to main content

Breaking: cva 1.0 upgrade removes cvaMerge from @trackunit/css-class-variance-utilities

@trackunit/css-class-variance-utilities (from 1.14.27) has upgraded to the upstream cva@1.0.0-beta.8 package. cvaMerge is removed with no compatibility shim — if any of your code calls it, bumping this dependency without running the migration is a compile failure, not a deprecation warning. Repos that never called cvaMerge are unaffected.

Run these two commands in the consuming repo after bumping the dependency:

nx g @trackunit/migrations:migrate
nx g @trackunit/migrations:run-migrations

Why this changed

cvaMerge was a hand-written wrapper around class-variance-authority that merged conflicting Tailwind classes. cva@1.0 ships that integration natively via defineConfig({ hooks: { onComplete } }), so the custom wrapper became maintenance we don't need to carry. cva is now a thin wrapper over the upstream cva package with tailwind-merge wired into its onComplete hook — callers should stop calling twMerge themselves, since cx and compose resolve conflicts automatically.

When it affects you

Nothing changes until you bump @trackunit/css-class-variance-utilities. At or above 1.14.27, cvaMerge is gone and any call site still using the old shape stops compiling until migrated.

The old call took the base classes as a positional argument; cva takes a single config object with a base property:

// before
cvaMerge(["grid", "h-full"], { variants: { ... } });
// after
cva({ base: ["grid", "h-full"], variants: { ... } });

What the migration does

  • migrate discovers the package's pending migration and writes it to trackunit-migrations.json. run-migrations applies it. A second run is a no-op — already-applied identities are recorded and skipped.
  • The codemod rewrites the imports and the call shape, preserving comments and formatting.
  • Any call site it can't rewrite with confidence — a base or config built at runtime, or an aliased import — is reported with its file and line rather than guessed at. Fix those by hand.
  • What it does not fix — null variant props. cva 0.7 accepted null for a variant prop and suppressed that variant; cva 1.0's types reject null, and at runtime null falls through to the variant's defaultVariants entry. The codemod rewrites variant definitions, not their consumers, so these surface as type errors after migrating. undefined is the replacement, but the two are only equivalent when that variant key has no defaultVariants entry — where a default exists, undefined silently applies the default class instead of no class. This bit us at two call sites internally, and it's the detail most likely to bite you too.

For the .variants.ts authoring guide, see Storybook Foundations → Styling variants.