İçeriğe geç
·17 dk okuma

Tailwind CSS v4: Gerçekte Ne Değişti ve Geçiş Yapmalı mısın

CSS-first yapılandırma, @layer entegrasyonu, yerleşik container query'ler, yeni motor performansı, kırıcı değişiklikler ve v3'ten v4'e dürüst geçiş deneyimim.

Paylaş:X / TwitterLinkedIn

Tailwind CSS'i v1.x'ten beri kullanıyorum, topluluğun yarısının iğrenç bulduğu, diğer yarısının onunla göndermekten vazgeçemediği günlerden beri. Her major sürüm önemli bir sıçrama oldu ama v4 farklı. Sadece bir özellik sürümü değil. Senle framework arasındaki temel sözleşmeyi değiştiren baştan sona bir mimari yeniden yazım.

İki production projeyi v3'ten v4'e geçirdikten ve üç yeni projeyi sıfırdan v4'te başlattıktan sonra neyin gerçekten daha iyi, neyin pürüzlü ve bugün geçiş yapmanın doğru olup olmadığı konusunda net bir resmim var. Hype yok, öfke yok — sadece gözlemlediğim şey.

Büyük Resim: v4 Gerçekte Ne#

Tailwind CSS v4 aynı anda üç şey:

  1. Yeni bir motor — JavaScript'ten Rust'a yeniden yazıldı (Oxide motoru), build'leri dramatik olarak hızlandırıyor
  2. Yeni bir yapılandırma paradigması — CSS-first yapılandırma varsayılan olarak tailwind.config.js'nin yerini alıyor
  3. CSS platformuyla daha sıkı entegrasyon — native @layer, container query'ler, @starting-style ve cascade layer'lar birinci sınıf vatandaş

Her yerde göreceğin manşet "10 kat daha hızlı." Bu gerçek ama asıl değişikliği küçümsüyor. Tailwind'i yapılandırma ve genişletme zihinsel modeli temelden kaymış. Artık CSS ile çalışıyorsun, CSS üreten bir JavaScript yapılandırma nesnesiyle değil.

Minimal bir Tailwind v4 kurulumu şöyle görünür:

css
/* app.css — kurulumun tamamı bu */
@import "tailwindcss";

Bu kadar. Config dosyası yok. PostCSS plugin yapılandırması yok (çoğu kurulum için). @tailwind base; @tailwind components; @tailwind utilities; direktifleri yok. Tek import ve çalışıyorsun.

v3 ile karşılaştır:

css
/* v3 — app.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
js
// v3 — tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./src/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
};
js
// v3 — postcss.config.js
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
};

Üç dosya tek bir satıra indi. Bu sadece daha az boilerplate değil — yanlış yapılandırma için daha az yüzey alanı. v4'te içerik algılama otomatik. Glob kalıpları hecelemene gerek kalmadan proje dosyalarını tarar.

@theme ile CSS-First Yapılandırma#

Bu en büyük kavramsal değişim. v3'te Tailwind'i bir JavaScript config nesnesiyle özelleştirirdin:

js
// 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",
      },
    },
  },
};

v4'te tüm bunlar CSS'te @theme direktifiyle yaşar:

css
@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;
}

İlk başta direndim. Tüm tasarım sistemimi görebildiğim tek bir JavaScript nesnesini seviyordum. Ama CSS yaklaşımıyla bir hafta sonra fikrimi üç nedenden dolayı değiştirdim:

1. Native CSS custom property'ler otomatik olarak dışa aktarılır. @theme'de tanımladığın her değer :root üzerinde bir CSS custom property olur. Bu da tema değerlerinin düz CSS'te, CSS Modules'ta, <style> tag'lerinde — CSS'in çalıştığı her yerde erişilebilir olduğu anlamına gelir:

css
/* bunu bedavaya alırsın */
:root {
  --color-brand-50: #eff6ff;
  --color-brand-500: #3b82f6;
  --color-brand-900: #1e3a5f;
}
css
/* her yerde kullan — Tailwind gerekmez */
.custom-element {
  border: 2px solid var(--color-brand-500);
}

2. @theme içinde CSS özelliklerini kullanabilirsin. Media query'ler, light-dark(), calc() — gerçek CSS burada çalışır çünkü gerçek CSS:

