# Source: https://github.com/tailwindlabs/tailwindcss.com/blob/main/src/docs/upgrade-guide.mdx

export const title = "Upgrade guide";
export const description = "Upgrading your Tailwind CSS projects from v3 to v4.";

Tailwind CSS v4.0 is a new major version of the framework, so while we've worked really hard to minimize breaking changes, some updates are necessary. This guide outlines all the steps required to upgrade your projects from v3 to v4.

**Tailwind CSS v4.0 is designed for Safari 16.4+, Chrome 111+, and Firefox 128+.** If you need to support older browsers, stick with v3.4 until your browser support requirements change.

## Using the upgrade tool

If you'd like to upgrade a project from v3 to v4, you can use our upgrade tool to do the vast majority of the heavy lifting for you:

```sh
  # [!code filename:Terminal]
$ npx @tailwindcss/upgrade
```

For most projects, the upgrade tool will automate the entire migration process including updating your dependencies, migrating your configuration file to CSS, and handling any changes to your template files.

The upgrade tool requires Node.js 20 or higher, so ensure your environment is updated before running it.

**We recommend running the upgrade tool in a new branch**, then carefully reviewing the diff and testing your project in the browser to make sure all of the changes look correct. You may need to tweak a few things by hand in complex projects, but the tool will save you a ton of time either way.

It's also a good idea to go over all of the [breaking changes](#changes-from-v3) in v4 and get a good understanding of what's changed, in case there are other things you need to update in your project that the upgrade tool doesn't catch.

## Upgrading manually

### Using PostCSS

In v3, the `tailwindcss` package was a PostCSS plugin, but in v4 the PostCSS plugin lives in a dedicated `@tailwindcss/postcss` package.

Additionally, in v4 imports and vendor prefixing is now handled for you automatically, so you can remove `postcss-import` and `autoprefixer` if they are in your project:

```js
// [!code filename:postcss.config.mjs]
export default {
  plugins: {
    // [!code --:4]
    "postcss-import": {},
    tailwindcss: {},
    autoprefixer: {},
    // [!code ++:2]
    "@tailwindcss/postcss": {},
  },
};
```

### Using Vite

If you're using Vite, we recommend migrating from the PostCSS plugin to our new dedicated Vite plugin for improved performance and the best developer experience:

```ts
// [!code filename:vite.config.ts]
import { defineConfig } from "vite";
// [!code highlight:2]
import tailwindcss from "@tailwindcss/vite";

export default defineConfig({
  plugins: [
    // [!code highlight:2]
    tailwindcss(),
  ],
});
```

### Using Tailwind CLI

In v4, Tailwind CLI lives in a dedicated `@tailwindcss/cli` package. Update any of your build commands to use the new package instead:

```sh
/* [!code filename:Terminal] */
  # [!code --:2]
npx tailwindcss -i input.css -o output.css
  # [!code ++:2]
npx @tailwindcss/cli -i input.css -o output.css
```

## Changes from v3

Here's a comprehensive list of all the breaking changes in Tailwind CSS v4.0.

