CSS değişkenleri, resmî adıyla özel özellikler (custom properties), adı iki tire ile başlayan ve bir değeri saklayan özelliklerdir: --ana-renk: #1166ff; gibi. Saklanan değer başka bir bildirimde var(--ana-renk) yazılarak kullanılır. Aynı rengi, boşluğu ya da gölgeyi stil dosyasının onlarca yerinde tekrar yazmak yerine tek bir yerde tanımlar, gerektiğinde tek satırı değiştirerek bütün siteyi güncellersiniz.

Ancak CSS değişkenleri programlama dillerindeki değişkenlere tam olarak benzemez. Cascade'e katılırlar, ebeveynden çocuğa kalıtılırlar, geçersiz bir değer aldıklarında beklenmedik biçimde davranabilirler. Bu yazıda tanımlama kurallarını, var() fonksiyonunun yedek değer mantığını, kalıtım ve kapsamı, hesaplanan değer zamanında geçersizlik kavramını ve sık yapılan hataları MDN belgelerine dayanarak anlatıyoruz. Değişkenleri türlü ve animasyona uygun hâle getiren @property kuralı ayrı bir yazının konusu: CSS @property ile kayıtlı özel özellikler.

Özel özellik nasıl tanımlanır?

Özel özellik, sıradan bir CSS bildirimi gibi bir kural bloğunun içine yazılır. Adı -- ile başlar, iki noktadan sonra değeri gelir. MDN değeri <declaration-value> olarak tanımlar: izin verilmeyen bir belirteç içermediği sürece bir ya da daha fazla belirteçten oluşan herhangi bir dizi. Yani bir değişkene tek bir anahtar kelime, bir renk ya da birden çok parçadan oluşan bir gölge değeri koyabilirsiniz.

css
:root {
  --ana-renk: #1166ff;
  --vurgu-renk: #ffff77;
  --kart-golgesi: 3px 6px rgb(20 32 54);
  --hizalama: left;
}

.kart {
  color: var(--ana-renk);
  box-shadow: var(--kart-golgesi);
  text-align: var(--hizalama);
}

Burada iki önemli kural var. Birincisi, özel özellik adları büyük-küçük harfe duyarlıdır: --ana-renk ile --Ana-renk iki ayrı özelliktir. İkincisi, değişkeni tanımladığınız seçici onun kapsamını belirler. Değişken yalnızca seçicinin eşleştiği öğelerde ve onların alt öğelerinde geçerlidir. Bu yüzden genel kullanım için değişkenleri :root sözde sınıfında tanımlamak yaygın bir alışkanlıktır. HTML'de :root, <html> öğesini seçer; html seçicisiyle aynı öğeyi hedefler ama özgüllüğü daha yüksektir (0-1-0, html için 0-0-1).

Her değişkenin :root üzerinde olması gerekmez. MDN, kapsamı bilinçli olarak daraltmak için geçerli nedenleriniz olabileceğini hatırlatır. Örneğin yalnızca bir bileşene ait ayarları o bileşenin kök seçicisinde tanımlamak, değişkenlerin sayfanın başka yerlerine sızmasını önler ve kodu okuyan kişiye değişkenin nerede kullanıldığını gösterir.

MDN, özel özelliklerin iki ayrı faydası olduğunu vurgular. İlki bakım kolaylığıdır: Büyük sitelerde aynı renk yüzlerce yerde tekrarlanabilir ve bu rengi değiştirmek bütün dosyalarda arama ve değiştirme yapmayı gerektirir; değişkenle değer tek bir yerde tanımlanır. İkincisi anlamdır: --ana-metin-rengi gibi bir ad, #00ff00 gibi onaltılık bir koddan çok daha kolay anlaşılır, özellikle aynı renk farklı bağlamlarda kullanılıyorsa. Adı değerin ne olduğuna göre değil, ne işe yaradığına göre seçmek (örneğin --mavi yerine --renk-birincil) ileride rengi değiştirdiğinizde adın yanıltıcı hâle gelmesini önler.

var() fonksiyonu ve yedek değer

var() fonksiyonu ve yedek değer: İlk virgülden sonrası tamamen yedek değerdir., Boş yedek geçerlidir., İç içe yedeklerin bir maliyeti vardır.