css
@theme {
  --color-surface: light-dark(#ffffff, #0a0a0a);
  --color-text: light-dark(#0a0a0a, #fafafa);
  --spacing-container: calc(100vw - 2rem);
}

3. Diğer CSS'inle birlikte yaşama. Teman, özel utility'lerin ve base stillerin aynı dilde, istersen aynı dosyada yaşar. "CSS dünyası" ile "JavaScript config dünyası" arası bağlam geçişi yok.

Varsayılan Temayı Override vs Extend Etme#

v3'te theme (değiştir) vs theme.extend (birleştir) vardı. v4'te zihinsel model farklı:

css
@import "tailwindcss";
 
/* Bu varsayılan temayı EXTEND eder — mevcut renklerin yanına brand renkleri ekler */
@theme {
  --color-brand-500: #3b82f6;
}

Bir namespace'i tamamen değiştirmek istiyorsan (tüm varsayılan renkleri kaldırmak gibi), @theme'i --color-* wildcard reset ile kullanırsın:

css
@import "tailwindcss";
 
@theme {
  /* Önce tüm varsayılan renkleri temizle */
  --color-*: initial;
 
  /* Şimdi sadece kendi renklerini tanımla */
  --color-white: #ffffff;
  --color-black: #000000;
  --color-brand-50: #eff6ff;
  --color-brand-500: #3b82f6;
  --color-brand-900: #1e3a5f;
}

Bu wildcard reset kalıbı zarif. Varsayılan temanın hangi parçalarını tutacağını ve hangilerini değiştireceğini tam olarak seçersin. Tüm varsayılan spacing'i ama özel renkleri mi istiyorsun? --color-*: initial; ile sıfırla ve spacing'e dokunma.

Birden Fazla Tema Dosyası#

Daha büyük projeler için temayı dosyalara bölebilirsin:

css
/* 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;
}
css
/* app.css */
@import "tailwindcss";
@import "./theme/colors.css";
@import "./theme/typography.css";

Bu, v3'teki devasa bir tailwind.config.js'ye sahip olmak veya require() ile bölmeye çalışmak kalıbından çok daha temiz.

Oxide Motoru: Gerçekten 10 Kat Daha Hızlı#

Tailwind v4'ün motoru Rust'ta tamamen yeniden yazıldı. Buna Oxide diyorlar. "10 kat daha hızlı" iddiasına şüpheciydim — pazarlama rakamları gerçek projelerle karşılaşınca nadiren hayatta kalır. Bu yüzden benchmark yaptım.

Test projem: 847 bileşen, 142 sayfa, yaklaşık 23.000 Tailwind sınıf kullanımı olan bir Next.js uygulaması.

Metrikv3 (Node)v4 (Oxide)İyileşme
İlk build4.280ms387ms11x
Artımlı (1 dosya düzenle)340ms18ms19x
Tam rebuild (temiz)5.100ms510ms10x
Dev sunucu başlangıcı3.200ms290ms11x

"10x" iddiası projem için tutucu. Artımlı build'ler gerçekten parlıyor — 18ms temelde anlık. Dosyayı kaydedip sekme değiştirmeden tarayıcıda yeni stiller hazır.

Neden Bu Kadar Hızlı?#

Üç neden:

1. JavaScript yerine Rust. Çekirdek CSS parser'ı, sınıf algılama ve kod üretimi hep native Rust. Bu "eğlence için Rust'ta yeniden yazalım" durumu değil — CSS ayrıştırma gerçekten CPU'ya bağlı bir iş ve native kodun V8 üzerinde devasa bir avantajı var.

2. Hot path'te PostCSS yok. v3'te Tailwind bir PostCSS plugin'iydi. Her build şu anlama geliyordu: CSS'i PostCSS AST'ye ayrıştır, Tailwind plugin'ini çalıştır, CSS string'e geri serileştir, sonra diğer PostCSS plugin'leri çalışsın. v4'te Tailwind'in kaynaktan çıktıya doğrudan giden kendi CSS parser'ı var. PostCSS uyumluluk için hala destekleniyor ama birincil yol onu tamamen atlıyor.

3. Daha akıllı artımlı işleme. Yeni motor agresif olarak önbelleğe alır. Tek dosya düzenlediğinde, sadece o dosyayı sınıf adları için yeniden tarar ve sadece değişen CSS kurallarını yeniden üretir. v3 motoru insanların verdiği krediden daha akıllıydı bu konuda (JIT modu zaten artımlıydı) ama v4 ince taneli bağımlılık takibiyle çok daha ileri gidiyor.

Hız Gerçekten Önemli mi?#

Evet, ama beklediğin nedenden değil. Çoğu proje için v3'ün build hızı "iyiydi." Dev'de birkaç yüz milisaniye beklerdin. Acı verici değildi.

v4 hızı önemli çünkü Tailwind'i araç zincirinde görünmez kılıyor. Build'ler 20ms altındayken Tailwind'i bir build adımı olarak düşünmeyi bırakıyorsun. Sözdizimi vurgulama gibi oluyor — her zaman orada, asla yolda değil. Bu psikolojik fark tam bir geliştirme gününde önemli.

Native @layer Entegrasyonu#

v3'te Tailwind @layer base, @layer components ve @layer utilities ile kendi katman sistemini kullanıyordu. Bunlar CSS cascade layer'larına benziyordu ama değillerdi — üretilen CSS'in çıktıda nerede görüneceğini kontrol eden Tailwind'e özgü direktiflerdi.

v4'te Tailwind gerçek CSS cascade layer'ları kullanır:

css
/* v4 çıktısı — basitleştirilmiş */
@layer theme, base, components, utilities;
 
@layer base {
  /* reset, preflight */
}
 
@layer components {
  /* senin bileşen sınıfların */
}
 
@layer utilities {
  /* tüm üretilen utility sınıfları */
}

Bu önemli bir değişiklik çünkü CSS cascade layer'larının gerçek specificity etkileri var. Daha düşük öncelikli bir katmandaki kural, seçici specificity'sinden bağımsız olarak her zaman daha yüksek öncelikli bir katmandakine kaybeder. Bu şu anlama gelir:

css
@layer components {
  /* specificity: 0-1-0 */
  .card { padding: 1rem; }
}
 
@layer utilities {
  /* specificity: 0-1-0 — aynı specificity ama kazanır çünkü utilities katmanı daha sonra */
  .p-4 { padding: 1rem; }
}

Utility'ler her zaman bileşenleri override eder. Bileşenler her zaman base'i override eder. v3'te Tailwind kavramsal olarak böyle çalışıyordu ama artık kaynak sıra manipülasyonuyla değil, tarayıcının cascade layer mekanizmasıyla zorunlu kılınıyor.

Özel Utility'ler Ekleme#

v3'te özel utility'leri bir plugin API'ı veya @layer utilities ile tanımlardın:

js
// v3 — plugin yaklaşımı
const plugin = require("tailwindcss/plugin");
 
module.exports = {
  plugins: [
    plugin(function ({ addUtilities }) {
      addUtilities({
        ".text-balance": {
          "text-wrap": "balance",
        },
        ".text-pretty": {
          "text-wrap": "pretty",
        },
      });
    }),
  ],
};

v4'te özel utility'ler @utility direktifiyle tanımlanır:

css
@import "tailwindcss";
 
@utility text-balance {
  text-wrap: balance;
}
 
@utility text-pretty {
  text-wrap: pretty;
}

@utility direktifi Tailwind'e "bu bir utility sınıfı — utilities katmanına koy ve variant'larla kullanılmasına izin ver" der. Son kısım önemli. @utility ile tanımlanan bir utility otomatik olarak hover:, focus:, md: ve diğer tüm variant'larla çalışır:

html
<p class="text-pretty md:text-balance">...</p>

Özel Variant'lar#

@variant ile özel variant'lar da tanımlayabilirsin:

css
@import "tailwindcss";
 
@variant hocus (&:hover, &:focus);
@variant theme-dark (.dark &);
html
<button class="hocus:bg-brand-500 theme-dark:text-white">
  Tıkla
</button>

Bu, çoğu kullanım durumu için v3 addVariant plugin API'sının yerini alır. Daha az güçlü (programatik variant üretimi yapamazsın) ama insanların gerçekte yaptığının %90'ını karşılar.

Container Query'ler: Yerleşik, Plugin Yok#

Container query'ler v3'te en çok istenen özelliklerden biriydi. @tailwindcss/container-queries plugin'iyle alabiliyordun ama ek bir paketdi. v4'te framework'e yerleşik.

Temel Kullanım#

Ebeveyni @container ile işaretle ve @ önekiyle boyutunu sorgula:

html
<!-- ebeveyni container olarak işaretle -->
<div class="@container">
  <!-- viewport'a değil ebeveynin genişliğine göre responsive -->
  <div class="flex flex-col @md:flex-row @lg:grid @lg:grid-cols-3">
    <div class="p-4">Kart 1</div>
    <div class="p-4">Kart 2</div>
    <div class="p-4">Kart 3</div>
  </div>
</div>

@md, @lg vb. variant'lar responsive breakpoint'ler gibi çalışır ama viewport yerine en yakın @container ataya göre. Breakpoint değerleri Tailwind'in varsayılan breakpoint'lerine karşılık gelir:

VariantMin-width
@sm24rem (384px)
@md28rem (448px)
@lg32rem (512px)
@xl36rem (576px)
@2xl42rem (672px)

İsimli Container'lar#

Belirli ataları sorgulamak için container'ları adlandırabilirsin:

html
<div class="@container/sidebar">
  <div class="@container/card">
    <!-- card container'ını sorgular -->
    <div class="@md/card:text-lg">...</div>
 
    <!-- sidebar container'ını sorgular -->
    <div class="@lg/sidebar:hidden">...</div>
  </div>
</div>

Bu Neden Önemli#

Container query'ler responsive tasarım hakkındaki düşünce şeklini değiştiriyor. "Bu viewport genişliğinde üç sütun göster" yerine "bu bileşenin container'ı yeterince genişken üç sütun göster" diyorsun. Bileşenler gerçekten bağımsız hale geliyor. Bir kart bileşenini tam genişlikli layout'tan sidebar'a taşıyabilirsin ve otomatik olarak uyum sağlar. Media query cambazlığı yok.

Bileşen kütüphanelerimi viewport breakpoint'leri yerine varsayılan olarak container query'leri kullanacak şekilde yeniden düzenliyorum. Sonuç, nereye yerleştirirsen yerleştir çalışan bileşenler — ebeveynin bileşenin responsive davranışı hakkında hiçbir şey bilmesine gerek yok.

html
<!-- Bu bileşen yerleştirildiği HERHANGİ bir container'a uyum sağlar -->
<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">Yazı Başlığı</h2>
      <p class="mt-2 text-sm @md:text-base text-gray-600">
        Yazı özeti buraya gelir...
      </p>
      <div class="mt-4 hidden @md:flex gap-2">
        <span class="text-xs bg-gray-100 px-2 py-1 rounded">Etiket</span>
      </div>
    </div>
  </div>
</article>

Gerçekten Önemli Yeni Variant'lar#

v4 sürekli başvurduğum birkaç yeni variant ekliyor. Gerçek boşlukları dolduruyorlar.

starting: Variant'ı#

Bu CSS @starting-style'a eşlenir ve bir eleman ilk göründüğünde başlangıç durumunu tanımlamana izin verir. JavaScript olmadan eleman giriş animasyonu yapmanın eksik parçası:

html
<dialog class="opacity-0 starting:opacity-0 open:opacity-100 transition-opacity duration-300">
  <p>Bu dialog açıldığında fade-in yapar</p>
</dialog>

starting: variant'ı bir @starting-style bloğu içinde CSS üretir:

css
/* Tailwind'in ürettiği */
@starting-style {
  dialog[open] {
    opacity: 0;
  }
}
 
dialog[open] {
  opacity: 1;
  transition: opacity 300ms;
}

Bu dialog'lar, popover'lar, dropdown menüler — giriş animasyonu gerektiren her şey için devasa. Bundan önce bir sonraki frame'de sınıf eklemek için JavaScript gerekiyordu veya @keyframes kullanıyordun. Artık bir utility sınıfı.

not-* Variant'ı#

Negasyon. Her zaman istediğimiz bir şey:

html
<!-- sonuncu hariç her çocuk border alır -->
<div class="divide-y">
  <div class="not-last:pb-4">Öğe 1</div>
  <div class="not-last:pb-4">Öğe 2</div>
  <div class="not-last:pb-4">Öğe 3</div>
</div>
 
<!-- disabled olmayan her şeye stil ver -->
<input class="not-disabled:hover:border-brand-500" />
 
<!-- data attribute'larını nega et -->
<div class="not-data-active:opacity-50">...</div>

nth-* Variant'ları#

Doğrudan nth-child ve nth-of-type erişimi:

html
<ul>
  <li class="nth-1:font-bold">İlk öğe — kalın</li>
  <li class="nth-even:bg-gray-50">Çift satırlar — gri arkaplan</li>
  <li class="nth-odd:bg-white">Tek satırlar — beyaz arkaplan</li>
  <li class="nth-[3n+1]:text-brand-500">Her üçüncü+1 — brand rengi</li>
</ul>

Köşeli parantez sözdizimi (nth-[3n+1]) geçerli herhangi bir nth-child ifadesini destekler. Bu, tablo şeritleme ve grid kalıpları için yazmam gereken birçok özel CSS'in yerini aldı.

in-* Variant'ı (Ebeveyn Durumu)#

Bu group-*'ın tersi. "Ebeveyn (group) hover edildiğinde, beni stilize et" yerine "bu durumla eşleşen bir ebeveynin içindeyken, beni stilize et":

html
<div class="in-data-active:bg-brand-50">
  Herhangi bir ata data-active'e sahip olduğunda arka plan alır
</div>

**: Derin Evrensel Variant#

Sadece doğrudan çocukları değil, tüm torunları stilize et. Bu kontrollü güç — idareli kullan ama düzyazı içerik ve CMS çıktısı için paha biçilmez:

html
<!-- bu div'in içindeki tüm paragraflar, herhangi bir derinlikte -->
<div class="**:data-highlight:bg-yellow-100">
  <section>
    <p data-highlight>Bu vurgulanır</p>
    <div>
      <p data-highlight>Bu da, daha derinde iç içe</p>
    </div>
  </section>
</div>

Kırıcı Değişiklikler: Gerçekte Ne Bozuldu#

Açık konuşayım. Büyük bir v3 projen varsa, geçiş önemsiz değil. Projelerimde neler bozuldu:

1. Yapılandırma Formatı#

tailwind.config.js kutudan çıktığı gibi çalışmaz. Ya:

  • @theme CSS'e dönüştürmen (yeni mimari için önerilen) ya da
  • Uyumluluk katmanı @config direktifini kullanman (hızlı geçiş yolu) gerekir
css
/* hızlı geçiş — eski config'ini koru */
@import "tailwindcss";
@config "../../tailwind.config.js";

Bu @config köprüsü çalışır ama açıkça bir geçiş aracı. Öneri zamanla @theme'e geçmek.

2. Kaldırılan Deprecated Utility'ler#

v3'te deprecated olan bazı utility'ler gitti:

/* v4'te KALDIRILDI */
bg-opacity-*     → bg-black/50 kullan (slash opacity sözdizimi)
text-opacity-*   → text-black/50 kullan
border-opacity-* → border-black/50 kullan
flex-shrink-*    → shrink-* kullan
flex-grow-*      → grow-* kullan
overflow-ellipsis → text-ellipsis kullan
decoration-slice  → box-decoration-slice kullan
decoration-clone  → box-decoration-clone kullan

v3'te zaten modern sözdizimi kullanıyorduysan (slash opacity, shrink-*), sorun yok. Kullanmıyorsan, bunlar basit bul-ve-değiştir değişiklikleri.

3. Varsayılan Renk Paleti Değişiklikleri#

Varsayılan renk paleti hafifçe kaydı. v3'ten tam renk değerlerine bağımlıysan (isimle değil, gerçek hex değeriyle), görsel farklılıklar fark edebilirsin. İsimli renkler (blue-500, gray-200) hala var ama bazı hex değerleri değişti.

4. İçerik Algılama#

v3 açık content yapılandırması gerektiriyordu:

js
// v3
module.exports = {
  content: [
    "./src/**/*.{js,ts,jsx,tsx}",
    "./components/**/*.{js,ts,jsx,tsx}",
  ],
};

v4 otomatik içerik algılama kullanır. Proje kökünü tarar ve template dosyalarını otomatik bulur. Bu çoğunlukla "çalışır" ama olağandışı bir proje yapın varsa (proje kökü dışında paketlere sahip monorepo, beklenmedik konumlarda template dosyaları), kaynak yollarını açıkça yapılandırman gerekebilir:

css
@import "tailwindcss";
@source "../shared-components/**/*.tsx";
@source "../design-system/src/**/*.tsx";

5. Plugin API Değişiklikleri#

Özel plugin yazdıysan API değişti. addUtilities, addComponents, addBase ve addVariant fonksiyonları uyumluluk katmanı aracılığıyla hala çalışır ama idiomatik v4 yaklaşımı CSS-native:

js
// v3 plugin
plugin(function ({ addUtilities, theme }) {
  addUtilities({
    ".scrollbar-hide": {
      "-ms-overflow-style": "none",
      "scrollbar-width": "none",
      "&::-webkit-scrollbar": {
        display: "none",
      },
    },
  });
});
css
/* v4 — sadece CSS */
@utility scrollbar-hide {
  -ms-overflow-style: none;
  scrollbar-width: none;
  &::-webkit-scrollbar {
    display: none;
  }
}

Çoğu birinci taraf plugin'in (@tailwindcss/typography, @tailwindcss/forms, @tailwindcss/aspect-ratio) v4 uyumlu sürümleri var. Üçüncü taraf plugin'ler değişken — geçişten önce repo'larını kontrol et.

6. JIT Tek Mod#

v3'te JIT modundan çıkabiliyordun (neredeyse kimse çıkmasa da). v4'te JIT olmayan mod yok. Her şey her zaman istek üzerine üretilir. Eski AOT (ahead-of-time) motorunu kullanmak için bir nedenin varsa, o yol kapandı.

7. Bazı Variant Sözdizimi Değişiklikleri#

Birkaç variant yeniden adlandırıldı veya davranışları değişti:

html
<!-- v3 -->
<div class="[&>*]:p-4">...</div>
 
<!-- v4 — >* kısmı artık inset variant sözdizimini kullanır -->
<div class="*:p-4">...</div>

Arbitrary variant sözdizimi [&...] hala çalışır ama v4 yaygın kalıplar için isimli alternatifler sağlar.

Geçiş Rehberi: Gerçek Süreç#

İşte gerçekte nasıl geçiş yaptım — dokümanların mutlu yolu değil, sürecin gerçekte neye benzediği.

Adım 1: Resmi Codemod'u Çalıştır#

Tailwind mekanik değişikliklerin çoğunu idare eden bir codemod sağlar:

bash
npx @tailwindcss/upgrade

Bu otomatik olarak birçok şeyi yapar:

  • @tailwind direktiflerini @import "tailwindcss"'e dönüştürür
  • Deprecated utility sınıflarını yeniden adlandırır
  • Variant sözdizimini günceller
  • Opacity utility'lerini slash sözdizime dönüştürür (bg-opacity-50'yi bg-black/50'ye)
  • Config'inden temel bir @theme bloğu oluşturur

Codemod'un İyi Hallettiği#

  • Utility sınıf yeniden adlandırmaları (neredeyse mükemmel)
  • Direktif sözdizimi değişiklikleri
  • Basit tema değerleri (renkler, spacing, fontlar)
  • Opacity sözdizimi geçişi

Codemod'un Halletmediği#

  • Karmaşık plugin dönüşümleri
  • Dinamik config değerleri (JavaScript'te theme() çağrıları)
  • Koşullu tema yapılandırması (ör. ortama dayalı tema değerleri)
  • Özel plugin API geçişleri
  • Yeni parser'ın farklı yorumladığı arbitrary value uç durumları
  • JavaScript'te dinamik olarak oluşturulan sınıf adları (template literal'lar, string birleştirme)

Adım 2: PostCSS Yapılandırmasını Düzelt#

Çoğu kurulum için PostCSS config'ini güncelleyeceksin:

js
// postcss.config.js — v4
module.exports = {
  plugins: {
    "@tailwindcss/postcss": {},
  },
};

Not: plugin adı tailwindcss'ten @tailwindcss/postcss'e değişti. Vite kullanıyorsan PostCSS'i tamamen atlayıp Vite plugin'ini kullanabilirsin:

js
// vite.config.ts
import tailwindcss from "@tailwindcss/vite";
 
export default defineConfig({
  plugins: [tailwindcss()],
});

Adım 3: Tema Yapılandırmasını Dönüştür#

Bu manuel kısım. tailwind.config.js tema değerlerini alıp @theme'e dönüştür:

js
// v3 config — önce
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" },
        },
      },
    },
  },
};
css
/* v4 CSS — sonra */
@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; }
}

