CSS @property kuralı bir özel özelliği (custom property, CSS değişkeni) tarayıcıya kaydeder: değişkenin hangi türde değer alabileceğini (syntax), kalıtılıp kalıtılmayacağını (inherits) ve başlangıç değerini (initial-value) belirlersiniz. Kayıtlı bir değişken artık sıradan bir metin parçası değil, türü bilinen bir değerdir. Bu sayede yanlış türde değer atandığında öngörülebilir biçimde başlangıç değerine döner ve en önemlisi, geçiş ile animasyonlarda adım adım (yumuşak) değişebilir.

Bu yazı, özel özellikleri ve var() fonksiyonunu bildiğinizi varsayar; temel kullanım için önce CSS değişkenleri ve var() yazısını okuyabilirsiniz. Burada üç tanımlayıcıyı, kuralın geçerlilik şartlarını, geçersiz değerlerin nasıl ele alındığını ve kayıtlı değişkenlerle animasyonu adım adım inceliyoruz.

Neden bir değişkeni kaydetmek gerekir?

Sıradan bir özel özellik herhangi bir değeri kabul eder. :root üzerinde --ana-renk: #bada55 tanımlayıp başka bir yerde aynı değişkene 45deg atamanızı hiçbir şey engellemez. Sorun, değişken kullanıldığı yerde ortaya çıkar:

css
:root {
  --theme-color: 45deg;
}

body {
  background-color: var(--theme-color);
}
/* sonuç: 45deg bir renk değildir. Bildirim yok sayılmaz;
   background-color kalıtılmayan bir özellik olduğu için
   başlangıç değeri transparent kullanılır */

MDN bu durumu hata işleme rehberinde anlatır: var() ile yapılan yer değiştirme geçersiz bir değer üretirse bildirim atılmaz, özellik kalıtılan bir özellikse üst öğeden kalıtılan değeri, değilse başlangıç değerini alır. Yani beklediğiniz renk yerine sessizce saydam bir zemin elde edersiniz. @property ile değişkenin türünü belirlediğinizde ise yanlış türdeki atama değişkenin kendisinde reddedilir ve değişken kendi kayıtlı başlangıç değerine döner.

MDN'in kayıt rehberi kaydetmeyi özellikle üç durumda önerir: değişkeni animasyonla değiştirecekseniz, tür denetimi istiyorsanız ve kalıtım davranışının öngörülebilir olmasını istiyorsanız. Bir CSS çatısı (framework) yazıyorsanız değişkenlerinizi kaydetmek, kullanıcıların onlara yanlış türde değer atamasını önler ve her zaman bir varsayılan değer olmasını sağlar.

Sözdizimi ve üç tanımlayıcı

Sözdizimi ve üç tanımlayıcı: syntax, inherits, initial-value
css
@property --rotation {
  syntax: "<angle>";
  inherits: false;
  initial-value: 45deg;
}

@property --defaultSize {
  syntax: "<length> | <percentage>";
  inherits: true;
  initial-value: 200px;
}

Özellik adı iki tire ile başlayan bir <dashed-ident> olmalıdır ve büyük-küçük harfe duyarlıdır; --rotation ile --Rotation farklı özelliklerdir. Gövdede üç tanımlayıcı kullanılır:

TanımlayıcıNe belirlerZorunlu mu
syntaxKabul edilen değer türleri (tırnak içinde bir metin)Evet
inheritsDeğerin varsayılan olarak alt öğelere kalıtılıp kalıtılmayacağı (true veya false)Evet
initial-valueBaşka değer yokken kullanılacak başlangıç değerisyntax "*" değilse evet

Kuralın geçerli olma şartları

@property kuralının geçersiz sayılıp tamamen yok sayılması, sık karşılaşılan ve fark edilmesi zor bir sorundur. MDN'deki şartları tek tek sıralayalım:

  • syntax ve inherits tanımlayıcılarının ikisi de bulunmalıdır. Biri eksikse bütün kural geçersizdir.
  • syntax değeri bir metindir ve tırnak içinde yazılmalıdır: syntax: "<color>"; doğru, tırnaksız yazım yanlıştır.
  • initial-value yalnızca syntax evrensel tanım olan "*" ise atlanabilir. Başka bir sözdiziminde gerekli olup eksikse bütün kural geçersizdir.
  • Evrensel olmayan bir sözdiziminde initial-value hesaplama açısından bağımsız (computationally independent) olmalıdır: 10px geçerlidir, 2in de geçerlidir çünkü 1in her zaman 96px'e eşittir; ama 3em geçersizdir, çünkü em üst öğenin yazı tipi boyutuna bağlıdır.
  • Bilinmeyen tanımlayıcılar yok sayılır ama kuralı geçersiz kılmaz.
  • Aynı adla birden fazla geçerli @property varsa stil dosyasında sonra gelen kazanır.

