Kapsayıcı sorgusu polyfill içinde

Gerald Monaco
Gerald Monaco

Kapsayıcı sorguları, alt öğelerine stil uygulamak için üst öğenin özelliklerini (ör. genişliği veya yüksekliği) hedefleyen stil mantığı yazmanıza olanak tanıyan yeni bir CSS özelliğidir. Kısa süre önce, tarayıcılarda desteğin kullanıma sunulmasıyla aynı zamanda polyfill için büyük bir güncelleme yayınlandı.

Bu yayında, polyfill'in nasıl çalıştığına, üstesinden geldiği zorluklara ve ziyaretçilerinize mükemmel bir kullanıcı deneyimi sunmak için kullanırken dikkat etmeniz gereken en iyi uygulamalara göz atabilirsiniz.

Perde arkası

Transpilasyon

Bir tarayıcıdaki CSS ayrıştırıcısı, yeni @container kuralı gibi bilinmeyen bir at-kuralıyla karşılaştığında bu kuralı hiç yokmuş gibi atar. Bu nedenle, polyfill'in yapması gereken ilk ve en önemli şey, bir @container sorgusunu atılmayacağı bir şeye dönüştürmektir.

Derlemenin ilk adımı, üst düzey @container kuralını bir @media sorgusuna dönüştürmektir. Bu sayede, içerikler genellikle gruplandırılmış halde kalır. Örneğin, CSSOM API'leri kullanılırken ve CSS kaynağı görüntülenirken.

Önce
@container (width > 300px) {
  /* content */
}
Sonra
@media all {
  /* content */
}

Kapsayıcı sorguları kullanılmadan önce, CSS'de yazarların kural gruplarını keyfi olarak etkinleştirmesi veya devre dışı bırakması için bir yöntem yoktu. Bu davranışı polyfill yapmak için kapsayıcı sorgusunun içindeki kuralların da dönüştürülmesi gerekir. Her @container'ye kendi benzersiz kimliği (ör. 123) verilir. Bu kimlik, her seçiciyi yalnızca öğede bu kimliği içeren bir cq-XYZ özelliği olduğunda geçerli olacak şekilde dönüştürmek için kullanılır. Bu özellik, çalışma zamanında polyfill tarafından ayarlanır.

Önce
@container (width > 300px) {
  .card {
    /* ... */
  }
}
Sonra
@media all {
  .card:where([cq-XYZ~="123"]) {
    /* ... */
  }
}

:where(...) sözde sınıfının kullanıldığına dikkat edin. Normalde ek bir özellik seçici eklemek, seçicinin özellik değerini artırır. Sanal sınıf sayesinde, orijinal özgüllüğü korunurken ek koşul uygulanabilir. Bunun neden önemli olduğunu anlamak için aşağıdaki örneği inceleyin:

@container (width > 300px) {
  .card {
    color: blue;
  }
}

.card {
  color: red;
}

Bu CSS'de, .card sınıfına sahip bir öğe her zaman color: red özelliğine sahip olmalıdır. Çünkü sonraki kural, aynı seçici ve özgünlükle önceki kuralı her zaman geçersiz kılar. Bu nedenle, ilk kuralı derleyip :where(...) olmadan ek bir özellik seçici eklemek özgüllüğü artıracak ve color: blue'ın hatalı bir şekilde uygulanmasına neden olacaktır.

Ancak :where(...) sözde sınıfı oldukça yeni. Bu özelliği desteklemeyen tarayıcılar için polyfill güvenli ve kolay bir geçici çözüm sunar: @container kurallarınıza manuel olarak bir :not(.container-query-polyfill) seçici ekleyerek kurallarınızın özgünlüğünü kasıtlı olarak artırabilirsiniz:

Önce
@container (width > 300px) {
  .card {
    color: blue;
  }
}

.card {
  color: red;
}
Sonra
@container (width > 300px) {
  .card:not(.container-query-polyfill) {
    color: blue;
  }
}

.card {
  color: red;
}

Bu işlemin bazı avantajları vardır:

  • Kaynak CSS'deki seçici değiştiğinden, özgünlükteki fark açıkça görülebilir. Bu, geçici çözümü veya polyfill'i desteklemeniz gerekmediğinde etkilenen öğeleri bilmeniz için doküman görevi de görür.
  • polyfill bunu değiştirmediğinden, kuralların özgünlüğü her zaman aynı olur.

Doldurma, transpilasyon sırasında bu sözde öğeyi aynı özgünlükteki özellik seçiciyle değiştirir. Herhangi bir sürprizle karşılaşmamak için çoklu dolgu, her iki seçiciyi de kullanır: Öğenin çoklu dolgu özelliğini alıp almayacağını belirlemek için orijinal kaynak seçici, stil oluşturmak için ise dönüştürülmüş seçici kullanılır.

