Design Token Mimarisi: Katmanlar, İsimlendirme ve Temalar

· 5 dk okuma · CSS

Bu sayfada

Bir projede aynı gri ton kırk farklı yerde elle yazılmışsa, o renk bir karar değil, bir tesadüftür. Rengi değiştirmek istediğinde kırk yeri tek tek bulman gerekir ve hangisinin butona, hangisinin kenarlığa ait olduğunu bilemezsin. Birini atlarsan site iki farklı gri ile kalır.

Farkı kendin dene: bir renk seç ve iki kartın nasıl değiştiğine bak.

Token olmadan

  • Button.tsxbg-orange-600
  • Checkbox.tsxbg-fuchsia-600
  • Icon.tsxtext-yellow-500
3 dosya, 3 ayrı renk

Token ile

--brand: var(--color-teal-700)

Button, Checkbox, Icon → var(--brand)

1 token, 3 bileşen

Design token'lar bu sorunu çözüyor: kararlara bir isim verip onları tek bir yerde tutuyor. Değeri orada değiştirdiğinde, o ismi kullanan her yer birlikte değişiyor. Bu yazıda bunun nasıl kurulduğunu Tailwind CSS, shadcn/ui ve GitHub'ın tasarım sistemi Primer üzerinden anlattım.

Token Nedir?

Design Tokens Community Group token'ları bir tasarım sisteminin bölünemez parçaları olarak tanımlıyor: renkler, boşluklar, yazı ölçekleri. Kısacası token, isim verilmiş bir tasarım kararı. Frontend'de bunu çoğunlukla bir CSS değişkeni olarak görürüz.

Tailwind CSS v4'te bu kararlar @theme içinde tanımlanıyor ve her biri hem bir CSS değişkeni hem de bir utility class üretiyor:

CSS
@theme {
  --color-mint-500: oklch(0.72 0.11 178);
}

Bu tanımdan sonra bg-mint-500 ve text-mint-500 kullanılabiliyor. Öndeki --color- hangi utility'lerin üretileceğini belirliyor; --spacing-* boşluk, --radius-* köşe utility'leri üretiyor.

Üç Katman

Yalnızca değişken kullanmak yetmiyor. Kırk yerde --color-neutral-900 yazıyorsan, kırk yerde gri yazmaktan pek farkı yok: butonun rengini değiştirmek istediğinde yine hangi neutral-900'ün butona ait olduğunu aramak zorundasın. Çözüm token'ları katmanlara ayırmak. Rangle ve GitHub Primer farklı isimler kullansa da ayrım aynı:

KatmanPrimer'daki adıNe anlatır?shadcn/uiPrimer
PrimitiveBaseHam değer, anlamı yok--color-neutral-900--base-size-16
SemanticFunctionalSistem genelindeki bir karar--primary--fgColor-default
ComponentComponent/patternTek bir bileşene özel karar--sidebar-primary--button-primary-bgColor-rest
Her katman bir alttakine referans verir; ham değer yalnızca en altta durur

Katmanları ayıran şey referansın tek yöne akması. Bileşen semantic token'ı kullanır, semantic token primitive'e işaret eder, ham değer yalnızca en altta yazılır. Böylece "butonun rengi" kararı tek bir isimde duruyor: --primary.

Gerçek hayatta bu şu demek: butonların rengini griden maviye çevirmek istediğinde kırk dosyayı aramıyorsun. --primary: var(--color-neutral-900) satırını var(--color-blue-600) yapman yetiyor; bg-primary kullanan her bileşen birlikte değişiyor. Yazının başındaki kırk yere dağılmış gri, burada tek bir satıra iniyor. Primer'ın token rehberi kuralı tek cümleyle koyuyor: ham değer kullanma, yalnızca semantic token kullan.

Tailwind ve shadcn/ui ile Kurmak

shadcn/ui bu modeli hazır getiriyor. Semantic token'lar :root'ta tanımlanıyor, ardından @theme inline ile Tailwind'e tanıtılıyor:

CSS
:root {
  --primary: oklch(0.205 0 0);
  --primary-foreground: oklch(0.985 0 0);
}

@theme inline {
  --color-primary: var(--primary);
  --color-primary-foreground: var(--primary-foreground);
}

Böylece bg-primary ve text-primary-foreground class'ları oluşuyor. Tailwind dokümantasyonu, başka bir değişkene referans veren theme variable'larda inline kullanılmasını öneriyor. Token'lar shadcn/ui'da zemin ve üstündeki içerik çiftleri halinde: --primary yüzeyin, --primary-foreground üstündeki yazının rengi.