Aynı adın hem CSS'te @property ile hem de JavaScript'te CSS.registerProperty() ile kaydedilmesi durumunda JavaScript kaydı kazanır. JavaScript tarafı bu serinin kapsamı dışındadır; MDN, iki yöntemin eşdeğer olduğunu belirtir.

syntax tanımlayıcısı: hangi türler kabul edilir?

syntax metni bir veya daha fazla bileşen adından oluşur. Bileşen adı açılı parantezle yazılan bir veri türü (<length> gibi) ya da açılı parantezsiz bir anahtar sözcük (auto gibi) olabilir. Desteklenen veri türleri şunlardır: <angle>, <color>, <custom-ident>, <image>, <integer>, <length>, <length-percentage>, <number>, <percentage>, <resolution>, <string>, <time>, <transform-function>, <transform-list> ve <url>.

css
syntax: "<color>";                 /* tek tür */
syntax: "<length> | <percentage>";   /* biri ya da öteki */
syntax: "<color>+";                  /* boşlukla ayrılmış liste */
syntax: "<length>#";                 /* virgülle ayrılmış liste */
syntax: "small | medium | large";    /* anahtar sözcükler */
syntax: "<length> | auto";           /* tür ve anahtar sözcük */
syntax: "*";                         /* her geçerli değer */

+ çarpanı boşlukla ayrılmış bir listeyi, # çarpanı virgülle ayrılmış bir listeyi, | birleştiricisi ise "ya da" koşulunu ifade eder. <transform-list>, <transform-function>+ yazımına eşdeğerdir. <length-percentage> ise uzunluk, yüzde ve ikisini birleştiren calc() ifadelerini kabul eder. Evrensel tanım * herhangi bir geçerli değeri kabul eder ama çarpanlarla ya da başka bileşenlerle birleştirilemez.

inherits: kalıtımı siz seçersiniz

Kaydedilmemiş bütün özel özellikler varsayılan olarak kalıtılır. inherits: false yazdığınızda değişken, değer verilmeyen alt öğelerde üst öğenin değerini değil, kendi initial-value değerini kullanır. MDN, bunun performans açısından da bir faydası olduğunu belirtir: kalıtılmayan kayıtlı bir değişken değiştiğinde tarayıcının alt öğeleri yeniden değerlendirmesi gerekmez, stil yeniden hesaplamasının kapsamı daralır.

CSS'in genel anahtar sözcükleri olan initial, inherit, unset, revert ve revert-layer, syntax ne olursa olsun her kayıtlı değişken için geçerlidir. Böylece kalıtılmayan bir değişkende bile inherit yazarak üst öğenin değerini açıkça alabilirsiniz. Bu anahtar sözcüklerin genel anlamı için cascade ve kalıtım yazısına bakabilirsiniz.

Çalışılmış örnek: geçerli ve geçersiz değerler

Çalışılmış örnek: geçerli ve geçersiz değerler: Hiçbir değişken atanmamış. --item-size kalıtıldığı için kapsayıcıdaki 20% değerini alır. --item-color..., --item-size: initial...

MDN'in kayıt rehberindeki örneği CSS ile yazalım. İki değişken kaydediyoruz: kalıtılan bir boyut ve kalıtılmayan bir renk. Ardından kapsayıcıya ve üç öğeye farklı değerler veriyoruz:

css
@property --item-size {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 40%;
}

@property --item-color {
  syntax: "<color>";
  inherits: false;
  initial-value: aqua;
}

.container {
  --item-size: 20%;
  --item-color: orange;
}

.two {
  --item-size: initial;
  --item-color: inherit;
}

.three {
  --item-size: 1000px;
  --item-color: xyz;
}

.item {
  width: var(--item-size);
  height: var(--item-size);
  background-color: var(--item-color);
}
html
<div class="container">
  <div class="item one">Bir</div>
  <div class="item two">İki</div>
  <div class="item three">Üç</div>
</div>