Sözde öğeler

Şu soruyu sorabilirsiniz: Polifill, bir öğede cq-XYZ özelliğini 123 benzersiz kapsayıcı kimliğini içerecek şekilde ayarlarsa, özellikleri ayarlanamayan sözde öğeler nasıl desteklenir?

Sanal öğeler her zaman DOM'daki kaynak öğe olarak adlandırılan gerçek bir öğeye bağlıdır. Koşullu seçici, derleme sırasında bunun yerine bu gerçek öğeye uygulanır:

Önce
@container (width > 300px) {
  #foo::before {
    /* ... */
  }
}
Sonra
@media all {
  #foo:where([cq-XYZ~="123"])::before {
    /* ... */
  }
}

Koşullu seçici, #foo::before:where([cq-XYZ~="123"]) olarak dönüştürülmek yerine (geçersiz olur) kaynak öğenin (#foo) sonuna taşınır.

Ancak bunun için gereken tek şey bu değildir. Bir kapsayıcının içinde bulunmayan hiçbir şeyi değiştirmesine izin verilmez (ve kapsayıcı kendi içinde olamaz). Ancak #foo, sorgulanan kapsayıcı öğesi olsaydı tam olarak bunun olacağını düşünün. #foo[cq-XYZ] özelliği yanlışlıkla değiştirilir ve tüm #foo kuralları yanlışlıkla uygulanır.

Bu sorunu düzeltmek için polyfill aslında iki özellik kullanır: Biri yalnızca bir öğeye üst öğe tarafından uygulanabilir, diğeri ise öğenin kendisine uygulayabileceği bir özelliktir. İkinci özellik, sözde öğeleri hedefleyen seçiciler için kullanılır.

Önce
@container (width > 300px) {
  #foo,
  #foo::before {
    /* ... */
  }
}
Sonra
@media all {
  #foo:where([cq-XYZ-A~="123"]),
  #foo:where([cq-XYZ-B~="123"])::before {
    /* ... */
  }
}

Bir kapsayıcı, ilk özelliği (cq-XYZ-A) hiçbir zaman kendisine uygulamaz. Bu nedenle, ilk seçici yalnızca farklı bir üst kapsayıcı kapsayıcı koşullarını karşılayıp uygularsa eşleşir.

Kapsayıcıya göreceli birimler

Kapsayıcı sorguları, CSS'nizde kullanabileceğiniz birkaç yeni birim de içerir. Örneğin, en yakın uygun üst kapsayıcının genişliğinin ve yüksekliğinin (sırasıyla) %1'i için cqw ve cqh gibi birimler kullanılabilir. Bu özellikleri desteklemek için birim, CSS Özel Özellikleri kullanılarak bir calc(...) ifadesine dönüştürülür. Polifill, kapsayıcı öğesindeki satır içi stiller aracılığıyla bu özelliklerin değerlerini ayarlar.

Önce
.card {
  width: 10cqw;
  height: 10cqh;
}
Sonra
.card {
  width: calc(10 * --cq-XYZ-cqw);
  height: calc(10 * --cq-XYZ-cqh);
}

Satır içi boyut ve blok boyutu için sırasıyla cqi ve cqb gibi mantıksal birimler de vardır. Satır içi ve blok eksenleri, sorgulanan öğenin değil, birimi kullanan öğenin writing-mode özelliğine göre belirlendiği için bu yöntemler biraz daha karmaşıktır. Bu özelliği desteklemek için polyfill, writing-mode değeri üst öğesinden farklı olan tüm öğelere satır içi stil uygular.

/* Element with a horizontal writing mode */
--cq-XYZ-cqi: var(--cq-XYZ-cqw);
--cq-XYZ-cqb: var(--cq-XYZ-cqh);

/* Element with a vertical writing mode */
--cq-XYZ-cqi: var(--cq-XYZ-cqh);
--cq-XYZ-cqb: var(--cq-XYZ-cqw);

Artık birimler, önceki gibi uygun CSS özel mülküne dönüştürülebilir.

Özellikler

Kapsayıcı sorguları, container-type ve container-name gibi birkaç yeni CSS özelliği de ekler. getComputedStyle(...) gibi API'ler bilinmeyen veya geçersiz özelliklerle kullanılamayacağından bunlar da ayrıştırıldıktan sonra CSS özel özelliklerine dönüştürülür. Bir özellik ayrıştırılamazsa (örneğin, geçersiz veya bilinmeyen bir değer içeriyorsa) tarayıcı tarafından işlenmesi için olduğu gibi bırakılır.