Keyframe'lerin @theme'den çıkıp normal CSS @keyframes olduğuna dikkat et. @theme'deki animasyon adı sadece onlara referans verir. Bu daha temiz — keyframe'ler CSS, CSS olarak yazılmalı.

Adım 4: Görsel Regresyon Testi#

Bu pazarlık konusu değil. Geçişten sonra uygulamanın her sayfasını açıp görsel olarak kontrol ettim. Playwright ekran görüntüsü testlerimi de çalıştırdım (varsa). Codemod iyi ama mükemmel değil. Görsel incelemede yakaladığım şeyler:

  • Opacity sözdizimi geçişinin biraz farklı sonuçlar ürettiği birkaç yer
  • Taşınmayan özel plugin çıktısı
  • Layer sıralaması nedeniyle z-index yığınlama değişiklikleri
  • Cascade layer'larla farklı davranan bazı !important override'ları

Adım 5: Üçüncü Taraf Bağımlılıkları Güncelle#

Tailwind ile ilgili her paketi kontrol et:

json
{
  "@tailwindcss/typography": "^1.0.0",
  "@tailwindcss/forms": "^1.0.0",
  "@tailwindcss/container-queries": "KALDIR — artık yerleşik",
  "tailwindcss-animate": "v4 desteğini kontrol et",
  "prettier-plugin-tailwindcss": "en son sürüme güncelle"
}