var(), bir özel özelliğin değerini başka bir özelliğin değerinin yerine (ya da bir parçasına) yerleştirir. İlk argüman özel özelliğin adıdır; isteğe bağlı ikinci argüman ise yedek değerdir (fallback). MDN önemli bir sınır koyar: var() yalnızca özellik değerlerinde kullanılabilir. Özellik adlarında, seçicilerde, medya sorgularında ya da kapsayıcı sorgularında çalışmaz.

css
.baslik {
  /* --baslik-rengi tanımlı değilse mavi kullanılır */
  color: var(--baslik-rengi, blue);
}

.metin {
  /* --metin-rengi de --yedek-renk de yoksa siyah */
  color: var(--metin-rengi, var(--yedek-renk, black));
}

.bosluk {
  /* --ek-bosluk yoksa var() yerine hiçbir şey konur */
  margin: 1rem var(--ek-bosluk,);
}

Yedek değerle ilgili üç ayrıntıyı bilmek, saatlerce hata ayıklamanızı önler:

  • İlk virgülden sonrası tamamen yedek değerdir. var(--renk, red, blue) yazdığınızda yedek değer red, blue olur. Bu yüzden birden fazla yedek tanımlamak için virgülle sıralamak değil, iç içe var() yazmak gerekir: var(--a, var(--b, pink)). MDN'nin rehberindeki var(--my-var, --my-background, pink) örneği bu nedenle geçersiz bir yedek üretir.
  • Boş yedek geçerlidir. var(--a,), değişken kullanılamıyorsa var() ifadesinin yerine hiçbir şey konmasını söyler. Bir değere isteğe bağlı ek parça eklerken kullanışlıdır.
  • İç içe yedeklerin bir maliyeti vardır. MDN, iç içe değişkenlerin ayrıştırılmasının daha fazla zaman aldığını ve bu yöntemi kullanırken performans etkisini göz önünde bulundurmanız gerektiğini belirtir.

Peki yedek değer tam olarak ne zaman devreye girer? MDN'ye göre başvurulan özel özelliğin kullanılabilir bir değeri olmadığında; belirtimin deyişiyle "garantili geçersiz değer" taşıdığında. Pratikte bu üç durumda olur: Özelliğe öğeye uygulanan hiçbir kuralda değer verilmemişse (ve @property ile initial-value tanımlanarak kaydedilmemişse), özellik initial anahtar kelimesine ayarlanmışsa (kayıtlı değilse) ya da değeri örneğin iki değişkenin birbirine döngüsel olarak başvurması yüzünden hesaplanan değer zamanında geçersizse.

Diğer genel anahtar kelimeler (inherit, unset, revert, revert-layer) özel özelliklerde de her özellikte olduğu gibi çalışır ve değişkeni kalıtılan ya da daha önce cascade'de belirlenmiş bir değere çözebilir; bu durumda yedek değer devreye girmeyebilir. Bir de şunu unutmayın: Yedek değerler tarayıcı uyumluluğu sorununu çözmek için değildir. Özel özellikleri hiç desteklemeyen bir tarayıcıda yedek değer de işe yaramaz; yedek, değişkenin tanımlanmadığı ya da geçersiz olduğu durumlar içindir.

Kalıtım ve kapsam: değişken nereye kadar ulaşır?

Kalıtım ve kapsam: değişken nereye kadar ulaşır?

-- sözdizimiyle tanımlanan bir özel özellik değerini her zaman ebeveyninden kalıtır. MDN'nin örneğini Türkçeleştirerek inceleyelim: Tüm div öğeleri arka plan rengini var(--kutu-rengi) ile alıyor, değişken ise yalnızca iki sınıfta tanımlı.

html
<div class="bir">
  <div class="iki">
    <div class="uc"></div>
    <div class="dort"></div>
  </div>
</div>
css
div {
  background-color: var(--kutu-rengi);
}

.iki {
  --kutu-rengi: teal;
}

.uc {
  --kutu-rengi: pink;
}

/* sonuç:
   .bir  : değişken tanımsız, var() geçersiz değere çözülür
           (background-color unset gibi davranır)
   .iki  : teal
   .uc   : pink (kendi tanımı)
   .dort : teal (ebeveyni .iki öğesinden kalıtıldı) */