Şimdi her öğeyi adım adım çözelim:

  • Bir: Hiçbir değişken atanmamış. --item-size kalıtıldığı için kapsayıcıdaki 20% değerini alır. --item-color kalıtılmadığı için kapsayıcıdaki orange dikkate alınmaz, başlangıç değeri aqua kullanılır. Sonuç: %20 boyutunda, aqua renkli kutu.
  • İki: --item-size: initial kayıtlı başlangıç değeri olan 40%'ı verir. --item-color: inherit ise kalıtılmayan değişkeni açıkça kalıtır ve orange olur. Sonuç: %40 boyutunda, turuncu kutu.
  • Üç: 1000px bir uzunluktur, ama kayıt yalnızca <percentage> kabul ettiği için bildirim geçersizdir; değişken kalıtılan olduğundan kapsayıcının 20% değeri kullanılır. xyz geçerli bir renk değildir; değişken kalıtılmadığından aqua kullanılır. Sonuç: %20 boyutunda, aqua renkli kutu.

Buradaki ince nokta şudur: MDN'e göre kayıtlı özellikler ayrıştırma (parse) sırasında değil, değer hesaplanırken doğrulanır. Bu yüzden geçerli bir bildirimden sonra gelen geçersiz bir bildirim, önceki geçerli değere geri düşmez; doğrudan kayıtlı varsayılan davranışa, yani kalıtılan değere ya da başlangıç değerine döner. Ayrıca geçersiz değer, öğenin özelliklerini incelerken geçersiz olarak görünmeyebilir. Bu, sıradan CSS bildirimlerinin "son geçerli değer kazanır" davranışından farklıdır.

Kayıtlı değişkenlerle animasyon

Kaydedilmemiş özel özellikler tarayıcı için yalnızca metindir; iki metin arasında ara değer hesaplanamadığı için bu değişkenler animasyonda kesikli (discrete) olarak, yani bir anda atlayarak değişir. Kayıtlı bir değişkenin türü bilindiği için tarayıcı iki değer arasında ara değerler hesaplayabilir ve değişken yumuşak biçimde canlandırılabilir. MDN'in ilerleme çubuğu örneği bunu gösterir:

css
@property --progress {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 25%;
}

.bar {
  display: inline-block;
  --progress: 25%;
  width: 100%;
  height: 5px;
  background: linear-gradient(
    to right,
    #00d230 var(--progress),
    black var(--progress)
  );
  animation: progressAnimation 2.5s ease infinite;
}

@keyframes progressAnimation {
  to {
    --progress: 100%;
  }
}
/* sonuç: yeşil bölüm 2.5 saniyede %25'ten %100'e akarak büyür
   ve animasyon sürekli tekrarlanır */

Aynı fikirle konik bir gradyanın başlangıç açısını kayıtlı bir değişkene bağlayıp döndürebilirsiniz: değişken türü <angle> olarak bilindiği için ara değerlerle değişir ve gradyan her adımda yeni açıyla çizilir. Gradyan sözdizimi için linear, radial ve conic gradient yazısına bakabilirsiniz:

