Tailwind CSS v4 JIT Compilation Breaking Custom CSS Variables

In Tailwind CSS v4, when using JIT (Just-In-Time) compilation, custom CSS variables defined in @layer base are not being properly processed. You might see:

/* This doesn't work in Tailwind v4 JIT mode */
@layer base {
  :root {
    --primary-color: #3b82f6;
    --secondary-color: #64748b;
    --accent-color: #f59e0b;
  }
}

/* These classes won't work */
.bg-primary { background-color: var(--primary-color); }
.text-secondary { color: var(--secondary-color); }
.border-accent { border-color: var(--accent-color); }

What causes this and how can it be fixed?

Solution

The issue is caused by Tailwind CSS v4 changing how it processes CSS variables in JIT mode. The new engine strictly validates CSS variable usage and requires explicit registration of custom properties.

Use CSS custom properties with Tailwind's CSS variable system:

/* tailwind.css */
@import "tailwindcss";

@layer base {
  :root {
    --color-primary: 59 130 246; /* RGB values */
    --color-secondary: 100 116 139;
    --color-accent: 245 158 11;
  }
}

@layer utilities {
  .bg-primary {
    background-color: rgb(var(--color-primary));
  }
  
  .text-secondary {
    color: rgb(var(--color-secondary));
  }
  
  .border-accent {
    border-color: rgb(var(--color-accent));
  }
}

Or define custom colors in Tailwind config:

// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      colors: {
        primary: {
          DEFAULT: 'rgb(var(--color-primary) / <alpha-value>)',
          500: 'rgb(var(--color-primary) / 1)',
        }
      }
    }
  }
}

The key is using the rgb() function with RGB values instead of hex colors in CSS variables.

Alternative #1

If you need to maintain backward compatibility or have complex CSS variable usage, consider using Tailwind's built-in CSS variable system with the --tw- prefix. This approach leverages Tailwind's internal variable processing and ensures compatibility with future versions.

/* tailwind.css */
@import "tailwindcss";

@layer base {
  :root {
    --tw-color-primary: 59 130 246;
    --tw-color-secondary: 100 116 139;
    --tw-color-accent: 245 158 11;
  }
}

/* Use with Tailwind's built-in rgb() function */
.bg-primary { background-color: rgb(var(--tw-color-primary)); }
.text-secondary { color: rgb(var(--tw-color-secondary)); }
.border-accent { border-color: rgb(var(--tw-color-accent)); }

This approach provides better integration with Tailwind's JIT engine and automatic validation and error checking. It's also consistent with Tailwind's internal patterns, making it a future-proof solution.

You can also create utility classes that combine multiple variables:

@layer utilities {
  .btn-primary {
    background-color: rgb(var(--tw-color-primary));
    color: white;
    border: 1px solid rgb(var(--tw-color-primary));
  }
  
  .btn-primary:hover {
    background-color: rgb(var(--tw-color-primary) / 0.9);
  }
}

This makes your components more maintainable and consistent across your application.

Alternative #2

For complex applications or when you continue having issues, consider using CSS Modules as a fallback solution. This approach gives you full control over your CSS variables without depending on Tailwind's processing.

Create a separate CSS file for your custom variables:

/* variables.module.css */
:root {
  --color-primary: #3b82f6;
  --color-secondary: #64748b;
  --color-accent: #f59e0b;
  --spacing-xs: 0.25rem;
  --spacing-sm: 0.5rem;
  --spacing-md: 1rem;
}

.button {
  padding: var(--spacing-md);
  border-radius: 0.375rem;
  font-weight: 500;
  transition: all 0.2s;
}

.primary {
  background-color: var(--color-primary);
  color: white;
}

.primary:hover {
  background-color: var(--color-primary);
  opacity: 0.9;
}

Then import it in your components:

import styles from './variables.module.css';

function Button({ variant = 'primary', children, ...props }) {
  return (
    <button 
      className={`${styles.button} ${styles[variant]}`}
      {...props}
    >
      {children}
    </button>
  );
}

This approach is particularly useful when you have complex CSS variable usage, need backward compatibility, or are working with legacy code. It's also a good choice when your team prefers CSS Modules or when Tailwind integration issues persist.

The migration strategy involves starting with Tailwind's RGB variable approach, then using CSS Modules for complex cases. Gradually migrate simple variables to Tailwind while keeping CSS Modules for edge cases. This hybrid approach gives you the best of both worlds while ensuring your application works reliably.

Last modified: August 19, 2025
Stay in the loop
Subscribe to our newsletter to get the latest articles delivered to your inbox