Bu örnek değişkenlerin programlama dillerindeki değişkenlerden nasıl ayrıldığını gösterir. MDN'nin vurguladığı gibi değer bir yerde saklanıp başka yerde yeniden kullanılmaz; ihtiyaç duyulduğu öğede hesaplanır. Bir kardeş öğede tanımladığınız değişkeni, o kardeşin dışında kalan bir öğenin kuralından okuyamazsınız. Değişken yalnızca eşleşen öğe ve onun alt öğeleri için ayarlanmıştır. Kalıtımı kapatmak isterseniz @property kuralında inherits: false vermeniz gerekir; bunun ayrıntıları @property yazısında anlatılıyor.

Değişkenler cascade'e de katılır: Aynı öğe için birden çok kuralda aynı değişken tanımlanmışsa hangisinin kazanacağına, sıradan özelliklerde olduğu gibi kaynak, özgüllük ve sıra karar verir. Bu konunun genel kuralları cascade ve kalıtım yazısında anlatılıyor. Değişkenin tanımlandığı yerin kullanıldığı yerden önce ya da sonra gelmesinin ise önemi yoktur. MDN'nin örneklerine göre body kuralında kullanılan bir değişken stil dosyasında daha aşağıda :root içinde tanımlansa da, hatta başka bir CSS dosyasında tanımlansa da değer uygulanır; çünkü değer, cascade tamamlandıktan sonra öğe üzerinde çözülür.

Hesaplanan değer zamanında geçersizlik

Değişkenlerin en şaşırtıcı davranışı geçersiz değerlerle ilgilidir. Sıradan bir bildirimde geçersiz bir değer yazarsanız, örneğin color: 16px;, tarayıcı bu bildirimi ayrıştırma sırasında atar ve öğe sanki bildirim hiç yazılmamış gibi daha önceki geçerli değeri kullanır. Özel özelliklerde ise tarayıcı değeri ayrıştırırken onun nerede kullanılacağını bilemez; bu yüzden neredeyse her değeri geçerli kabul etmek zorundadır. Sorun ancak değer var() ile bir özelliğe yerleştirildiğinde ortaya çıkar. Bu duruma hesaplanan değer zamanında geçersizlik denir.

css
p {
  color: blue;
}

/* 1. durum: sıradan geçersiz değer */
.p1 {
  color: 16px;
  /* bildirim atılır, metin mavi kalır */
}

/* 2. durum: değişken üzerinden geçersiz değer */
.p2 {
  --metin-rengi: 16px;
  color: var(--metin-rengi);
  /* mavi kalmaz; color unset gibi davranır */
}

İkinci durumda ne olduğuna adım adım bakalım. MDN'ye göre geçersiz bir var() yerleştirmesiyle karşılaşan tarayıcı, özelliğin kalıtılan ya da başlangıç değerini kullanır; yani özellik unset verilmiş gibi davranır. color kalıtılan bir özellik olduğu için tarayıcı önce ebeveynde ayarlanmış bir color değeri arar. Ebeveynlerden hiçbiri renk ayarlamamışsa özelliğin başlangıç değerine düşülür ve metin varsayılan siyah renkte görünür. Önceki color: blue kuralı kurtarıcı olmaz, çünkü MDN'nin açıkladığı gibi tarayıcı değerin geçersiz olduğunu fark ettiğinde diğer cascade değerlerini zaten bırakmıştır.

Bu davranış iki pratik sonuç doğurur. Birincisi, değişkenle verilen bir değer geçersizse eski "yedek bildirim" yöntemi işe yaramaz; aynı özelliği iki kez yazıp ikincisinin başarısız olunca birincisine düşmesini bekleyemezsiniz. İkincisi, değişkene tür kısıtı koymak istiyorsanız @property ile kayıt yapıp bir initial-value tanımlayabilirsiniz; MDN'nin örneğinde kayıtlı ve <color> türünde tanımlanmış bir değişkene 16px verildiğinde, tanımlanan başlangıç rengi kullanılır.

Değişkenlerle hesaplama