@tailwindcss/container-queries plugin'i artık gerekli değil — container query'ler yerleşik. Diğer plugin'lerin v4 uyumlu sürümleri gerekiyor.

Next.js ile Çalışma#

Çoğu projemde Next.js kullandığım için, spesifik kurulum burada.

PostCSS Yaklaşımı (Next.js İçin Önerilen)#

Next.js perde arkasında PostCSS kullanır, bu yüzden PostCSS plugin'i doğal uyum:

bash
npm install tailwindcss @tailwindcss/postcss
js
// postcss.config.mjs
export default {
  plugins: {
    "@tailwindcss/postcss": {},
  },
};
css
/* 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;
}

Kurulumun tamamı bu. tailwind.config.js yok, autoprefixer yok (v4 vendor prefix'leri dahili olarak halleder).

CSS Import Sırası#

Beni takılan bir şey: v4'te cascade layer'lar nedeniyle CSS import sırası daha önemli. @import "tailwindcss" özel stillerinden önce gelmeli:

css
/* doğru sıra */
@import "tailwindcss";
@import "./theme.css";
@import "./custom-utilities.css";
 
/* satır içi @theme, @utility vb. */

Özel CSS'i Tailwind'den önce import edersen, stillerin daha düşük bir cascade layer'ında kalabilir ve beklenmedik şekilde override edilebilir.

