Repository: tailwindlabs/tailwindcss
File: packages/@tailwindcss-upgrade/src/template/codemods/theme-to-var.test.ts (TypeScript)

Code before:
  ['[color:theme(colors.red.500/var(--opacity))]', '[color:var(--color-red-500)]/[var(--opacity)]'],
  ['[color:theme(colors.red.500/.12345)]', '[color:var(--color-red-500)]/[12.345]'],
  ['[color:theme(colors.red.500/50.25%)]', '[color:var(--color-red-500)]/[50.25%]'],

  // Arbitrary value
  ['bg-[theme(colors.red.500/75%)]', 'bg-[var(--color-red-500)]/75'],
  ['bg-[theme(colors.red.500/12.34%)]', 'bg-[var(--color-red-500)]/[12.34%]'],

  // Arbitrary property that already contains a modifier
  ['[color:theme(colors.red.500/50%)]/50', '[color:theme(--color-red-500/50%)]/50'],

  // Arbitrary value, where the candidate already contains a modifier
  // This should still migrate the `theme(…)` syntax to the modern syntax.
  ['bg-[theme(colors.red.500/50%)]/50', 'bg-[theme(--color-red-500/50%)]/50'],

  // Variants, we can't use `var(…)` especially inside of `@media(…)`. We can
  // still upgrade the `theme(…)` to the modern syntax.
  ['max-[theme(spacing.4)]:flex', 'max-[theme(--spacing-4)]:flex'],

  // This test in itself doesn't make much sense. But we need to make sure
  // that this doesn't end up as the modifier in the candidate itself.
  ['max-[theme(spacing.4/50)]:flex', 'max-[theme(--spacing-4/50)]:flex'],

  // `theme(…)` calls in another CSS function is replaced correctly.
  // Additionally we remove unnecessary whitespace.
  ['grid-cols-[min(50%,theme(spacing.80))_auto]', 'grid-cols-[min(50%,var(--spacing-80))_auto]'],

  // `theme(…)` calls valid in v3, but not in v4 should still be converted.
  ['[--foo:theme(fontWeight.semibold)]', '[--foo:theme(fontWeight.semibold)]'],

  // `screens` values
  ['max-w-[theme(screens.md)]', 'max-w-[var(--breakpoint-md)]'],

  // Invalid cases
  ['[--foo:theme(colors.red.500/50/50)]', '[--foo:theme(colors.red.500/50/50)]'],
  ['[--foo:theme(colors.red.500/50/50)]/50', '[--foo:theme(colors.red.500/50/50)]/50'],

  // Partially invalid cases
  [
    '[--foo:theme(colors.red.500/50/50)_theme(colors.blue.200)]',
    '[--foo:theme(colors.red.500/50/50)_var(--color-blue-200)]',
  ],
  [
    '[--foo:theme(colors.red.500/50/50)_theme(colors.blue.200)]/50',
    '[--foo:theme(colors.red.500/50/50)_var(--color-blue-200)]/50',
  ],
])('%s => %s', async (candidate, result) => {
  let designSystem = await __unstable__loadDesignSystem('@import "tailwindcss";', {
    base: __dirname,
  })


Review comment (suggestion):
This doesn't seem to actually test the whitespace removal? Maybe something like this:
```suggestion
  ['grid-cols-[min(50%_,_theme(spacing.80))_auto]', 'grid-cols-[min(50%,var(--spacing-80))_auto]'],
```

Code after:
  ['[color:theme(colors.red.500/var(--opacity))]', '[color:var(--color-red-500)]/[var(--opacity)]'],
  ['[color:theme(colors.red.500/.12345)]', '[color:var(--color-red-500)]/[12.345]'],
  ['[color:theme(colors.red.500/50.25%)]', '[color:var(--color-red-500)]/[50.25%]'],

  // Arbitrary value
  ['bg-[theme(colors.red.500/75%)]', 'bg-[var(--color-red-500)]/75'],
  ['bg-[theme(colors.red.500/12.34%)]', 'bg-[var(--color-red-500)]/[12.34%]'],

  // Arbitrary property that already contains a modifier
  ['[color:theme(colors.red.500/50%)]/50', '[color:theme(--color-red-500/50%)]/50'],

  // Arbitrary value, where the candidate already contains a modifier
  // This should still migrate the `theme(…)` syntax to the modern syntax.
  ['bg-[theme(colors.red.500/50%)]/50', 'bg-[theme(--color-red-500/50%)]/50'],

  // Variants, we can't use `var(…)` especially inside of `@media(…)`. We can
  // still upgrade the `theme(…)` to the modern syntax.
  ['max-[theme(spacing.4)]:flex', 'max-[theme(--spacing-4)]:flex'],

  // This test in itself doesn't make much sense. But we need to make sure
  // that this doesn't end up as the modifier in the candidate itself.
  ['max-[theme(spacing.4/50)]:flex', 'max-[theme(--spacing-4/50)]:flex'],

  // `theme(…)` calls in another CSS function is replaced correctly.
  // Additionally we remove unnecessary whitespace.
  ['grid-cols-[min(50%_,_theme(spacing.80))_auto]', 'grid-cols-[min(50%,var(--spacing-80))_auto]'],

  // `theme(…)` calls valid in v3, but not in v4 should still be converted.
  ['[--foo:theme(fontWeight.semibold)]', '[--foo:theme(fontWeight.semibold)]'],

  // `screens` values
  ['max-w-[theme(screens.md)]', 'max-w-[var(--breakpoint-md)]'],

  // Invalid cases
  ['[--foo:theme(colors.red.500/50/50)]', '[--foo:theme(colors.red.500/50/50)]'],
  ['[--foo:theme(colors.red.500/50/50)]/50', '[--foo:theme(colors.red.500/50/50)]/50'],

  // Partially invalid cases
  [
    '[--foo:theme(colors.red.500/50/50)_theme(colors.blue.200)]',
    '[--foo:theme(colors.red.500/50/50)_var(--color-blue-200)]',
  ],
  [
    '[--foo:theme(colors.red.500/50/50)_theme(colors.blue.200)]/50',
    '[--foo:theme(colors.red.500/50/50)_var(--color-blue-200)]/50',
  ],
])('%s => %s', async (candidate, result) => {
  let designSystem = await __unstable__loadDesignSystem('@import "tailwindcss";', {
    base: __dirname,
  })