Bileşenler de yalnızca bu class'ları kullanıyor:

TSX
default: "bg-primary text-primary-foreground hover:bg-primary/90"

Button hiçbir yerde bir renk değeri yazmıyor. Renk değiştiğinde bileşen dosyasına dokunmuyorsun.

Tek Satırla Bütün Siteyi Değiştirmek

Bu sitede vurgu rengi turkuaz ve tek bir token'dan geliyor:

CSS
:root {
  --brand: oklch(0.511 0.096 186.391);
  --ring: var(--brand);
}

@theme inline {
  --color-brand: var(--brand);
}

Bu değer sitede şu yerlerde kullanılıyor:

  • ana sayfadaki çağrı butonu (bg-brand),
  • menüde açık olan sayfa (text-brand),
  • link alt çizgileri (decoration-brand),
  • odak halkaları (--ring: var(--brand)),
  • metin seçimi (bg-brand/25).

Turkuazı başka bir renge çevirmek için --brand satırını değiştirmem yetiyor. Bileşenlerin hiçbirine dokunmadan bütün sitenin vurgusu birlikte değişiyor. Yazının başındaki demoda değiştirdiğin token buydu.

İsimlendirme: Görünüşe Göre Değil, Role Göre

Tek yerden değiştirebilmek için ismin de doğru olması gerekiyor. --turquoise ismi, rengi mora çevirdiğin gün yalan söylemeye başlar; --brand ise rengin ne işe yaradığını anlatır ve renk değişse de doğru kalır.

Büyük sistemlerde isimler birden fazla parçadan oluşuyor. GitHub'ın tasarım sistemi Primer, isimleri sabit bir sırayla kuruyor: önek, namespace, desen, varyant, özellik, varyant, ölçek. Varyant ya deseni ya da özelliği nitelediği için sırada iki yeri var, ama bir token yalnızca bir varyant taşıyabiliyor. Ölçek ise boyut, kalınlık ve etkileşim durumu gibi sıra bildiren değerler için.

Primer'ın isim sırası: token yalnızca ihtiyaç duyduğu parçaları taşır, ama hep aynı sırayla

Sıra sabit olunca isim tahmin edilebiliyor. button-primary-bgColor-rest'i bilen biri, hover rengini aramadan button-primary-bgColor-hover diye yazabilir.

Kaç Katman Gerekli?

Üç katman her sistem için şart değil. Rangle erken aşamadaki sistemlerde component token'ları tutmayı öneriyor, çünkü tasarım değişince kod değil yalnızca referans değişiyor. Olgun sistemlerde ise bakım yükünü azaltmak için bu katmanı kaldırmayı öneriyor. Primer üç katmanı da tutuyor; shadcn çoğunlukla iki katmanla yetiniyor ve hover rengi için ayrı token yerine hover:bg-primary/90 kullanıyor.

Pratik bir ölçüt: bir bileşen semantic token'dan gerçekten farklı bir değere ihtiyaç duyuyorsa component token aç. "Belki lazım olur" diye açılanlar, semantic token'ların kopyası olan bir listeye dönüşür.

Temalar: İsmi Değil, Değeri Değiştir

Koyu tema da aynı fikrin devamı. Bileşenlere dokunmuyorsun; .dark altında aynı isimlere yeni değerler veriyorsun:

CSS
.dark {
  --primary: oklch(0.922 0 0);
  --primary-foreground: oklch(0.205 0 0);
}
Koyu temada yalnızca semantic token'ın değeri değişir; bileşenin kullandığı class aynı kalır

Koyu temada primary açık bir renge döndüğü için foreground da koyulaşıyor; çiftler bu yüzden birlikte tanımlanıyor. Primer aynı işi <html> üzerindeki data-color-mode ve data-dark-theme attribute'larıyla yapıyor.

Özet

  • Token, isim verilmiş bir tasarım kararı. Değeri tek bir yerde durur, değiştiğinde onu kullanan her yer değişir.
  • Primitive ham değeri, semantic rolü, component tek bir bileşenin kararını tutar. Referans hep aşağı akar.
  • İsim görünüşü değil rolü anlatır.
  • Tailwind ve shadcn/ui bu yapıyı hazır getiriyor; senin işin ham değer yazmamak.
  • Temalarda isimler değil, değerler değişir.

Kaynaklar