Dark Mode#

Dark mode kavramsal olarak aynı çalışır ama yapılandırma CSS'e taşındı:

css
@import "tailwindcss";
 
/* Sınıf tabanlı dark mode kullan (varsayılan media tabanlı) */
@variant dark (&:where(.dark, .dark *));

Bu v3 config'inin yerini alır:

js
// v3
module.exports = {
  darkMode: "class",
};

@variant yaklaşımı daha esnek. Dark mode'u istediğin gibi tanımlayabilirsin — sınıf tabanlı, data-attribute tabanlı veya media-query tabanlı:

css
/* data attribute yaklaşımı */
@variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));
 
/* media query — bu varsayılan, bildirmen gerekmez */
@variant dark (@media (prefers-color-scheme: dark));

Turbopack Uyumluluğu#

Next.js'i Turbopack ile kullanıyorsan (artık varsayılan dev bundler), v4 harika çalışır. Rust motoru Turbopack'in kendi Rust tabanlı mimarisiyle iyi uyum sağlar. Dev başlatma sürelerimi ölçtüm:

Kurulumv3 + Webpackv3 + Turbopackv4 + Turbopack
Soğuk başlangıç4,8s2,1s1,3s
HMR (CSS değişikliği)450ms180ms40ms

CSS değişiklikleri için 40ms HMR neredeyse algılanamaz. Anlık hissettiriyor.

