Tailwind CSS v4: Co se skutečně změnilo a jestli migrovat
CSS-first konfigurace, integrace @layer, vestavěné container queries, výkon nového enginu, breaking changes a moje upřímná zkušenost s migrací z v3 na v4.
Tailwind CSS používám od v1.x, kdy polovina komunity ho považovala za ohavnost a druhá polovina s ním nedokázala přestat dodávat. Každá velká verze byla významný skok, ale v4 je jiná. Není to jen feature release. Je to architektonické přepsání od základů, které mění fundamentální smlouvu mezi vámi a frameworkem.
Po migraci dvou produkčních projektů z v3 na v4 a spuštění tří nových projektů na v4 od nuly mám jasný obrázek toho, co je skutečně lepší, co je hrubé a jestli byste měli migrovat dnes. Žádný hype, žádné pobouření — jen to, co jsem pozoroval.
Celkový obraz: Co v4 skutečně je#
Tailwind CSS v4 je tři věci najednou:
- Nový engine — přepsaný z JavaScriptu do Rustu (Oxide engine), díky čemuž jsou buildy dramaticky rychlejší
- Nové konfigurační paradigma — CSS-first konfigurace nahrazuje
tailwind.config.jsjako výchozí - Těsnější integrace s CSS platformou — nativní
@layer, container queries,@starting-stylea cascade layers jsou prvotřídní občané
Titulek, který uvidíte všude, je „10x rychlejší." To je reálné, ale podceňuje to skutečnou změnu. Mentální model pro konfiguraci a rozšiřování Tailwindu se fundamentálně posunul. Teď pracujete s CSS, ne s JavaScriptovým konfiguračním objektem, který generuje CSS.
Takto vypadá minimální Tailwind v4 setup:
/* app.css — toto je celý setup */
@import "tailwindcss";To je vše. Žádný konfigurační soubor. Žádná konfigurace PostCSS pluginu (pro většinu setupů). Žádné direktivy @tailwind base; @tailwind components; @tailwind utilities;. Jeden import a jedete.
Srovnejte to s v3:
/* v3 — app.css */
@tailwind base;
@tailwind components;
@tailwind utilities;// v3 — tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {},
},
plugins: [],
};// v3 — postcss.config.js
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};Tři soubory zredukované na jeden řádek. To není jen méně boilerplate — je to menší plocha pro chybnou konfiguraci. Ve v4 je detekce obsahu automatická. Skenuje vaše projektové soubory bez potřeby vypisovat glob patterny.
CSS-First konfigurace s @theme#
Toto je největší konceptuální posun. Ve v3 jste Tailwind přizpůsobovali přes JavaScriptový konfigurační objekt:
// v3 — tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
brand: {
50: "#eff6ff",
500: "#3b82f6",
900: "#1e3a5f",
},
},
fontFamily: {
display: ["Inter Variable", "sans-serif"],
},
spacing: {
18: "4.5rem",
112: "28rem",
},
borderRadius: {
"4xl": "2rem",
},
},
},
};Ve v4 toto vše žije v CSS pomocí direktivy @theme:
@import "tailwindcss";
@theme {
--color-brand-50: #eff6ff;
--color-brand-500: #3b82f6;
--color-brand-900: #1e3a5f;
--font-display: "Inter Variable", sans-serif;
--spacing-18: 4.5rem;
--spacing-112: 28rem;
--radius-4xl: 2rem;
}Zpočátku jsem se tomu bránil. Líbilo se mi mít jediný JavaScriptový objekt, kde jsem viděl celý svůj design systém. Ale po týdnu s CSS přístupem jsem změnil názor ze tří důvodů:
1. Nativní CSS custom properties se automaticky vystavují. Každá hodnota, kterou definujete v @theme, se stává CSS custom property na :root. To znamená, že vaše theme hodnoty jsou přístupné v čistém CSS, v CSS Modules, ve <style> tazích, kdekoli CSS běží:
/* tohle dostanete zadarmo */
:root {
--color-brand-50: #eff6ff;
--color-brand-500: #3b82f6;
--color-brand-900: #1e3a5f;
}/* použijte je kdekoli — bez Tailwindu */
.custom-element {
border: 2px solid var(--color-brand-500);
}2. Můžete používat CSS features uvnitř @theme. Media queries, light-dark(), calc() — reálné CSS zde funguje, protože to je reálné CSS:
@theme {
--color-surface: light-dark(#ffffff, #0a0a0a);
--color-text: light-dark(#0a0a0a, #fafafa);
--spacing-container: calc(100vw - 2rem);
}3. Společné umístění s vaším dalším CSS. Vaše téma, vaše vlastní utility a vaše základní styly žijí ve stejném jazyce, ve stejném souboru, pokud chcete. Žádné přepínání kontextu mezi „CSS světem" a „JavaScript config světem."
Přepisování vs. rozšiřování výchozího tématu#
Ve v3 jste měli theme (nahradit) vs theme.extend (sloučit). Ve v4 je mentální model odlišný:
@import "tailwindcss";
/* Toto ROZŠIŘUJE výchozí téma — přidává brand barvy vedle existujících */
@theme {
--color-brand-500: #3b82f6;
}Pokud chcete kompletně nahradit namespace (jako odstranění všech výchozích barev), použijete @theme s wildcard resetem --color-*:
@import "tailwindcss";
@theme {
/* Nejdříve vymažeme všechny výchozí barvy */
--color-*: initial;
/* Teď definujeme jen naše barvy */
--color-white: #ffffff;
--color-black: #000000;
--color-brand-50: #eff6ff;
--color-brand-500: #3b82f6;
--color-brand-900: #1e3a5f;
}Tento pattern wildcard resetu je elegantní. Vyberete si přesně, které části výchozího tématu zachovat a které nahradit. Chcete všechny výchozí spacingy, ale vlastní barvy? Resetujte --color-*: initial; a spacing nechte na pokoji.
Více souborů s tématem#
Pro větší projekty můžete téma rozdělit do souborů:
/* styles/theme/colors.css */
@theme {
--color-brand-50: #eff6ff;
--color-brand-100: #dbeafe;
--color-brand-200: #bfdbfe;
--color-brand-300: #93c5fd;
--color-brand-400: #60a5fa;
--color-brand-500: #3b82f6;
--color-brand-600: #2563eb;
--color-brand-700: #1d4ed8;
--color-brand-800: #1e40af;
--color-brand-900: #1e3a5f;
--color-brand-950: #172554;
}
/* styles/theme/typography.css */
@theme {
--font-display: "Inter Variable", sans-serif;
--font-body: "Source Sans 3 Variable", sans-serif;
--font-mono: "JetBrains Mono Variable", monospace;
--text-display: 3.5rem;
--text-display--line-height: 1.1;
--text-display--letter-spacing: -0.02em;
}/* app.css */
@import "tailwindcss";
@import "./theme/colors.css";
@import "./theme/typography.css";Toto je mnohem čistší než v3 pattern s obrovským tailwind.config.js nebo pokusy o rozdělení pomocí require().
Oxide Engine: Skutečně 10x rychlejší#
Engine Tailwindu v4 je kompletní přepis v Rustu. Říkají mu Oxide. Byl jsem skeptický k tvrzení „10x rychlejší" — marketingová čísla zřídka přežijí kontakt s reálnými projekty. Tak jsem to změřil.
Můj testovací projekt: Next.js aplikace s 847 komponentami, 142 stránkami, přibližně 23 000 použitími Tailwind tříd.
| Metrika | v3 (Node) | v4 (Oxide) | Zlepšení |
|---|---|---|---|
| Úvodní build | 4 280ms | 387ms | 11x |
| Inkrementální (editace 1 souboru) | 340ms | 18ms | 19x |
| Plný rebuild (clean) | 5 100ms | 510ms | 10x |
| Start dev serveru | 3 200ms | 290ms | 11x |
Tvrzení „10x" je pro můj projekt konzervativní. Inkrementální buildy jsou tam, kde to skutečně září — 18ms znamená, že je to v podstatě okamžité. Uložíte soubor a prohlížeč má nové styly dříve, než přepnete záložku.
Proč je to tak rychlé?#
Tři důvody:
1. Rust místo JavaScriptu. Jádrový CSS parser, detekce tříd a generování kódu jsou vše nativní Rust. Toto není situace „přepišme v Rustu pro zábavu" — parsování CSS je skutečně CPU-bound práce, kde nativní kód má masivní výhodu nad V8.
2. Žádné PostCSS v horké cestě. Ve v3 byl Tailwind PostCSS plugin. Každý build znamenal: parsovat CSS do PostCSS AST, spustit Tailwind plugin, serializovat zpět do CSS řetězce, pak běží další PostCSS pluginy. Ve v4 má Tailwind vlastní CSS parser, který jde přímo ze zdroje na výstup. PostCSS je stále podporováno pro kompatibilitu, ale primární cesta ho zcela přeskakuje.
3. Chytřejší inkrementální zpracování. Nový engine agresivně cachuje. Když editujete jeden soubor, znovu skenuje jen ten soubor na názvy tříd a regeneruje jen CSS pravidla, která se změnila. V3 engine byl v tomhle chytřejší, než mu lidé připisují (JIT mód byl již inkrementální), ale v4 to posouvá mnohem dále s jemným sledováním závislostí.
Záleží na rychlosti?#
Ano, ale ne z důvodu, který byste čekali. Pro většinu projektů byla rychlost buildu v3 „v pořádku." Čekali jste pár set milisekund v dev režimu. Ne bolestivé.
Rychlost v4 záleží, protože dělá Tailwind neviditelným ve vašem toolchainu. Když jsou buildy pod 20ms, přestanete na Tailwind myslet jako na build krok vůbec. Stane se z něj něco jako zvýrazňování syntaxe — vždy tam, nikdy v cestě. Ten psychologický rozdíl je významný přes celý den vývoje.
Nativní integrace @layer#
Ve v3 Tailwind používal vlastní systém vrstev s @layer base, @layer components a @layer utilities. Vypadaly jako CSS cascade layers, ale nebyly — byly to Tailwind-specifické direktivy, které řídily, kde se generované CSS objeví ve výstupu.
Ve v4 Tailwind používá skutečné CSS cascade layers:
/* v4 výstup — zjednodušený */
@layer theme, base, components, utilities;
@layer base {
/* reset, preflight */
}
@layer components {
/* vaše component třídy */
}
@layer utilities {
/* všechny generované utility třídy */
}Toto je významná změna, protože CSS cascade layers mají reálné důsledky pro specificitu. Pravidlo v vrstvě s nižší prioritou vždy prohraje s pravidlem ve vrstvě s vyšší prioritou, bez ohledu na specificitu selektoru. To znamená:
@layer components {
/* specificita: 0-1-0 */
.card { padding: 1rem; }
}
@layer utilities {
/* specificita: 0-1-0 — stejná specificita ale vyhrává, protože utilities vrstva je pozdější */
.p-4 { padding: 1rem; }
}Utility vždy přepisují components. Components vždy přepisují base. Takto Tailwind fungoval konceptuálně ve v3, ale teď je to vynuceno mechanismem cascade layers prohlížeče, ne manipulací s pořadím zdrojového kódu.
Přidání vlastních utilit#
Ve v3 jste definovali vlastní utility s plugin API nebo @layer utilities:
// v3 — plugin přístup
const plugin = require("tailwindcss/plugin");
module.exports = {
plugins: [
plugin(function ({ addUtilities }) {
addUtilities({
".text-balance": {
"text-wrap": "balance",
},
".text-pretty": {
"text-wrap": "pretty",
},
});
}),
],
};Ve v4 se vlastní utility definují direktivou @utility:
@import "tailwindcss";
@utility text-balance {
text-wrap: balance;
}
@utility text-pretty {
text-wrap: pretty;
}Direktiva @utility říká Tailwindu „toto je utility třída — vlož ji do utilities vrstvy a povol její použití s variantami." Ta poslední část je klíčová. Utilita definovaná s @utility automaticky funguje s hover:, focus:, md: a každou další variantou:
<p class="text-pretty md:text-balance">...</p>Vlastní varianty#
Můžete také definovat vlastní varianty s @variant:
@import "tailwindcss";
@variant hocus (&:hover, &:focus);
@variant theme-dark (.dark &);<button class="hocus:bg-brand-500 theme-dark:text-white">
Klikni
</button>Toto nahrazuje v3 addVariant plugin API pro většinu případů použití. Je to méně mocné (nemůžete generovat varianty programaticky), ale pokrývá 90 % toho, co lidé skutečně dělají.
Container Queries: Vestavěné, bez pluginu#
Container queries byly jednou z nejvíce žádaných funkcí ve v3. Mohli jste je získat s pluginem @tailwindcss/container-queries, ale byl to doplněk. Ve v4 jsou zabudované do frameworku.
Základní použití#
Označte kontejner s @container a dotazujte jeho velikost s prefixem @:
<!-- označíme rodiče jako kontejner -->
<div class="@container">
<!-- responsivní vůči šířce rodiče, ne viewportu -->
<div class="flex flex-col @md:flex-row @lg:grid @lg:grid-cols-3">
<div class="p-4">Karta 1</div>
<div class="p-4">Karta 2</div>
<div class="p-4">Karta 3</div>
</div>
</div>Varianty @md, @lg atd. fungují jako responsivní breakpointy, ale jsou relativní k nejbližšímu @container předkovi místo viewportu. Hodnoty breakpointů odpovídají výchozím breakpointům Tailwindu:
| Varianta | Min-width |
|---|---|
@sm | 24rem (384px) |
@md | 28rem (448px) |
@lg | 32rem (512px) |
@xl | 36rem (576px) |
@2xl | 42rem (672px) |
Pojmenované kontejnery#
Kontejnery můžete pojmenovat pro dotazování specifických předků:
<div class="@container/sidebar">
<div class="@container/card">
<!-- dotazuje card kontejner -->
<div class="@md/card:text-lg">...</div>
<!-- dotazuje sidebar kontejner -->
<div class="@lg/sidebar:hidden">...</div>
</div>
</div>Proč na tom záleží#
Container queries mění způsob, jakým přemýšlíte o responsivním designu. Místo „při této šířce viewportu zobraz tři sloupce" řeknete „když je kontejner této komponenty dostatečně široký, zobraz tři sloupce." Komponenty se stávají skutečně samostatnými. Můžete přesunout komponentu karty z full-width layoutu do sidebaru a automaticky se přizpůsobí. Žádné cvičení s media queries.
Své knihovny komponent refaktoruji na použití container queries jako výchozího místo viewport breakpointů. Výsledkem jsou komponenty, které fungují kdekoli je umístíte, bez toho, aby rodič potřeboval cokoli vědět o responsivním chování komponenty.
<!-- Tato komponenta se přizpůsobí JAKÉMUKOLI kontejneru, do kterého je umístěna -->
<article class="@container">
<div class="grid grid-cols-1 @md:grid-cols-[200px_1fr] gap-4">
<img
class="w-full @md:w-auto rounded-lg aspect-video @md:aspect-square object-cover"
src="/post-image.jpg"
alt=""
/>
<div>
<h2 class="text-lg @lg:text-xl font-semibold">Název příspěvku</h2>
<p class="mt-2 text-sm @md:text-base text-gray-600">
Výtah z příspěvku...
</p>
<div class="mt-4 hidden @md:flex gap-2">
<span class="text-xs bg-gray-100 px-2 py-1 rounded">Tag</span>
</div>
</div>
</div>
</article>Nové varianty, které skutečně záleží#
v4 přidává několik nových variant, po kterých neustále sahám. Vyplňují reálné mezery.
Varianta starting:#
Mapuje se na CSS @starting-style, který umožňuje definovat počáteční stav elementu při jeho prvním zobrazení. Toto je chybějící dílek pro animování vstupu elementu bez JavaScriptu:
<dialog class="opacity-0 starting:opacity-0 open:opacity-100 transition-opacity duration-300">
<p>Tento dialog se rozsvítí při otevření</p>
</dialog>Varianta starting: generuje CSS uvnitř bloku @starting-style:
/* co Tailwind generuje */
@starting-style {
dialog[open] {
opacity: 0;
}
}
dialog[open] {
opacity: 1;
transition: opacity 300ms;
}Toto je obrovské pro dialogy, popovery, dropdown menu — cokoli, co potřebuje vstupní animaci. Předtím jste potřebovali JavaScript k přidání třídy na dalším framu, nebo jste použili @keyframes. Teď je to utility třída.
Varianta not-*#
Negace. Něco, co jsme chtěli odjakživa:
<!-- každé dítě kromě posledního dostane border -->
<div class="divide-y">
<div class="not-last:pb-4">Položka 1</div>
<div class="not-last:pb-4">Položka 2</div>
<div class="not-last:pb-4">Položka 3</div>
</div>
<!-- stylujeme vše, co není disabled -->
<input class="not-disabled:hover:border-brand-500" />
<!-- negujeme data atributy -->
<div class="not-data-active:opacity-50">...</div>Varianty nth-*#
Přímý přístup k nth-child a nth-of-type:
<ul>
<li class="nth-1:font-bold">První položka — tučně</li>
<li class="nth-even:bg-gray-50">Sudé řádky — šedé pozadí</li>
<li class="nth-odd:bg-white">Liché řádky — bílé pozadí</li>
<li class="nth-[3n+1]:text-brand-500">Každý třetí+1 — brand barva</li>
</ul>Syntaxe se závorkami (nth-[3n+1]) podporuje jakýkoli platný nth-child výraz. Toto nahrazuje spoustu vlastního CSS, které jsem dříve psal pro stripování tabulek a grid patterny.
Varianta in-* (Stav rodiče)#
Toto je inverze group-*. Místo „když je můj rodič (group) v hoveru, styluj mě" je to „když jsem uvnitř rodiče, který odpovídá tomuto stavu, styluj mě":
<div class="in-data-active:bg-brand-50">
Toto dostane pozadí, když jakýkoli předek má data-active
</div>Varianta **: Hluboký univerzální výběr#
Stylujte všechny potomky, ne jen přímé děti. Toto je kontrolovaná síla — používejte ji střídmě, ale je neocenitelná pro prózový obsah a CMS výstup:
<!-- všechny odstavce uvnitř tohoto divu, v jakékoli hloubce -->
<div class="**:data-highlight:bg-yellow-100">
<section>
<p data-highlight>Toto se zvýrazní</p>
<div>
<p data-highlight>Toto také, zanořeno hlouběji</p>
</div>
</section>
</div>Breaking Changes: Co se skutečně rozbilo#
Budu přímý. Pokud máte velký v3 projekt, migrace není triviální. Zde je to, co se rozbilo v mých projektech:
1. Formát konfigurace#
Váš tailwind.config.js nefunguje rovnou. Musíte buď:
- Převést ho na
@themeCSS (doporučeno pro novou architekturu) - Použít kompatibilní vrstvu s direktivou
@config(rychlá migrační cesta)
/* rychlá migrace — ponechte starý config */
@import "tailwindcss";
@config "../../tailwind.config.js";Tento @config most funguje, ale je explicitně migrační nástroj. Doporučení je přejít na @theme postupem času.
2. Odstraněné zastaralé utility#
Některé utility, které byly ve v3 zastaralé, jsou pryč:
/* ODSTRANĚNO ve v4 */
bg-opacity-* → použijte bg-black/50 (lomítková syntaxe opacity)
text-opacity-* → použijte text-black/50
border-opacity-* → použijte border-black/50
flex-shrink-* → použijte shrink-*
flex-grow-* → použijte grow-*
overflow-ellipsis → použijte text-ellipsis
decoration-slice → použijte box-decoration-slice
decoration-clone → použijte box-decoration-clone
Pokud jste již ve v3 používali moderní syntaxi (lomítková opacity, shrink-*), jste v pohodě. Pokud ne, jedná se o přímočaré změny typu najdi-a-nahraď.
3. Změny výchozí palety barev#
Výchozí paleta barev se mírně posunula. Pokud závisíte na přesných hodnotách barev z v3 (ne podle názvu, ale podle skutečné hex hodnoty), můžete si všimnout vizuálních rozdílů. Pojmenované barvy (blue-500, gray-200) stále existují, ale některé hex hodnoty se změnily.
4. Detekce obsahu#
v3 vyžadoval explicitní konfiguraci content:
// v3
module.exports = {
content: [
"./src/**/*.{js,ts,jsx,tsx}",
"./components/**/*.{js,ts,jsx,tsx}",
],
};v4 používá automatickou detekci obsahu. Skenuje kořen vašeho projektu a automaticky nachází soubory se šablonami. Většinou to „prostě funguje," ale pokud máte neobvyklou strukturu projektu (monorepo s balíčky mimo kořen projektu, soubory šablon na neočekávaných místech), možná budete muset explicitně nakonfigurovat zdrojové cesty:
@import "tailwindcss";
@source "../shared-components/**/*.tsx";
@source "../design-system/src/**/*.tsx";5. Změny Plugin API#
Pokud jste psali vlastní pluginy, API se změnilo. Funkce addUtilities, addComponents, addBase a addVariant stále fungují přes kompatibilní vrstvu, ale idiomatický přístup v4 je CSS-nativní:
// v3 plugin
plugin(function ({ addUtilities, theme }) {
addUtilities({
".scrollbar-hide": {
"-ms-overflow-style": "none",
"scrollbar-width": "none",
"&::-webkit-scrollbar": {
display: "none",
},
},
});
});/* v4 — jen CSS */
@utility scrollbar-hide {
-ms-overflow-style: none;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
}Většina prvotřídních pluginů (@tailwindcss/typography, @tailwindcss/forms, @tailwindcss/aspect-ratio) má v4-kompatibilní verze. Pluginy třetích stran jsou buď ano, nebo ne — zkontrolujte jejich repo před migrací.
6. JIT je jediný režim#
Ve v3 jste se mohli z JIT režimu odhlásit (i když to skoro nikdo nedělal). Ve v4 žádný ne-JIT režim neexistuje. Vše se generuje na vyžádání, vždy. Pokud jste měli nějaký důvod používat starý AOT (ahead-of-time) engine, ta cesta je pryč.
7. Některé změny syntaxe variant#
Několik variant bylo přejmenováno nebo změnilo chování:
<!-- v3 -->
<div class="[&>*]:p-4">...</div>
<!-- v4 — část >* nyní používá syntaxi inset varianty -->
<div class="*:p-4">...</div>Syntaxe libovolných variant [&...] stále funguje, ale v4 poskytuje pojmenované alternativy pro běžné patterny.
Průvodce migrací: Skutečný proces#
Takto jsem skutečně migroval, ne šťastná cesta z dokumentace, ale jak proces opravdu vypadal.
Krok 1: Spusťte oficiální codemod#
Tailwind poskytuje codemod, který zvládne většinu mechanických změn:
npx @tailwindcss/upgradeToto dělá hodně automaticky:
- Převádí direktivy
@tailwindna@import "tailwindcss" - Přejmenovává zastaralé utility třídy
- Aktualizuje syntaxi variant
- Převádí opacity utility na lomítkovou syntaxi (
bg-opacity-50nabg-black/50) - Vytváří základní blok
@themez vaší konfigurace
Co codemod zvládá dobře#
- Přejmenování utility tříd (téměř dokonalé)
- Změny syntaxe direktiv
- Jednoduché theme hodnoty (barvy, spacing, fonty)
- Migrace syntaxe opacity
Co codemod NEZVLÁDÁ#
- Složité konverze pluginů
- Dynamické konfigurační hodnoty (volání
theme()v JavaScriptu) - Podmíněnou konfiguraci tématu (např. theme hodnoty na základě prostředí)
- Migrace vlastního plugin API
- Edge cases libovolných hodnot, kde nový parser interpretuje odlišně
- Názvy tříd konstruované dynamicky v JavaScriptu (template literály, string concatenation)
Krok 2: Opravte konfiguraci PostCSS#
Pro většinu setupů aktualizujete svůj PostCSS config:
// postcss.config.js — v4
module.exports = {
plugins: {
"@tailwindcss/postcss": {},
},
};Poznámka: název pluginu se změnil z tailwindcss na @tailwindcss/postcss. Pokud používáte Vite, můžete PostCSS zcela přeskočit a použít Vite plugin:
// vite.config.ts
import tailwindcss from "@tailwindcss/vite";
export default defineConfig({
plugins: [tailwindcss()],
});Krok 3: Převeďte konfiguraci tématu#
Toto je manuální část. Vezměte vaše theme hodnoty z tailwind.config.js a převeďte je na @theme:
// v3 config — před
module.exports = {
theme: {
extend: {
colors: {
brand: {
light: "#60a5fa",
DEFAULT: "#3b82f6",
dark: "#1d4ed8",
},
},
fontSize: {
"2xs": ["0.65rem", { lineHeight: "1rem" }],
},
animation: {
"fade-in": "fade-in 0.5s ease-out",
},
keyframes: {
"fade-in": {
"0%": { opacity: "0" },
"100%": { opacity: "1" },
},
},
},
},
};/* v4 CSS — po */
@import "tailwindcss";
@theme {
--color-brand-light: #60a5fa;
--color-brand: #3b82f6;
--color-brand-dark: #1d4ed8;
--text-2xs: 0.65rem;
--text-2xs--line-height: 1rem;
--animate-fade-in: fade-in 0.5s ease-out;
}
@keyframes fade-in {
0% { opacity: 0; }
100% { opacity: 1; }
}Všimněte si, že keyframes se přesunují mimo @theme a stávají se běžnými CSS @keyframes. Název animace v @theme na ně pouze odkazuje. Toto je čistší — keyframes jsou CSS, měly by být psány jako CSS.
Krok 4: Vizuální regresní testování#
Toto je nezbytné. Po migraci jsem otevřel každou stránku své aplikace a vizuálně ji zkontroloval. Také jsem spustil své Playwright screenshot testy (pokud je máte). Codemod je dobrý, ale ne dokonalý. Co jsem zachytil při vizuální kontrole:
- Několik míst, kde migrace syntaxe opacity produkovala mírně odlišné výsledky
- Výstup vlastních pluginů, který se nepřenesl
- Změny z-index stackingu kvůli řazení vrstev
- Některé
!importantoverridy, které se chovaly odlišně s cascade layers
Krok 5: Aktualizujte závislosti třetích stran#
Zkontrolujte každý balíček související s Tailwindem:
{
"@tailwindcss/typography": "^1.0.0",
"@tailwindcss/forms": "^1.0.0",
"@tailwindcss/container-queries": "ODSTRAŇTE — nyní vestavěné",
"tailwindcss-animate": "zkontrolujte podporu v4",
"prettier-plugin-tailwindcss": "aktualizujte na nejnovější"
}Plugin @tailwindcss/container-queries již není potřeba — container queries jsou vestavěné. Ostatní pluginy potřebují své v4-kompatibilní verze.
Práce s Next.js#
Jelikož používám Next.js pro většinu projektů, zde je konkrétní setup.
PostCSS přístup (doporučený pro Next.js)#
Next.js používá PostCSS pod kapotou, takže PostCSS plugin je přirozená volba:
npm install tailwindcss @tailwindcss/postcss// postcss.config.mjs
export default {
plugins: {
"@tailwindcss/postcss": {},
},
};/* app/globals.css */
@import "tailwindcss";
@theme {
--font-sans: "Inter Variable", ui-sans-serif, system-ui, sans-serif;
--font-mono: "JetBrains Mono Variable", ui-monospace, monospace;
}To je kompletní setup. Žádný tailwind.config.js, žádný autoprefixer (v4 zvládá vendor prefixy interně).
Pořadí CSS importů#
Jedna věc, na které jsem se zasekl: pořadí CSS importů záleží ve v4 víc kvůli cascade layers. Váš @import "tailwindcss" by měl být před vašimi vlastními styly:
/* správné pořadí */
@import "tailwindcss";
@import "./theme.css";
@import "./custom-utilities.css";
/* vaše inline @theme, @utility, atd. */Pokud importujete vlastní CSS před Tailwindem, vaše styly mohou skončit v cascade layer s nižší prioritou a být nečekaně přepsány.
Tmavý režim#
Tmavý režim funguje konceptuálně stejně, ale konfigurace se přesunula do CSS:
@import "tailwindcss";
/* Použijte class-based tmavý režim (výchozí je media-based) */
@variant dark (&:where(.dark, .dark *));Toto nahrazuje v3 konfiguraci:
// v3
module.exports = {
darkMode: "class",
};Přístup @variant je flexibilnější. Tmavý režim můžete definovat jak chcete — na základě třídy, data-atributu nebo media query:
/* přístup přes data atribut */
@variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));
/* media query — toto je výchozí, takže to nemusíte deklarovat */
@variant dark (@media (prefers-color-scheme: dark));Kompatibilita s Turbopackem#
Pokud používáte Next.js s Turbopackem (který je nyní výchozí dev bundler), v4 funguje skvěle. Rust engine ladí s vlastní Rust-based architekturou Turbopacku. Změřil jsem časy spuštění dev serveru:
| Setup | v3 + Webpack | v3 + Turbopack | v4 + Turbopack |
|---|---|---|---|
| Studený start | 4.8s | 2.1s | 1.3s |
| HMR (CSS změna) | 450ms | 180ms | 40ms |
40ms HMR pro CSS změny je sotva postřehnutelné. Působí to okamžitě.
Hlubší pohled na výkon: Za hranicí rychlosti buildu#
Výhody Oxide enginu sahají za pouhou rychlost buildu.
Spotřeba paměti#
v4 používá výrazně méně paměti. Na mém projektu s 847 komponentami:
| Metrika | v3 | v4 |
|---|---|---|
| Špičková paměť (build) | 380MB | 45MB |
| Ustálený stav (dev) | 210MB | 28MB |
To záleží pro CI/CD pipeline, kde je paměť omezená, a pro vývojové stroje, které současně spouští deset procesů.
Velikost CSS výstupu#
v4 generuje mírně menší CSS výstup, protože nový engine je lepší v deduplikaci a eliminaci mrtvého kódu:
v3 výstup: 34.2 KB (gzipped)
v4 výstup: 29.8 KB (gzipped)
13% redukce bez změny jakéhokoli kódu. Ne transformativní, ale výkon zadarmo.
Tree Shaking theme hodnot#
Ve v4, pokud definujete theme hodnotu, ale nikdy ji nepoužijete ve svých šablonách, odpovídající CSS custom property je stále emitována (je v @theme, které se mapuje na :root proměnné). Nicméně utility třídy pro nepoužité hodnoty se negenerují. To je stejné jako chování JIT ve v3, ale stojí za zmínku: vaše CSS custom properties jsou vždy dostupné, i pro hodnoty bez utility použití.
Pokud chcete zabránit určitým theme hodnotám v generování CSS custom properties, můžete použít @theme inline:
@theme inline {
/* Tyto hodnoty generují utility, ale NE CSS custom properties */
--color-internal-debug: #ff00ff;
--spacing-magic-number: 3.7rem;
}Toto je užitečné pro interní design tokeny, které nechcete vystavovat jako CSS proměnné.
Pokročilé: Skládání témat pro více značek#
Jeden pattern, který v4 výrazně zjednodušuje, je multi-brand theming. Protože theme hodnoty jsou CSS custom properties, můžete je vyměnit za běhu:
@import "tailwindcss";
@theme {
--color-brand: var(--brand-primary, #3b82f6);
--color-brand-light: var(--brand-light, #60a5fa);
--color-brand-dark: var(--brand-dark, #1d4ed8);
}
/* Přepisy značek */
.theme-acme {
--brand-primary: #e11d48;
--brand-light: #fb7185;
--brand-dark: #9f1239;
}
.theme-globex {
--brand-primary: #059669;
--brand-light: #34d399;
--brand-dark: #047857;
}<body class="theme-acme">
<!-- všechny bg-brand, text-brand, atd. používají barvy Acme -->
<div class="bg-brand text-white">Acme Corp</div>
</body>Ve v3 toto vyžadovalo vlastní plugin nebo složité nastavení CSS proměnných mimo Tailwind. Ve v4 je to přirozené — téma jsou CSS proměnné a CSS proměnné kaskádují. Toto je druh věci, díky které CSS-first přístup působí správně.
Co mi chybí z v3#
Budu vyrovnaný. Jsou věci, které v3 dělal a které mi ve v4 skutečně chybí:
1. JavaScript konfigurace pro programatická témata. Měl jsem projekt, kde jsme generovali barevné škály z jediné brand barvy pomocí JavaScriptové funkce v konfiguraci. Ve v4 to v @theme udělat nemůžete — potřebovali byste build krok, který generuje CSS soubor, nebo byste barvy spočítali jednou a vložili je. Kompatibilní vrstva @config pomáhá, ale není to dlouhodobé řešení.
2. IntelliSense byl lepší při spuštění. V3 VS Code rozšíření mělo roky ladění. v4 IntelliSense funguje, ale zpočátku měl některé mezery — vlastní @theme hodnoty se někdy nedoplňovaly automaticky a definice @utility nebyly vždy zachyceny. To se s nedávnými aktualizacemi podstatně zlepšilo, ale stojí za zmínku.
3. Vyspělost ekosystému. Ekosystém kolem v3 byl obrovský. Headless UI, Radix, shadcn/ui, Flowbite, DaisyUI — vše bylo testováno proti v3. Podpora v4 se postupně zavádí, ale není univerzální. Musel jsem poslat PR jedné knihovně komponent, abych opravil kompatibilitu s v4.
Měli byste migrovat?#
Zde je můj rozhodovací rámec po několika týdnech života s v4:
Migrujte teď, pokud:#
- Začínáte nový projekt (zřejmá volba — začněte s v4)
- Váš projekt má minimální vlastní pluginy
- Chcete výhody výkonu pro velké projekty
- Již používáte moderní Tailwind patterny (lomítková opacity,
shrink-*atd.) - Potřebujete container queries a raději byste nepřidávali plugin
Počkejte, pokud:#
- Silně závisíte na pluginech třetích stran, které zatím v4 nepodporují
- Máte složitou programatickou konfiguraci tématu
- Váš projekt je stabilní a aktivně se nevyvíjí (proč na něj sahat?)
- Jste uprostřed feature sprintu (migrujte mezi sprinty, ne během)
Nemigrujte, pokud:#
- Jste na v2 nebo dříve (nejdřív upgradujte na v3, stabilizujte, pak zvažte v4)
- Váš projekt končí v příštích pár měsících (nestojí za tu turbulenci)
Můj upřímný názor#
Pro nové projekty je v4 zřejmá volba. CSS-first konfigurace je čistší, engine je dramaticky rychlejší a nové funkce (container queries, @starting-style, nové varianty) jsou skutečně užitečné.
Pro existující projekty doporučuji postupný přístup:
- Teď: Začněte jakýkoli nový projekt na v4
- Brzy: Experimentujte konverzí malého interního projektu na v4
- Až budete připraveni: Migrujte produkční projekty během klidného sprintu, s vizuálním regresním testováním
Migrace není bolestivá, pokud se na ni připravíte. Codemod zvládá 80 % práce. Zbývajících 20 % je manuálních, ale přímočarých. Počítejte den na střední projekt, dva až tři dny na velký.
Tailwind v4 je to, čím měl Tailwind být odjakživa. JavaScriptová konfigurace byla vždy ústupek nástrojům své doby. CSS-first konfigurace, nativní cascade layers, Rust engine — to nejsou trendy, to je framework, který dohání platformu. Webová platforma se zlepšila a Tailwind v4 se do ní opírá místo toho, aby proti ní bojoval.
Přechod k psaní designových tokenů v CSS, jejich skládání s CSS funkcemi a ponechání kaskádového mechanismu prohlížeče na starosti o specificitu — to je správný směr. Trvalo to čtyři velké verze, než jsme se sem dostali, ale výsledek je nejkoherentnější verze Tailwindu dosud.
Začněte na tom svůj další projekt. Nebudete se ohlížet.