Özel özellikler calc() ile birlikte çok güçlüdür. MDN'nin calc() sayfasındaki örnekte bir değişken başka bir değişkenden türetilir:

css
.kutu {
  --genislik-a: 100px;
  --genislik-b: calc(var(--genislik-a) / 2);
  --genislik-c: calc(var(--genislik-b) / 2);
  width: var(--genislik-c);
  /* açılım: calc(calc(100px / 2) / 2)
     iç calc() parantez sayılır: (100px / 2) / 2
     100px / 2 = 50px, 50px / 2 = 25px
     sonuç: width 25px */
}

İç içe calc() ifadeleri basit parantez gibi düzleştirilir, bu yüzden değişkenleri zincirlemek güvenlidir. Birimsiz bir sayıyı uzunluğa çevirmeniz gerekirse, calc() sayfasının belirttiği gibi birimi sayının yanına yapıştıramazsınız (calc(100 / 4)% geçersizdir); bunun yerine birimsiz değeri birimli bir değerle çarparsınız. Matematik fonksiyonlarının tüm kuralları için calc(), min(), max() ve clamp() yazısına bakın.

css
:root {
  --olcek: 3;
}

.bosluklu {
  padding: calc(var(--olcek) * 0.5rem);
  /* 3 * 0.5rem = 1.5rem */
}

Uygulamalı mini örnek: tema ve bileşen değişkenleri

Gerçek projelerde değişkenler genellikle iki katmanda kullanılır: Sitenin genel renk ve boşluk değerlerini tutan küresel değişkenler ve bir bileşenin bu genel değerleri kendi ihtiyacına göre yeniden adlandırdığı yerel değişkenler. Aşağıdaki örnekte buton bileşeni kendi değişkenlerini küresel değerlerden alıyor; bir varyant yalnızca yerel değişkeni değiştirerek farklı görünüme kavuşuyor.

css
:root {
  --renk-birincil: #1166ff;
  --renk-tehlike: #cc2200;
  --bosluk: 0.75rem;
}

.buton {
  --buton-zemin: var(--renk-birincil);
  --buton-dolgu: var(--bosluk);
  background-color: var(--buton-zemin);
  padding: var(--buton-dolgu) calc(var(--buton-dolgu) * 2);
  color: white;
}

.buton.tehlike {
  --buton-zemin: var(--renk-tehlike);
  /* yalnızca değişken değişti, diğer bildirimler aynen kaldı */
}

.yogun-alan {
  --bosluk: 0.5rem;
  /* bu alanın içindeki her buton daha az dolgu alır */
}

Son kurala dikkat edin: .yogun-alan içinde --bosluk değiştiğinde, o alanın içindeki butonlar kalıtım sayesinde yeni değeri görür; çünkü .buton kuralındaki var(--bosluk) her butonun kendi konumunda çözülür. Aynı yaklaşım karanlık tema için de kullanılır: Renk değişkenlerini bir medya sorgusu ya da bir sınıf içinde yeniden tanımlamak bütün temayı değiştirir. Bunun ayrıntıları dark mode ve prefers-color-scheme yazısında. Değişkenin değerini betikten okumak isterseniz getComputedStyle(oge).getPropertyValue("--bosluk") sıradan özelliklerde olduğu gibi çalışır.

Sık yapılan hatalar

Sık yapılan hatalar: Değişkeni medya sorgusunda kullanmaya çalışmak, Seçici ya da özellik adı olarak kullanmak, Birden fazla yedeği virgülle sıralamak, Büyük-küçük harf karışıklığı
  • Değişkeni medya sorgusunda kullanmaya çalışmak: @media (width >= var(--kirilma)) gibi bir yazım çalışmaz; MDN, değişkenlerin medya ve kapsayıcı sorgularının içinde kullanılamadığını açıkça belirtir. Değişkeni sorgu koşulunda değil, sorgunun içindeki bildirimlerde değiştirin.
  • Seçici ya da özellik adı olarak kullanmak: var() yalnızca değerlerde çalışır.
  • Birden fazla yedeği virgülle sıralamak: İlk virgülden sonrası tek bir yedek değer sayılır; zincirleme yedek için iç içe var() yazın.
  • Büyük-küçük harf karışıklığı: --AnaRenk tanımlayıp var(--anarenk) kullanmak, tanımsız bir değişkene başvurmak demektir.
  • Geçersiz değerde eski değere düşmeyi beklemek: Değişken üzerinden gelen geçersiz değer özelliği unset yapar; önceki kural geri gelmez.
  • Değişkenin kardeş öğelere geçeceğini sanmak: Değişken yalnızca tanımlandığı öğeye ve onun alt ağacına kalıtılır.