Önce
.card {
  container-name: card-container;
  container-type: inline-size;
}
Sonra
.card {
  --cq-XYZ-container-name: card-container;
  --cq-XYZ-container-type: inline-size;
}

Bu özellikler keşfedildiklerinde dönüştürülür. Bu sayede polyfill, @supports gibi diğer CSS özellikleriyle sorunsuz bir şekilde çalışır. Bu işlev, aşağıda açıklandığı gibi polyfill'i kullanmayla ilgili en iyi uygulamaların temelini oluşturur.

Önce
@supports (container-type: inline-size) {
  /* ... */
}
Sonra
@supports (--cq-XYZ-container-type: inline-size) {
  /* ... */
}

CSS özel özellikleri varsayılan olarak devralınır. Örneğin, .card öğesinin tüm alt öğeleri --cq-XYZ-container-name ve --cq-XYZ-container-type değerini alır. Yerel mülkler kesinlikle bu şekilde davranmaz. Bu sorunu çözmek için polyfill, kullanıcı stillerinden önce aşağıdaki kuralı ekler. Böylece, başka bir kural tarafından kasıtlı olarak geçersiz kılınmadığı sürece her öğenin ilk değerleri almasını sağlar.

* {
  --cq-XYZ-container-name: none;
  --cq-XYZ-container-type: normal;
}

En iyi uygulamalar

Çoğu ziyaretçinin, yerleşik kapsayıcı sorgu desteğine sahip tarayıcılar kullanması beklense de geri kalan ziyaretçilerinize iyi bir deneyim sunmak yine de önemlidir.

İlk yükleme sırasında, polyfill'in sayfayı düzenleyebilmesi için birçok şeyin gerçekleşmesi gerekir:

  • Polifill'in yüklenmesi ve başlatılması gerekir.
  • Stil sayfalarının ayrıştırılması ve derlenmesi gerekir. Harici stil sayfasının ham kaynağına erişmek için herhangi bir API olmadığından, ideal olarak yalnızca tarayıcı önbelleğinden olsa da harici stil sayfasının eşzamansız olarak yeniden getirilmesi gerekebilir.

Bu endişeler polyfill tarafından dikkatlice ele alınmazsa Core Web Vitals metriğiniz geriye gidebilir.

Ziyaretçilerinize keyifli bir deneyim sunmanızı kolaylaştırmak için polyfill, Largest Contentful Paint (LCP) pahasına olsa bile First Input Delay (FID) ve Cumulative Layout Shift (CLS)'e öncelik verecek şekilde tasarlanmıştır. Daha açık belirtmek gerekirse, polyfill, kapsayıcı sorgularınızın ilk boyamadan önce değerlendirileceği konusunda garanti vermez. Bu, en iyi kullanıcı deneyimi için, kapsayıcı sorgularının kullanılmasından boyutu veya konumu etkilenecek tüm içeriklerin, polyfill yüklenip CSS'nizi transpile edene kadar gizlendiğinden emin olmanız gerekir. Bunu yapmanın bir yolu, @supports kuralı kullanmaktır:

@supports not (container-type: inline-size) {
  #content {
    visibility: hidden;
  }
}

Ziyaretçiye bir şeyler olduğunu bildirmek için bunu, (gizli) içeriğinizin üzerine kesinlikle yerleştirilmiş, saf CSS yükleme animasyonunu kullanarak birleştirmeniz önerilir. Bu yaklaşımın tam demosunu burada bulabilirsiniz.

Bu yaklaşımın birkaç nedeni vardır:

  • Saf CSS yükleyici, yeni tarayıcılara sahip kullanıcılar için yükü en aza indirirken eski tarayıcılarda ve daha yavaş ağlarda olanlara hafif geri bildirim sağlar.
  • Yükleyicinin mutlak konumunu visibility: hidden ile birleştirerek düzen kaymasından kaçınabilirsiniz.
  • Polifill yüklendikten sonra bu @supports koşulu artık geçerli olmaz ve içeriğiniz gösterilir.
  • Kapsayıcı sorguları için yerleşik destek sunan tarayıcılarda bu koşul hiçbir zaman geçerli olmaz ve sayfa, ilk boyamada beklendiği gibi gösterilir.

Sonuç

Eski tarayıcılarda kapsayıcı sorgularını kullanmak istiyorsanız polyfill'i deneyin. Herhangi bir sorunla karşılaşırsanız sorun kaydı oluşturmaktan çekinmeyin.

Bu araçla neler üreteceğinizi görmek ve deneyimlemek için sabırsızlanıyoruz.