Our [upgrade tool](#using-the-upgrade-tool) will handle most of these changes for you automatically, so we highly recommend using it if you can.

### Browser requirements

Tailwind CSS v4.0 is designed for modern browsers and targets Safari 16.4, Chrome 111, and Firefox 128. We depend on modern CSS features like `@property` and `color-mix()` for core framework features, and Tailwind CSS v4.0 will not work in older browsers.

If you need to support older browsers, we recommend sticking with v3.4 for now. We're actively exploring a compatibility mode to help people upgrade sooner that we hope to share more news on in the future.

### Removed @tailwind directives

In v4 you import Tailwind using a regular CSS `@import` statement, not using the `@tailwind` directives you used in v3:

```css
/* [!code filename:CSS] */
/* [!code --:4] */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* [!code ++:2] */
@import "tailwindcss";
```

### Removed deprecated utilities

We've removed any utilities that were deprecated in v3 and have been undocumented for several years. Here's a list of what's been removed along with the modern alternative:

{

<table>
  <thead>
    <tr>
      <th>Deprecated</th>
      <th>Replacement</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>
        <code className="whitespace-nowrap">bg-opacity-*</code>
      </td>
      <td>
        Use opacity modifiers like <code className="whitespace-nowrap">bg-black/50</code>
      </td>
    </tr>
    <tr>
      <td>
        <code className="whitespace-nowrap">text-opacity-*</code>
      </td>
      <td>
        Use opacity modifiers like <code className="whitespace-nowrap">text-black/50</code>
      </td>
    </tr>
    <tr>
      <td>
        <code className="whitespace-nowrap">border-opacity-*</code>
      </td>
      <td>
        Use opacity modifiers like <code className="whitespace-nowrap">border-black/50</code>
      </td>
    </tr>
    <tr>
      <td>
        <code className="whitespace-nowrap">divide-opacity-*</code>
      </td>
      <td>
        Use opacity modifiers like <code className="whitespace-nowrap">divide-black/50</code>
      </td>
    </tr>
    <tr>
      <td>
        <code className="whitespace-nowrap">ring-opacity-*</code>
      </td>
      <td>
        Use opacity modifiers like <code className="whitespace-nowrap">ring-black/50</code>
      </td>
    </tr>
    <tr>
      <td>
        <code className="whitespace-nowrap">placeholder-opacity-*</code>
      </td>
      <td>
        Use opacity modifiers like <code className="whitespace-nowrap">placeholder-black/50</code>
      </td>
    </tr>
    <tr>
      <td>
        <code className="whitespace-nowrap">flex-shrink-*</code>
      </td>
      <td>
        <code className="whitespace-nowrap">shrink-*</code>
      </td>
    </tr>
    <tr>
      <td>
        <code className="whitespace-nowrap">flex-grow-*</code>
      </td>
      <td>
        <code className="whitespace-nowrap">grow-*</code>
      </td>
    </tr>
    <tr>
      <td>
        <code className="whitespace-nowrap">overflow-ellipsis</code>
      </td>
      <td>
        <code className="whitespace-nowrap">text-ellipsis</code>
      </td>
    </tr>
    <tr>
      <td>
        <code className="whitespace-nowrap">decoration-slice</code>
      </td>
      <td>
        <code className="whitespace-nowrap">box-decoration-slice</code>
      </td>
    </tr>
    <tr>
      <td>
        <code className="whitespace-nowrap">decoration-clone</code>
      </td>
      <td>
        <code className="whitespace-nowrap">box-decoration-clone</code>
      </td>
    </tr>
  </tbody>
</table>

}

### Renamed utilities

We've renamed the following utilities in v4 to make them more consistent and predictable:

{

<table>
  <thead>
    <tr>
      <th>v3</th>
      <th>v4</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>
        <code className="whitespace-nowrap">shadow-sm</code>
      </td>
      <td>
        <code className="whitespace-nowrap">shadow-xs</code>
      </td>
    </tr>
    <tr>
      <td>
        <code className="whitespace-nowrap">shadow</code>
      </td>
      <td>
        <code className="whitespace-nowrap">shadow-sm</code>
      </td>
    </tr>
    <tr>
      <td>
        <code className="whitespace-nowrap">drop-shadow-sm</code>
      </td>
      <td>
        <code className="whitespace-nowrap">drop-shadow-xs</code>
      </td>
    </tr>
    <tr>
      <td>
        <code className="whitespace-nowrap">drop-shadow</code>
      </td>
      <td>
        <code className="whitespace-nowrap">drop-shadow-sm</code>
      </td>
    </tr>
    <tr>
      <td>
        <code className="whitespace-nowrap">blur-sm</code>
      </td>
      <td>
        <code className="whitespace-nowrap">blur-xs</code>
      </td>
    </tr>
    <tr>
      <td>
        <code className="whitespace-nowrap">blur</code>
      </td>
      <td>
        <code className="whitespace-nowrap">blur-sm</code>
      </td>
    </tr>
    <tr>
      <td>
        <code className="whitespace-nowrap">backdrop-blur-sm</code>
      </td>
      <td>
        <code className="whitespace-nowrap">backdrop-blur-xs</code>
      </td>
    </tr>
    <tr>
      <td>
        <code className="whitespace-nowrap">backdrop-blur</code>
      </td>
      <td>
        <code className="whitespace-nowrap">backdrop-blur-sm</code>
      </td>
    </tr>
    <tr>
      <td>
        <code className="whitespace-nowrap">rounded-sm</code>
      </td>
      <td>
        <code className="whitespace-nowrap">rounded-xs</code>
      </td>
    </tr>
    <tr>
      <td>
        <code className="whitespace-nowrap">rounded</code>
      </td>
      <td>
        <code className="whitespace-nowrap">rounded-sm</code>
      </td>
    </tr>
    <tr>
      <td>
        <code className="whitespace-nowrap">outline-none</code>
      </td>
      <td>
        <code className="whitespace-nowrap">outline-hidden</code>
      </td>
    </tr>
    <tr>
      <td>
        <code className="whitespace-nowrap">ring</code>
      </td>
      <td>
        <code className="whitespace-nowrap">ring-3</code>
      </td>
    </tr>
  </tbody>
</table>

}

#### Updated shadow, radius, and blur scales

We've renamed the default shadow, radius and blur scales to make sure every utility has a named value. The "bare" versions still work for backward compatibility, but the <code><em>{'<utility>'}</em>-sm</code> utilities will look different unless updated to their respective <code><em>{'<utility>'}</em>-xs</code> versions.

To update your project for these changes, replace all the v3 utilities with their v4 versions:

```html
<!-- [!code filename:HTML] -->
<!-- [!code --:2] -->
<input class="shadow-sm" />
<!-- [!code ++:2] -->
<input class="shadow-xs" />

<!-- [!code --:2] -->
<input class="shadow" />
<!-- [!code ++:2] -->
<input class="shadow-sm" />
```

#### Renamed outline utility

The `outline` utility now sets `outline-width: 1px` by default to be more consistent with border and ring utilities. Furthermore all `outline-<number>` utilities default `outline-style` to `solid`, omitting the need to combine them with `outline`:

```html
<!-- [!code filename:HTML] -->
<!-- [!code --:2] -->
<input class="outline outline-2" />
<!-- [!code ++:2] -->
<input class="outline-2" />
```

The `outline-none` utility previously didn't actually set `outline-style: none`, and instead set an invisible outline that would still show up in forced colors mode for accessibility reasons.

To make this more clear we've renamed this utility to `outline-hidden` and added a new `outline-none` utility that actually sets `outline-style: none`.

To update your project for this change, replace any usage of `outline-none` with `outline-hidden`:

```html
<!-- [!code filename:HTML] -->
<!-- [!code --:2] -->
<input class="focus:outline-none" />
<!-- [!code ++:2] -->
<input class="focus:outline-hidden" />
```

#### Default ring width change

In v3, the `ring` utility added a `3px` ring. We've changed this in v4 to be `1px` to make it consistent with borders and outlines.

To update your project for this change, replace any usage of `ring` with `ring-3`:

```html
<!-- [!code filename:HTML] -->
<!-- [!code --:2] -->
<input class="ring ring-blue-500" />
<!-- [!code ++:2] -->
<input class="ring-3 ring-blue-500" />
```

### Space-between selector

We've changed the selector used by the [`space-x-*` and `space-y-*` utilities](/docs/margin#adding-space-between-children) to address serious performance issues on large pages:

```css
/* [!code filename:CSS] */
/* Before */
.space-y-4 > :not([hidden]) ~ :not([hidden]) {
  margin-top: 1rem;
}

/* Now */
.space-y-4 > :not(:last-child) {
  margin-bottom: 1rem;
}
```

You might see changes in your project if you were ever using these utilities with inline elements, or if you were adding other margins to child elements to tweak their spacing.

If this change causes any issues in your project, we recommend migrating to a flex or grid layout and using `gap` instead:

{/* prettier-ignore */}
```html
<!-- [!code filename:HTML] -->
<div class="space-y-4 p-4"> <!-- [!code --] -->
<div class="flex flex-col gap-4 p-4"> <!-- [!code ++] -->
  <label for="name">Name</label>
  <input type="text" name="name" />
</div>
```

### Divide selector

We've changed the selector used by the [`divide-x-*` and `divide-y-*` utilities](/docs/border-width#between-children) to address serious performance issues on large pages:

```css
/* [!code filename:CSS] */
/* Before */
.divide-y-4 > :not([hidden]) ~ :not([hidden]) {
  border-top-width: 4px;
}

/* Now */
.divide-y-4 > :not(:last-child) {
  border-bottom-width: 4px;
}
```

You might see changes in your project if you were ever using these utilities with inline elements, if you were adding other margins/padding to child elements to tweak their spacing, or adjusting the borders of specific child elements.

### Using variants with gradients

In v3, overriding part of a gradient with a variant would "reset" the entire gradient, so in this example the `to-*` color would be transparent in dark mode instead of yellow:

```html
<!-- [!code filename:HTML] -->
<!-- [!code classes:dark:from-blue-500] -->
<div class="bg-gradient-to-r from-red-500 to-yellow-400 dark:from-blue-500">
  <!-- ... -->
</div>
```

In v4, these values are preserved which is more consistent with how other utilities in Tailwind work.

This means you may need to explicitly use `via-none` if you want to "unset" a three-stop gradient back to a two-stop gradient in a specific state:

```html
<!-- [!code filename:HTML] -->
<!-- [!code classes:dark:via-none] -->
<div class="bg-linear-to-r from-red-500 via-orange-400 to-yellow-400 dark:via-none dark:from-blue-500 dark:to-teal-400">
  <!-- ... -->
</div>
```

### Container configuration

In v3, the `container` utility had several configuration options like `center` and `padding` that no longer exist in v4.

To customize the `container` utility in v4, extend it using the `@utility` directive:

```css
/* [!code filename:CSS] */
@utility container {
  margin-inline: auto;
  padding-inline: 2rem;
}
```

### Default border color

In v3, the `border-*` and `divide-*` utilities used your configured `gray-200` color by default. We've changed this to `currentColor` in v4 to make Tailwind less opinionated and match browser defaults.

To update your project for this change, make sure you specify a color anywhere you're using a `border-*` or `divide-*` utility:

```html
<!-- [!code classes:border-gray-200] -->
<div class="border border-gray-200 px-2 py-3 ...">
  <!-- ... -->
</div>
```

Alternatively, add these base styles to your project to preserve the v3 behavior:

```css
/* [!code filename:CSS] */
@layer base {
  *,
  ::after,
  ::before,
  ::backdrop,
  ::file-selector-button {
    border-color: var(--color-gray-200, currentColor);
  }
}
```

### Default ring width and color

We've changed the width of the `ring` utility from 3px to 1px and changed the default color from `blue-500` to `currentColor` to make things more consistent the `border-*`, `divide-*`, and `outline-*` utilities.

To update your project for these changes, replace any use of `ring` with `ring-3`:

```html
<!-- prettier-ignore -->
<button class="focus:ring ..."> <!-- [!code --] -->
<button class="focus:ring-3 ..."> <!-- [!code ++] -->
  <!-- ... -->
</button>
```

Then make sure to add `ring-blue-500` anywhere you were depending on the default ring color:

```html
<!-- [!code classes:focus:ring-blue-500] -->
<button class="focus:ring-3 focus:ring-blue-500 ...">
  <!-- ... -->
</button>
```

Alternatively, add these theme variables to your CSS to preserve the v3 behavior:

```css
/* [!code filename:CSS] */
@theme {
  --default-ring-width: 3px;
  --default-ring-color: var(--color-blue-500);
}
```

Note though that these variables are only supported for compatibility reasons, and are not considered idiomatic usage of Tailwind CSS v4.0.

### Preflight changes

We've made a couple small changes to the base styles in Preflight in v4:

#### New default placeholder color

In v3, placeholder text used your configured `gray-400` color by default. We've simplified this in v4 to just use the current text color at 50% opacity.

You probably won't even notice this change (it might even make your project look better), but if you want to preserve the v3 behavior, add this CSS to your project:

```css
/* [!code filename:CSS] */
@layer base {
  input::placeholder,
  textarea::placeholder {
    color: var(--color-gray-400);
  }
}
```

#### Buttons use the default cursor

Buttons now use `cursor: default` instead of `cursor: pointer` to match the default browser behavior.

If you'd like to continue using `cursor: pointer` by default, add these base styles to your CSS:

```css
/* [!code filename:CSS] */
@layer base {
  button:not(:disabled),
  [role="button"]:not(:disabled) {
    cursor: pointer;
  }
}
```

#### Dialog margins removed

Preflight now resets margins on `<dialog>` elements to be consistent with how other elements are reset.

If you still want dialogs to be centered by default, add this CSS to your project:

```css
/* [!code filename:CSS] */
@layer base {
  dialog {
    margin: auto;
  }
}
```

#### Hidden attribute takes priority

Display classes like `block` or `flex` no longer take priority over the `hidden` attribute on an element. Remove the `hidden` attribute if you want an element to be visible to the user. Note that this does not apply to `hidden="until-found"`.

### Using a prefix

Prefixes now look like variants and are always at the beginning of the class name:

```html
<!-- [!code classes:tw:bg-red-500,tw:flex,tw:hover:bg-red-600] -->
<div class="tw:flex tw:bg-red-500 tw:hover:bg-red-600">
  <!-- ... -->
</div>
```

When using a prefix, you should still configure your theme variables as if you aren't using a prefix:

```css {{ filename: "app.css" }}
@import "tailwindcss" prefix(tw);

@theme {
  --font-display: "Satoshi", "sans-serif";

  --breakpoint-3xl: 120rem;

  --color-avocado-100: oklch(0.99 0 0);
  --color-avocado-200: oklch(0.98 0.04 113.22);
  --color-avocado-300: oklch(0.94 0.11 115.03);

  /* ... */
}
```

The generated CSS variables _will_ include a prefix to avoid conflicts with any existing variables in your project:

```css {{ filename: "dist.css" }}
:root {
  --tw-font-display: "Satoshi", "sans-serif";

  --tw-breakpoint-3xl: 120rem;

  --tw-color-avocado-100: oklch(0.99 0 0);
  --tw-color-avocado-200: oklch(0.98 0.04 113.22);
  --tw-color-avocado-300: oklch(0.94 0.11 115.03);

  /* ... */
}
```

### The important modifier

In v3 you could mark a utility as important by placing an `!` at the beginning of the utility name (but after any variants). In v4 you should place the `!` at the very end of the class name instead:

```html
<!-- [!code classes:bg-red-500!,flex!,hover:bg-red-600/50!] -->
<div class="flex! bg-red-500! hover:bg-red-600/50!">
  <!-- ... -->
</div>
```

The old way is still supported for compatibility but is deprecated.

### Adding custom utilities

In v3, any custom classes you defined within `@layer utilities` or `@layer components` would get picked up by Tailwind as a true utility class and would automatically work with variants like `hover`, `focus`, or `lg` with the difference being that `@layer components` would always come first in the generated stylesheet.

In v4 we are using native cascade layers and no longer hijacking the `@layer` at-rule, so we've introduced the `@utility` API as a replacement:

```css
/* [!code filename:CSS] */
/* [!code --:6] */
@layer utilities {
  .tab-4 {
    tab-size: 4;
  }
}
/* [!code ++:4] */
@utility tab-4 {
  tab-size: 4;
}
```

Custom utilities are now also sorted based on the amount of properties they define. This means that component utilities like this `.btn` can be overwritten by other Tailwind utilities without additional configuration:

```css
/* [!code filename:CSS] */
/* [!code --:8] */
@layer components {
  .btn {
    border-radius: 0.5rem;
    padding: 0.5rem 1rem;
    background-color: ButtonFace;
  }
}
/* [!code ++:6] */
@utility btn {
  border-radius: 0.5rem;
  padding: 0.5rem 1rem;
  background-color: ButtonFace;
}
```

Learn more about registering custom utilities in the [adding custom utilities documentation](/docs/adding-custom-styles#adding-custom-utilities).

### Variant stacking order

In v3, stacked variants were applied from right to left, but in v4 we've updated them to apply left to right to look more like CSS syntax.

To update your project for this change, reverse the order of any order-sensitive stacked variants in your project:

```html
<!-- [!code filename:HTML] -->
<!-- [!code --:2] -->
<!-- prettier-ignore -->
<ul class="py-4 first:*:pt-0 last:*:pb-0">
<!-- [!code ++:2] -->
<ul class="py-4 *:first:pt-0 *:last:pb-0">
  <li>One</li>
  <li>Two</li>
  <li>Three</li>
</ul>
```

You likely have very few of these if any—the direct child variant (`*`) and any typography plugin variants (`prose-headings`) are the most likely ones you might be using, and even then it's only if you've stacked them with other variants.

### Variables in arbitrary values

In v3 you were able to use CSS variables as arbitrary values without `var()`, but recent updates to CSS mean that this can often be ambiguous, so we've changed the syntax for this in v4 to use parentheses instead of square brackets.

To update your project for this change, replace usage of the old variable shorthand syntax with the new variable shorthand syntax:

```html
<!-- [!code filename:HTML] -->
<!-- [!code --:2] -->
<div class="bg-[--brand-color]"></div>
<!-- [!code ++:2] -->
<div class="bg-(--brand-color)"></div>
```

### Arbitrary values in grid and object-position utilities

Commas were previously replaced with spaces in the `grid-cols-*`, `grid-rows-*`, and `object-*` utilities inside arbitrary values. This special behavior existed in Tailwind CSS v3 for compatibility with v2. This compatibility no longer exists in v4.0 and underscores must be used to represent spaces.

To update your project for this change, replace usage of commas that were intended to be spaces with underscores:

```html
<!-- [!code filename:HTML] -->
<!-- [!code --:2] -->
<div class="grid-cols-[max-content,auto]"></div>
<!-- [!code ++:2] -->
<div class="grid-cols-[max-content_auto]"></div>
```

### Hover styles on mobile

In v4 we've updated the `hover` variant to only apply when the primary input device supports hover:

```css
/* [!code filename:CSS] */
@media (hover: hover) {
  .hover\:underline:hover {
    text-decoration: underline;
  }
}
```

This can create problems if you've built your site in a way that depends on touch devices triggering hover on tap. If this is an issue for you, you can override the `hover` variant with your own variant that uses the old implementation:

```css
/* [!code filename:CSS] */
@custom-variant hover (&:hover);
```

Generally though we recommend treating hover functionality as an enhancement, and not depending on it for your site to work since touch devices don't truly have the ability to hover.

### Transitioning outline-color

The `transition` and `transition-colors` utilities now include the `outline-color` property.

This means if you were adding an outline with a custom color on focus, you will see the color transition from the default color. To avoid this, make sure you set the outline color unconditionally, or explicitly set it for both states:

```html
<!-- [!code filename:HTML] -->
<!-- [!code --:2] -->
<button class="transition hover:outline-2 hover:outline-cyan-500"></button>
<!-- [!code ++:2] -->
<button class="outline-cyan-500 transition hover:outline-2"></button>
```

### Individual transform properties

The `rotate-*`, `scale-*`, and `translate-*` utilities are now based on the individual `rotate`, `scale`, and `translate` properties in CSS. Normally this shouldn't affect the behavior but there's a couple of cases to look out for:

#### Resetting Transforms

You previously would've been able to "reset" your rotate, scale, and translate utilities via `transform-none`. This no longer works and you will need to reset the individual properties instead:

```html
<!-- [!code filename:HTML] -->
<!-- [!code --:2] -->
<button class="scale-150 focus:transform-none"></button>
<!-- [!code ++:2] -->
<button class="scale-150 focus:scale-none"></button>
```

#### Transitions

If you customize the list of transitioned properties and include `transform` (e.g. by writing `transition-[opacity,transform]`) then these utilities will no longer transition. To fix this, include the individual properties in the list. For example, if you want to transition changes when using `opacity-*` and `scale-*` utilities you should use `transition-[opacity,scale]` instead.

```html
<!-- [!code filename:HTML] -->
<!-- [!code --:2] -->
<button class="hover:scale-150 transition-[opacity,transform]"></button>
<!-- [!code ++:2] -->
<button class="hover:scale-150 transition-[opacity,scale]"></button>
```

### Disabling core plugins

In v3 there was a `corePlugins` option you could use to completely disable certain utilities in the framework. This is no longer supported in v4.

### Using the theme() function

Since v4 includes CSS variables for all of your theme values, we recommend using those variables instead of the `theme()` function whenever possible:

```css
/* [!code filename:CSS] */
.my-class {
  /* [!code --:2] */
  background-color: theme(colors.red.500);
  /* [!code ++:2] */
  background-color: var(--color-red-500);
}
```

For cases where you still need to use the `theme()` function (like in media queries where CSS variables aren't supported), you should use the CSS variable name instead of the old dot notation:

```css
/* [!code filename:CSS] */
@media (width >= theme(screens.xl)) { /* [!code --] */
@media (width >= theme(--breakpoint-xl)) { /* [!code ++] */
  /* ... */
}
```

### Using a JavaScript config file

JavaScript config files are still supported for backward compatibility, but they are no longer detected automatically in v4.

If you still need to use a JavaScript config file, you can load it explicitly using the `@config` directive:

```css
/* [!code filename:CSS] */
@config "../../tailwind.config.js";
```

The `corePlugins`, `safelist`, and `separator` options from the JavaScript-based config are not supported in v4.0. To safelist utilities in v4 use [`@source inline()`](/docs/detecting-classes-in-source-files#safelisting-specific-utilities).

### Theme values in JavaScript

In v3 we exported a `resolveConfig` function that you could use to turn your JavaScript-based config into a flat object that you could use in your other JavaScript.

We've removed this in v4 in hopes that people can use the CSS variables we generate directly instead, which is much simpler and will significantly reduce your bundle size.

For example, the popular [Motion](https://motion.dev/docs/react-quick-start) library for React lets you animate to and from CSS variable values:

```jsx
// [!code filename:JSX]
// [!code word:var(--color-blue-500)]
<motion.div animate={{ backgroundColor: "var(--color-blue-500)" }} />
```

If you need access to a resolved CSS variable value in JS, you can use `getComputedStyle` to get the value of a theme variable on the document root:

```js
// [!code filename:spaghetti.js]
let styles = getComputedStyle(document.documentElement);
let shadow = styles.getPropertyValue("--shadow-xl");
```

### Using @apply with Vue, Svelte, or CSS modules

In v4, stylesheets that are bundled separately from your main CSS file (e.g. CSS modules files, `<style>` blocks in Vue, Svelte, or Astro, etc.) do not have access to theme variables, custom utilities, and custom variants defined in other files.

To make these definitions available in these contexts, use [`@reference`](/docs/functions-and-directives#reference-directive) to import them without duplicating any CSS in your bundle:

```html
<!-- [!code filename:Vue] -->
<template>
  <h1>Hello world!</h1>
</template>

<style>
  /* [!code highlight:2] */
  @reference "../../app.css";

  h1 {
    @apply text-2xl font-bold text-red-500;
  }
</style>
```

Alternatively, you can use your CSS theme variables directly instead of using `@apply` at all, which will also improve performance since Tailwind won't need to process these styles:

```html
<!-- [!code filename:Vue] -->
<template>
  <h1>Hello world!</h1>
</template>

<style>
  h1 {
    /* [!code highlight:2] */
    color: var(--text-red-500);
  }
</style>
```

You can find more documentation on [using Tailwind with CSS modules](/docs/compatibility#css-modules).

### Using Sass, Less, and Stylus

Tailwind CSS v4.0 is not designed to be used with CSS preprocessors like Sass, Less, or Stylus. Think of Tailwind CSS itself as your preprocessor — you shouldn't use Tailwind with Sass for the same reason you wouldn't use Sass with Stylus. Because of this it is not possible to use Sass, Less, or Stylus for your stylesheets or `<style>` blocks in Vue, Svelte, Astro, etc.

Learn more in the [compatibility documentation](/docs/compatibility#sass-less-and-stylus).