css
@property --aci {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

.halka {
  background: conic-gradient(from var(--aci), red, blue, red);
  animation: don 4s linear infinite;
}

@keyframes don {
  to { --aci: 360deg; }
}
/* sonuç: konik gradyan 4 saniyede bir tam tur döner;
   @property olmasaydı açı kesikli değişir, dönüş akıcı görünmezdi */

Animasyonun genel kuralları için animation ve @keyframes yazısına bakabilirsiniz; hareketi azaltma tercihini (prefers-reduced-motion) orada ele alıyoruz.

Kayıtlı ve kayıtsız değişken karşılaştırması

Kayıtlı ve kayıtsız değişken karşılaştırması: Tür denetimi, Kalıtım, Varsayılan değer, Yanlış türde atama
DavranışSıradan özel özellik@property ile kayıtlı
Tür denetimiYok, her değer kabul edilirsyntax ile sınırlı
KalıtımHer zaman kalıtılırinherits ile seçilir
Varsayılan değerYok (var() yedeği gerekir)initial-value
Yanlış türde atamaKullanıldığı özellikte sorun çıkarDeğişken varsayılan davranışa döner
AnimasyonKesikli değişirAra değerlerle akıcı değişir

Pratik desen: tasarım değişkenlerini kaydetmek

Bir sitenin renklerini ve boşluklarını değişkenlerle yönetiyorsanız, en kritik birkaç değişkeni kaydetmek hataları erkenden yakalamanızı sağlar. MDN, bir CSS çatısı geliştirenlere değişkenlerine bir ad öneki vermelerini (namespace) ve onları kaydetmelerini önerir; böylece her değişkenin her zaman bir varsayılan değeri olur ve kullanıcılar yanlış türde değer atayamaz. Aşağıdaki örnekte --ky- öneki kendi projenize ait değişkenleri başka kütüphanelerin değişkenlerinden ayırır:

css
@property --ky-vurgu {
  syntax: "<color>";
  inherits: true;
  initial-value: rebeccapurple;
}

@property --ky-bosluk {
  syntax: "<length>";
  inherits: true;
  initial-value: 16px;
}

.dugme {
  background-color: var(--ky-vurgu);
  padding: var(--ky-bosluk);
}

.kampanya {
  --ky-vurgu: 12px;
}
/* sonuç: .kampanya içindeki 12px ataması <color> türüne uymaz ve
   reddedilir; değişken kalıtılan olduğu için üst öğeden gelen değer,
   o da yoksa rebeccapurple kullanılır. Düğme saydam kalmaz */

Kayıt olmasaydı aynı hatalı atama, değişkeni kullanan background-color bildirimini hesaplama anında geçersiz kılar ve zemin saydam olurdu. Kayıtla birlikte hatanın etkisi değişkenin kendisinde kalır ve tasarım bozulmaz. Bu davranışın bir bedeli de vardır: hata sessizce düzeltildiği için fark edilmesi zorlaşabilir; değerlerin beklediğiniz gibi gelmediğini düşündüğünüzde önce atamanın türünü kontrol edin. initial-value için 16px seçmemizin nedeni de şartlarla ilgilidir: 1rem gibi göreli bir birim başlangıç değeri olarak kabul edilmez.

Houdini bağlantısı

@property, MDN'in CSS Houdini olarak adlandırdığı API ailesinin bir parçasıdır. Houdini, CSS motorunun bazı bölümlerini geliştiricilere açan API'lerin genel adıdır; worklet adı verilen JavaScript modülleriyle CSS'e yeni çizim yetenekleri eklemek de bu ailededir. @property'nin farkı, kaydı JavaScript yazmadan doğrudan stil dosyasında yapabilmenizdir.

Sık yapılan hatalar

  • Tırnakları unutmak: syntax: <color>; geçersizdir, metin tırnak içinde olmalıdır.
  • inherits tanımlayıcısını atlamak: Varsayılan bir değeri yoktur, eksikse bütün kural yok sayılır.
  • Göreli birimli başlangıç değeri: initial-value: 2em kuralı geçersiz kılar.
  • Önceki değere düşmeyi beklemek: Geçersiz atama, aynı kuraldaki önceki geçerli değere değil, kalıtılan ya da başlangıç değerine döner.
  • Kalıtılmayan değişkeni üstte tanımlayıp altta kullanmak: inherits: false ile :root üzerinde verdiğiniz değer alt öğelere ulaşmaz; alt öğeler başlangıç değerini görür.

Sık Sorulan Sorular

@property ile tanımladığım değişkeni yine :root içinde değiştirebilir miyim?

Evet. Kayıt yalnızca türü, kalıtımı ve başlangıç değerini belirler; değişkene istediğiniz kuralda, türüne uygun yeni değerler atayabilirsiniz.

initial-value ile var() yedek değeri arasındaki fark nedir?

initial-value değişkenin kendisinin başlangıç değeridir ve değişkenin kullanıldığı her yerde geçerlidir. var(--ad, yedek) içindeki yedek ise yalnızca o kullanımda, değişken tanımsız ya da geçersizse devreye girer.

syntax olarak "*" yazmak ne işe yarar?

Evrensel sözdizimi her geçerli değeri kabul eder ve initial-value zorunluluğunu kaldırır. Tür denetimi ve ara değer hesaplaması kaybolur, ama kalıtım davranışını inherits ile yine belirleyebilirsiniz.

Kayıt için JavaScript kullanmam gerekir mi?

Hayır. @property kuralı, JavaScript'teki CSS.registerProperty() çağrısıyla aynı etkiyi doğrudan stil dosyasında sağlar. İkisi aynı ad için birlikte kullanılırsa MDN'e göre JavaScript kaydı kazanır; karışıklığı önlemek için her değişkeni tek bir yerden kaydetmeniz daha kolay takip edilir.

Neden animasyonum hâlâ atlayarak ilerliyor?

Değişkenin kaydı geçersiz olabilir: syntax veya inherits eksik mi, initial-value türe uygun ve hesaplama açısından bağımsız mı, kontrol edin. Kural geçersizse değişken kayıtsız kabul edilir ve kesikli değişir.

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 değişkenleri · animation ve @keyframes · transition · if() ve @function