65 lines
2.2 KiB
Markdown
65 lines
2.2 KiB
Markdown
---
|
|
title: "Migrating to Tailwind CSS v4"
|
|
date: 2026-05-13T08:00:00+08:00
|
|
draft: false
|
|
description: "CSS-first configuration, the new @theme directive, container queries, and what actually changed from v3. A practical migration log."
|
|
tags:
|
|
- CSS
|
|
- Tailwind
|
|
- Frontend
|
|
---
|
|
|
|
Tailwind v4 drops the JavaScript config file in favor of CSS-based configuration. The migration is mostly mechanical but there are gotchas with the `@apply` directive and custom variants.
|
|
|
|
## CSS-First Configuration
|
|
|
|
The biggest change: `tailwind.config.js` is replaced by CSS. Your design tokens live in the `@theme` directive:
|
|
|
|
```css
|
|
@import "tailwindcss";
|
|
|
|
@theme {
|
|
--color-primary: #2563eb;
|
|
--color-primary-dark: #1d4ed8;
|
|
--font-sans: "Inter", sans-serif;
|
|
--spacing-container: 1280px;
|
|
}
|
|
```
|
|
|
|
VSCode IntelliSense still works — the Tailwind CSS extension reads theme values from your stylesheet.
|
|
|
|
## The Migration Path
|
|
|
|
1. Install v4: `npm install tailwindcss@next`
|
|
2. Run the automatic migration tool: `npx @tailwindcss/upgrade`
|
|
3. Manually check `@apply` usages — the tool handles most cases, but deeply nested `@apply` with custom utilities sometimes breaks.
|
|
4. Replace `theme.extend` in your old config with `@theme` blocks in CSS.
|
|
|
|
## New Features Worth Using
|
|
|
|
### Container Queries
|
|
|
|
No more media queries for component-level responsiveness. Tailwind v4 ships container query utilities:
|
|
|
|
```html
|
|
<div class="@container">
|
|
<div class="grid @lg:grid-cols-2">...</div>
|
|
</div>
|
|
```
|
|
|
|
### Dynamic Utility Values
|
|
|
|
Arbitrary values are simpler: `w-(--sidebar-width)` references a CSS custom property. No more bracket syntax for common cases.
|
|
|
|
### Native Cascade Layers
|
|
|
|
Tailwind v4 uses `@layer` to control specificity. Base, components, and utilities are layered in the correct order — `@apply` in component layer no longer risks specificity wars.
|
|
|
|
## Gotchas
|
|
|
|
- Custom variants defined in old JS config need to be re-registered via `@variant` in CSS.
|
|
- The `important` config option now needs an explicit CSS strategy.
|
|
- Some plugins haven't updated yet — check compatibility before migrating production projects.
|
|
|
|
Overall, the migration took about two hours for a mid-sized project. The CSS-first approach feels more natural, and the build is noticeably faster thanks to the new Oxide engine.
|