Bir arayüzde tutarsızlığın nereden geldiğini bulmak genellikle zor değildir. Bir kartın iç boşluğu 18 piksel, öbürünün 20; bir butonun köşesi yuvarlak, öbürününki keskin; iki başlık aynı kelime ama farklı kalınlıkta. Hiçbiri tek başına hata gibi görünmez. Toplamda ise sayfa, biri çizmiş ama kimsenin yönetmediği bir karışıklık gibi durur.
Tasarım sistemleri bu karışıklığa karşı önerilen çözüm olarak anılır, ama çoğu zaman yanlış yerden başlanır. İnsanlar bileşen kütüphanesini kurar, buton ve kart çizer, sonra bir bakarlar ki renkler hâlâ yerinde yerinde hex kodlarıyla yazılmış. Oysa bir sistemin çekirdeği bileşenler değil, kararların kendisidir: hangi ölçek, hangi yarıçap, hangi vurgu rengi, hangi yazı ailesi.
Bu metinde Tailwind CSS 4 ile bu kararları nasıl tek bir yerde, @theme bloğunda tanımlayıp tüm arayüze dağıttığımızı anlatacağım. Tasarımcı ve geliştirici aynı dili konuşabilsin diye isimlendirme, ölçek ve koyu tema konularına ayrıca değineceğim.
Karar bir token'dır, değer bir sınıf
Tailwind 4 ile yapılandırma artık büyük bir JavaScript dosyasında değil, doğrudan CSS içinde yapılıyor. @theme bloğu içinde tanımladığınız her özel değişken, Tailwind tarafından bir yardımcı sınıfa dönüşür. --color-brand-600 tanımı bg-brand-600, text-brand-600 ve border-brand-600 sınıflarını üretir.
@import "tailwindcss";
@theme {
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, monospace;
--color-brand-50: oklch(97% 0.015 255);
--color-brand-600: oklch(52% 0.19 258);
--color-brand-700: oklch(45% 0.18 258);
--radius-control: 0.75rem;
--radius-surface: 1rem;
}Bu yaklaşımın gücü, kararın tek bir yerde durmasında. Markanın mavisi değiştiğinde yüz dosyayı aramak yerine --color-brand-600 satırını değiştirirsiniz. Tailwind'in üretilen tüm sınıfları bu değeri otomatik olarak kullanır.
Token ile değer arasındaki farkı ayırmak da önemli. bg-[#1d4ed8] gibi bir değer doğrudan yazılabilir, ama bu bir karar değil, bir tercih olarak kalır ve başka hiçbir yerde tekrar edilmez. Ekipte bu tür tek seferlik değerler birikmeye başladığında sistem yavaş yavaş dağılır.
Renk: az sayıda, ölçekli, anlamlı
Renk paletinde ilk hedef sayıyı sınırlamaktır. Nötr renkler, bir vurgu rengi ve bir de uyarı ailesi çoğu arayüz için yeterlidir. Her rengin 50'den 950'ye uzanan bir ölçeği olması, kontrast ihtiyacına göre doğru tonu seçmeyi kolaylaştırır.
| Rol | Ölçek | Kullanım |
|---|---|---|
| Nötr | zinc | Metin, yüzey, kenarlık |
| Vurgu | brand (mavi) | Bağlantı, birincil buton, odak |
| Başarı | emerald | Onay, tamamlandı durumu |
| Uyarı | amber | Bekleyen, dikkat gerektiren |
| Hata | rose | Doğrulama hatası, silme |
Vurgu rengini tek bir ton üzerinde tutmak da bir karardır. Birden fazla mavi kullanmak, kullanıcıya hangisinin etkileşim, hangisinin dekor olduğunu düşündürür. Bu yüzden vurgu ailesinden yalnızca birkaç ton seçilir ve kullanımları belgelenir.
Koyu tema da aynı paletin parçası olmalı. Tailwind 4'te varsayılan dark: varyantı tarayıcının sistem tercihine bakar. Sitenin kendi anahtarıyla çalışması için bu varyantı sınıf tabanlı hâle getirmek gerekir:
@custom-variant dark (&:where(.dark, .dark *));Bu tanımdan sonra dark:bg-zinc-900 gibi sınıflar yalnızca üst öğede dark sınıfı olduğunda uygulanır. Anahtarı açıp kapatmak, sayfadaki her öğenin yeniden yazılmasını gerektirmez.
İyi bir sistem, geliştiricinin yanlış kararı vermesini zorlaştıran sistemdir.
Yüzey, kontrol ve vurgu: isimler önemli
Token'lara renk adıyla değil, rolüyle isim vermek uzun vadede en çok fayda sağlayan karardır. --color-zinc-100 bir değer hakkında konuşur; --color-surface-muted ise bir amaç hakkında. Koyu temaya geçildiğinde yüzeyin açık gri olması gerekmez, ama bir yüzey olması gerekir.
Bu iki katmanlı bir yapıyla çalışılabilir. Ham ölçek (zinc, brand) bir alt katmanda kalır, anlamsal token'lar (surface, text, accent) ise onu kullanır. Tema değiştiğinde yalnızca anlamsal token'lar yeniden eşlenir.
| Anlamsal token | Açık tema | Koyu tema |
|---|---|---|
| surface | white | zinc-900 |
| surface-muted | zinc-50 | white/3 |
| text-primary | zinc-900 | white |
| text-secondary | zinc-600 | zinc-300 |
| border | zinc-200/80 | white/7 |
Bu tablo bir hatırlatma: ikincil metin için zinc-600 açık temada okunabilir kalırken, koyu temada aynı kontrast için daha açık bir ton gerekir. Renkleri birebir ters çevirmek her zaman işe yaramaz; her tema için kontrastı ayrıca ölçmek gerekir.
Ölçek disiplini: dört katlı ritim
Aralıkları 4'ün katları üzerinden kurmak, gözle görülür bir ritim yaratır. Tailwind 4'te temel boşluk birimi --spacing değişkeniyle belirlenir ve p-4, gap-6 gibi sınıflar bu birimin katları olarak hesaplanır.
Kart içi boşluk, bölümler arası boşluk ve başlık altı marj aynı ölçekten geldiğinde sayfa kendiliğinden sakinleşir. Bir kartta p-5, bir öbüründe p-6 kullanmak tek başına büyük bir fark yaratmaz; ama on sayfada yirmi farklı boşluk değeri olduğunda kullanıcı bunu hisseder.
- Kart iç boşluğu:
p-5ya dap-6, sayfa boyunca bir tane seçilir. - Bölümler arası:
py-16ilepy-24arasında iki ölçü yeterlidir. - Başlık altı marj:
mt-3ya damt-4. - Dar ekran ile geniş ekran arasındaki değişim
sm:velg:önekleriyle yapılır, sınıf sayısı artırılarak değil.
Tipografi: iki aile, net bir hiyerarşi
Yazı tipi seçimi bir ürün kararıdır. Gövde metni için okunabilirliği yüksek bir sans-serif, başlıklar için daha karakterli bir aile ve kod için monospace bir yazı tipi çoğu projede yeterlidir. Tailwind'de bunlar --font-sans ve --font-mono değişkenleriyle tanımlanır.
Hiyerarşiyi sınıf kombinasyonlarıyla değil, birkaç isimli desenle kurmak daha sağlıklıdır. Örneğin bir başlık deseni: büyük boyut, sıkı satır aralığı, hafif negatif harf aralığı. Bu deseni bir bileşen olarak yazarsanız, her başlık aynı kararları taşır.
<h2 class="text-2xl font-semibold tracking-tight text-zinc-900 sm:text-3xl dark:text-white">
Yorumlar
</h2>Monospace yazı tipi tarih, sayaç ve küçük etiketler için iyi çalışır. Ama bir paragrafın tamamı monospace olduğunda okuma hızı belirgin biçimde düşer. Bu ayrımı belgelemek, ekibin aynı hataya düşmesini engeller.
Tek seferlik değer nasıl yakalanır?
Sistem dağılmaya başladığında ilk işaret, tekrar etmeyen değerlerin çoğalmasıdır. Projede bunu kendi kendine yakalamak için basit bir arama yeterlidir: grep ile köşeli parantezli sınıfları ve sabit renk kodlarını listeleyin.
grep -rnE "\[#[0-9a-fA-F]{3,8}\]|\[[0-9]+px\]" resources/viewsÇıkan her satır için üç soru sorun. Bu değer bir tasarım kararı mı? Başka bir yerde de kullanılıyor mu? Bir token'a dönüşmeli mi? Bazı istisnalar olacaktır, örneğin bir grafik ya da bir marka logosu. Onları belgelemek yeterlidir.
- Tekrar eden değerleri bir token'a taşıyın.
- Tek seferlik istisnaları yorumla gerekçelendirin.
- Yeni bileşenlerde köşeli parantez kullanımını inceleme sürecinde işaretleyin.
Yuvarlaklık ve yüzeyler
Köşe yarıçapı, bir arayüzün karakterini belirleyen en görünür kararlardan biridir. Kontroller (butonlar, girdiler) ile yüzeyler (kartlar, paneller) farklı yarıçaplar almalıdır. Kontroller daha küçük, yüzeyler daha yumuşak olur; böylece iç içe geçen öğeler birbirini ezmez.
Bu iki değeri --radius-control ve --radius-surface olarak tanımlarsanız, bir bileşenin hangi yarıçapı kullanacağına karar vermek bir isim seçmeye indirgenir. İç içe bir kartta içteki öğenin yarıçapı, dıştakinden küçük olmalıdır; bu basit kural çoğu tutarsızlığı önler.
Tokenlardan bir kart bileşenine
Token'ların değeri, bir bileşende birlikte kullanıldıklarında ortaya çıkar. Bir içerik kartı için karar sayısı küçüktür: yüzey rengi, kenarlık, yarıçap ve iç boşluk. Bunların her biri ayrı bir token'dan gelir; bileşen yalnızca bu rolleri birleştirir.
<article class="rounded-surface border border-border bg-surface p-5 shadow-sm dark:bg-surface-dark">
<span class="font-mono text-xs uppercase tracking-[0.14em] text-text-secondary">
Gezi
</span>
<h3 class="mt-3 text-lg font-semibold text-text-primary">
Kapadokya'da sabah ışığı
</h3>
</article>Bu örnekte yalnızca anlamsal adlar var: bg-surface, border-border, text-text-secondary. Kartın koyu temada nasıl görüneceğine tasarımcı ve geliştirici birlikte karar verdiğinde, bu adlar bir sözleşme hâline gelir. Yeni bir kart yazıldığında, aynı adlar kullanılır ve sonuç otomatik olarak tutarlı kalır.
Bu yaklaşımın bir yan etkisi de inceleme kolaylığıdır. Bir kartta text-zinc-500 görüldüğünde, bunun neden seçildiğini sormak gerekir. text-text-secondary görüldüğünde ise sorunun cevabı zaten isimdedir.
Hareket ve geçiş token'ları
Hareket de bir tasarım kararıdır. Hızlı bir geçiş ile yavaş bir süzülme aynı arayüzde farklı duygular yaratır. Tailwind 4'te süre, yumuşatma ve animasyon değerleri de @theme içinde tanımlanabilir:
@theme {
--ease-soft: cubic-bezier(0.22, 1, 0.36, 1);
--duration-quick: 150ms;
--animate-fade-up: fade-up 0.4s var(--ease-soft) both;
@keyframes fade-up {
from { opacity: 0; transform: translateY(0.5rem); }
to { opacity: 1; transform: none; }
}
}Tanımlanan animasyonu sınıf olarak kullanmak yeterlidir: motion-safe:animate-fade-up. motion-safe: öneki, kullanıcı hareket azaltma tercihini açtıysa animasyonu uygulamaz. Bu küçük önek, erişilebilirlik kuralını her kullanımda kendiliğinden korur.
- Hızlı geçişler (150 ms civarı) butonlar ve küçük durum değişimleri için.
- Orta geçişler (300 ms civarı) açılır paneller ve modallar için.
- Yavaş ve süzülen geçişler yalnızca sayfa girişlerinde, ölçülü biçimde.
Koyu tema anahtarını bir veri özniteliğiyle bağlamak
Koyu temayı bir sınıfla değil, belgenin kökündeki bir veri özniteliğiyle yönetmek de mümkündür. Bu yöntem, sunucudan gelen tercihin ilk boyamadan önce uygulanmasını kolaylaştırır ve JavaScript'in yüklenmesini beklemez.
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));Bu tanımla <html data-theme="dark"> olduğunda tüm dark: sınıfları devreye girer. İlk yükleme sırasında tercihi okuyup öznitelik eklemek için küçük bir betik, sayfanın başına konulabilir. Bu betiğin içine, tercih okunamadığında varsayılan temaya düşen bir koruma eklemek de önemlidir; tarayıcı depolama alanına erişim her zaman mümkün olmayabilir.
Ekipte sahiplik ve dokümantasyon
Bir token'ın kim tarafından yönetildiği belli olmazsa, zamanla çelişkili değerler birikir. Her anlamsal token için bir sahip belirlemek bu sorunu önler. Sahip, token'ın amacını, kabul edilen değerlerini ve değişiklik sürecini belgeler.
| Token | Amaç | Sahip | Değişiklik kuralı |
|---|---|---|---|
--color-brand-600 | Birincil eylem ve bağlantı | Tasarım | Kontrast ölçümü ile birlikte |
--color-surface | Kart ve panel yüzeyi | Tasarım | Her iki temada da onaylanmalı |
--radius-control | Buton ve girdi köşesi | Geliştirme | Tüm kontroller birlikte değişir |
--spacing | Temel boşluk birimi | Geliştirme | Tüm sayfalar kontrol edilir |
Tablo tek başına yeterli değildir; bir değişiklik günlüğü de tutulmalıdır. Yeni bir renk, bir yarıçap ya da bir yazı tipi eklendiğinde kısa bir not düşmek, bir yıl sonra neden var olduğunu açıklar.
Sık yapılan hatalar
İlk hata, bir rolü başka bir rol için kullanmaktır. Marka mavisi olan brand-600, hata mesajında da kullanılırsa, kullanıcı bir bağlantı ile bir hatayı ayırt edemez. Hata için ayrı bir anlamsal token tanımlamak, bu karışıklığı ortadan kaldırır.
İkinci hata, token sayısını gereksiz yere artırmaktır. Her tonu bir token yapmak, sistemi okunmaz hâle getirir. Kullanılmayan token'ları düzenli olarak temizlemek, sistemi sade tutmanın en etkili yoludur.
Üçüncü hata, koyu temayı en sona bırakmaktır. Açık tema bittikten sonra koyu tema eklenirse, kontrast sorunları ve ters çevrilmiş anlamlar çoğu zaman yeniden tasarım gerektirir. Anlamsal token'ları baştan iki tema için birlikte tanımlamak, bu maliyeti en aza indirir.
Sonuç: tutarlılık bir süreçtir
Tailwind 4 ile tasarım tokenları yazmak zor değil. Zor olan, her yeni bileşende aynı kararları yeniden vermek yerine mevcut token'ları kullanma alışkanlığını korumak. Bunun için dokümantasyon, küçük bir denetim komutu ve inceleme sırasında sorulan birkaç soru yeterlidir.
Başlangıç için kısa bir liste:
- Renk, yarıçap, yazı tipi ve temel boşluk için
@themeiçinde tek bir kaynak tutun. - Anlamsal token'ları ham ölçeğin üstüne kurun ve tema değişimini onlardan yönetin.
- Koyu temayı sınıf tabanlı varyantla bağlayın ve her iki temada kontrastı ölçün.
- Köşeli parantezli değerleri düzenli aralıklarla tarayın.
Bir sistem bir kez kurulur, ama her gün korunur. Token'lar bunun en ucuz yolu, çünkü karar bir kez verilir ve herkes onu kullanır.