DurumSonuç
Değişken tanımlı ve geçerliDeğer yerleştirilir
Değişken tanımsız, yedek varYedek değer kullanılır
Değişken tanımsız, yedek yokÖzellik unset gibi davranır
Değişken var ama özellik için geçersizÖzellik unset gibi davranır
Değişken initial olarak ayarlanmış (kayıtsız)Yedek varsa yedek kullanılır
Kayıtlı ve initial-value tanımlı, değer verilmemişinitial-value kullanılır, yedek kullanılmaz

Sık Sorulan Sorular

Bir değişken başka bir değişkenden türetilebilir mi?

Evet. Bir özel özelliğin değeri içinde var() ve calc() kullanabilirsiniz: --genislik-b: calc(var(--genislik-a) / 2); gibi. Değerler öğe üzerinde çözüldüğünde iç içe calc() ifadeleri parantez gibi düzleştirilir. Yalnızca iki değişkenin birbirine döngüsel olarak başvurmamasına dikkat edin; MDN, döngüsel bağımlılığın değeri hesaplanan değer zamanında geçersiz kıldığını belirtir.

Değişkeni başka bir CSS dosyasında tanımlarsam çalışır mı?

Evet. MDN'nin örneğinde değişken 1.css dosyasında kullanılıyor, 2.css dosyasında :root üzerinde tanımlanıyor ve sayfa iki dosyayı da yüklediğinde değer uygulanıyor. Aynı şekilde değişkenin aynı dosyada kullanıldığı kuraldan daha aşağıda tanımlanması da sorun değildir. Önemli olan, sayfaya yüklenen stiller arasında değişkenin öğeye uygulanan bir kuralda tanımlı olmasıdır. Dosyalar arasında aynı değişken iki kez tanımlanmışsa hangisinin kazanacağına olağan cascade kuralları karar verir.

Değişkeni neden :root üzerinde tanımlıyoruz?

:root belgenin kök öğesini seçtiği ve özel özellikler kalıtıldığı için orada tanımlanan değişken sayfadaki her öğeden okunabilir. Bu bir zorunluluk değil, bir alışkanlıktır; yalnızca bir bileşende kullanılacak değişkenleri o bileşenin seçicisinde tanımlamak kapsamı daraltır.

Tanımsız bir değişken kullanırsam ne olur?

Yedek değer verdiyseniz o kullanılır. Vermediyseniz var() geçersiz bir değere çözülür ve özellik unset verilmiş gibi davranır: kalıtılan bir özellikse ebeveynin değerini, değilse başlangıç değerini alır.

Değişkenler animasyonla yumuşak geçiş yapabilir mi?

-- ile tanımlanan kayıtsız bir değişkenin değeri tarayıcı için türü belli olmayan bir belirteç dizisidir. Türü belirli, başlangıç değeri olan ve kalıtımı kontrol edilen bir değişken için @property kaydı gerekir; animasyon senaryoları @property yazısında anlatılıyor.

Yedek değer eski tarayıcılar için çözüm müdür?

Hayır. MDN bunu özellikle not eder: Yedek değerler, özel özellikleri desteklemeyen tarayıcılardaki uyumluluk sorununu çözmez, çünkü o tarayıcı var() fonksiyonunu da anlamaz. Yedek, özellikleri destekleyen bir tarayıcıda değişkenin henüz tanımlanmadığı ya da geçersiz olduğu durumlar içindir. Tarayıcı desteği için MDN sayfasındaki uyumluluk tablosuna bakın.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: CSS öğrenme rehberi · CSS @property: kayıtlı özel özellikler · CSS cascade ve kalıtım · CSS calc(), min(), max(), clamp() · CSS dark mode ve prefers-color-scheme · CSS if() ve @function