Performans Derinlemesine: Build Hızının Ötesi#

Oxide motorunun faydaları ham build hızının ötesine geçer.

Bellek Kullanımı#

v4 önemli ölçüde daha az bellek kullanır. 847 bileşenli projemde:

Metrikv3v4
Tepe bellek (build)380MB45MB
Kararlı durum (dev)210MB28MB

Bu, belleğin kısıtlı olduğu CI/CD pipeline'ları ve aynı anda on işlem çalıştıran geliştirme makineleri için önemli.

CSS Çıktı Boyutu#

v4 biraz daha küçük CSS çıktısı üretir çünkü yeni motor tekilleştirme ve ölü kod eliminasyonunda daha iyi:

v3 çıktısı: 34,2 KB (gzipli)
v4 çıktısı: 29,8 KB (gzipli)

Kod değiştirmeden %13 azalma. Dönüştürücü değil ama bedava performans.

Tema Değerlerinin Tree Shaking'i#

v4'te bir tema değeri tanımlayıp template'lerinde hiç kullanmazsan, karşılık gelen CSS custom property yine de yayınlanır (@theme'dedir, :root değişkenlerine eşlenir). Ancak kullanılmayan değerler için utility sınıfları üretilmez. Bu v3'ün JIT davranışıyla aynı ama belirtmeye değer: CSS custom property'lerin utility kullanımı olmasa bile her zaman erişilebilir.

Belirli tema değerlerinin CSS custom property üretmesini engellemek istiyorsan @theme inline kullanabilirsin:

css
@theme inline {
  /* Bu değerler utility üretir ama CSS custom property ÜRETMEZler */
  --color-internal-debug: #ff00ff;
  --spacing-magic-number: 3.7rem;
}

Bu, CSS değişkenleri olarak açığa çıkmasını istemediğin dahili tasarım token'ları için yararlı.

İleri Düzey: Çoklu Marka İçin Tema Birleştirme#

v4'ün önemli ölçüde kolaylaştırdığı bir kalıp çoklu marka temalama. Tema değerleri CSS custom property'ler olduğu için çalışma zamanında değiştirebilirsin:

css
@import "tailwindcss";
 
@theme {
  --color-brand: var(--brand-primary, #3b82f6);
  --color-brand-light: var(--brand-light, #60a5fa);
  --color-brand-dark: var(--brand-dark, #1d4ed8);
}
 
/* Marka override'ları */
.theme-acme {
  --brand-primary: #e11d48;
  --brand-light: #fb7185;
  --brand-dark: #9f1239;
}
 
.theme-globex {
  --brand-primary: #059669;
  --brand-light: #34d399;
  --brand-dark: #047857;
}
html
<body class="theme-acme">
  <!-- tüm bg-brand, text-brand vb. Acme renkleri kullanır -->
  <div class="bg-brand text-white">Acme Corp</div>
</body>

v3'te bu özel bir plugin veya Tailwind dışında karmaşık CSS değişken kurulumu gerektiriyordu. v4'te doğal — tema CSS değişkenleri ve CSS değişkenleri kademeli. CSS-first yaklaşımın doğru hissettiren türden bir şey bu.

v3'ten Neleri Özlüyorum#

Dengeli olayım. v3'ün v4'te gerçekten özlediğim yaptığı şeyler var:

1. Programatik temalar için JavaScript config. Tek bir marka renginden JavaScript fonksiyonu kullanarak renk skaları ürettiğim bir projem vardı. v4'te bunu @theme'de yapamazsın — CSS dosyasını üreten bir build adımına ihtiyacın olur veya renkleri bir kez hesaplayıp yapıştırırsın. @config uyumluluk katmanı yardımcı olur ama uzun vadeli hikaye bu değil.

2. Lansmanda IntelliSense daha iyiydi. v3 VS Code uzantısı yıllarca cilalanmıştı. v4 IntelliSense çalışır ama erken dönemde bazı boşlukları vardı — özel @theme değerleri bazen otomatik tamamlanmıyordu ve @utility tanımları her zaman algılanmıyordu. Bu son güncellemelerle önemli ölçüde iyileşti ama belirtmeye değer.

3. Ekosistem olgunluğu. v3 etrafındaki ekosistem devasa idi. Headless UI, Radix, shadcn/ui, Flowbite, DaisyUI — her şey v3'e karşı test edilmişti. v4 desteği yayılıyor ama evrensel değil. v4 uyumluluğunu düzeltmek için bir bileşen kütüphanesine PR göndermek zorunda kaldım.

Geçiş Yapmalı mısın?#

v4 ile birkaç hafta yaşadıktan sonraki karar çerçevem:

Şimdi Geçiş Yap Eğer:#

  • Yeni proje başlıyorsan (bariz seçim — v4 ile başla)
  • Projenin minimal özel plugin'leri varsa
  • Büyük projelerde performans faydalarını istiyorsan
  • Zaten modern Tailwind kalıplarını kullanıyorsan (slash opacity, shrink-* vb.)
  • Container query'lere plugin eklemeden ihtiyacın varsa

Bekle Eğer:#

  • Henüz v4 desteklemeyen üçüncü taraf Tailwind plugin'lerine çok bağımlıysan
  • Karmaşık programatik tema yapılandırman varsa
  • Projen stabil ve aktif olarak geliştirilmiyorsa (neden dokunasın?)
  • Özellik sprint'inin ortasındaysan (sprint'ler arasında geçiş yap, sırasında değil)

Geçiş Yapma Eğer:#

  • v2 veya daha eskisindeysen (önce v3'e yükselt, stabilize et, sonra v4'ü düşün)
  • Projen birkaç ay içinde bitecekse (çalkantıya değmez)

Dürüst Görüşüm#

Yeni projeler için v4 bariz seçim. CSS-first yapılandırma daha temiz, motor dramatik olarak daha hızlı ve yeni özellikler (container query'ler, @starting-style, yeni variant'lar) gerçekten yararlı.

Mevcut projeler için aşamalı bir yaklaşım öneriyorum:

  1. Şimdi: Her yeni projeyi v4'te başlat
  2. Yakında: Küçük bir dahili projeyi v4'e dönüştürerek deney yap
  3. Hazır olduğunda: Sakin bir sprint'te görsel regresyon testiyle production projelerini geçir

Geçiş hazırlanırsan acı vermiyor. Codemod işin %80'ini halleder. Kalan %20 manuel ama anlaşılır. Orta bir proje için bir gün, büyük bir proje için iki-üç gün ayır.

Tailwind v4, Tailwind'in her zaman olması gereken şey. JavaScript yapılandırması her zaman zamanının araçlarına bir ödün vermeydi. CSS-first yapılandırma, native cascade layer'lar, Rust motoru — bunlar trend değil, framework'ün platforma yetişmesi. Web platformu iyileşti ve Tailwind v4 onunla savaşmak yerine ona yaslanıyor.

Tasarım token'larını CSS'te yazmak, CSS özellikleriyle birleştirmek ve tarayıcının kendi cascade mekanizmasının specificity'yi idare etmesine izin vermek — doğru yön bu. Buraya varmak dört major sürüm aldı ama sonuç Tailwind'in şimdiye kadarki en tutarlı sürümü.

Bir sonraki projenle başla. Arkana bakmayacaksın.

İlgili Yazılar