Seçicilerinizin erişimini, kuraldaki CSS @scope ile sınırlayın

DOM'nizin yalnızca sınırlı bir alt ağacında yer alan öğeleri seçmek için @scope'u nasıl kullanacağınızı öğrenin.

Tarayıcı Desteği

  • Chrome: 118..
  • Kenar: 118..
  • Firefox: Bir bayrağın arkasında.
  • Safari: 17.4.

Kaynak

Hassas CSS seçici yazma sanatı

Seçicileri yazarken iki dünya arasında kaybolabilirsiniz. Bir yandan, hangi öğeleri seçeceğiniz konusunda oldukça spesifik olmak istersiniz. Diğer yandan, seçicilerinizin geçersiz kılınmasının kolay olmasını ve DOM yapısına sıkı sıkıya bağlı olmamasını istersiniz.

Örneğin, oldukça spesifik bir öğe seçimi olan "kart bileşeninin içerik alanındaki hero resim"i seçmek istediğinizde, büyük olasılıkla .card > .content > img.hero gibi bir seçici yazmak istemezsiniz.

  • Bu seçicinin (0,3,1) spesifikasyonu oldukça yüksek olduğundan kodunuz büyüdükçe geçersiz kılmayı zorlaştırır.
  • Doğrudan alt birleştiriciye dayalı olarak, DOM yapısına sıkı bir şekilde bağlanır. İşaretleme değişirse CSS'nizi de değiştirmeniz gerekir.

Ancak, bu öğe için seçici olarak yalnızca img yazmak da iyi bir fikir değildir. Bu, sayfanızdaki tüm resim öğelerinin seçilmesine neden olur.

Bu konuda doğru dengeyi bulmak genellikle oldukça zordur. Yıllar içinde bazı geliştiriciler, bu gibi durumlarda size yardımcı olacak çözümler ve geçici çözümler geliştirdiler. Örneğin:

  • BEM gibi metodolojiler, belirliliği düşük tutmak ve seçiminizde spesifik olmanıza izin vermek için bu öğeye card__img card__img--hero sınıfı vermenizi belirtir.
  • Kapsamlı CSS veya Stilli Bileşenler gibi JavaScript tabanlı çözümler, sayfanızın diğer tarafındaki öğeleri hedeflemelerini önlemek için seçicilerinize rastgele oluşturulmuş dizeler (ör. sc-596d7e0e-4) ekleyerek tüm seçicilerinizi yeniden yazar.
  • Bazı kitaplıklar, seçicileri tamamen kaldırır ve stil tetikleyicilerini doğrudan işaretlemenin içine yerleştirmenizi gerektirir.

Ama bunların hiçbirine ihtiyacınız yoksa ne yapabilirsiniz? CSS, yüksek belirginliğe sahip veya DOM'nize sıkı sıkıya bağlı seçiciler yazmanıza gerek kalmadan seçtiğiniz öğeler konusunda son derece spesifik olmanız için size bir yöntem sunsa ne olur? İşte burada @scope devreye girer ve yalnızca DOM'nizin bir alt ağacında bulunan öğeleri seçmenize olanak tanır.

@scope ile tanışın

@scope ile seçicilerinizin erişimini sınırlandırabilirsiniz. Bunu, hedeflemek istediğiniz alt ağacın üst sınırını belirleyen kapsam kökünü ayarlayarak yapabilirsiniz. Kapsam oluşturma kök kümesiyle, içerilen stil kuralları (kapsamlı stil kuralları adlı) yalnızca DOM'nin bu sınırlı alt ağacından seçim yapabilir.

Örneğin, .card bileşeninde yalnızca <img> öğelerini hedeflemek için .card öğesini, @scope kuralının kapsam kökü olarak ayarlarsınız.

@scope (.card) {
    img {
        border-color: green;
    }
}

img { … } kapsamlı stil kuralı yalnızca eşleşen .card öğesinin kapsamındaki <img> öğelerini etkili bir şekilde seçebilir.

Kartın içerik alanındaki (.card__content) <img> öğelerinin seçilmesini önlemek için img seçiciyi daha spesifik hale getirebilirsiniz. Bunu yapmanın bir diğer yolu, @scope kuralının alt sınırı belirleyen bir kapsam belirleme sınırını da kabul etmesidir.

@scope (.card) to (.card__content) {
    img {
        border-color: green;
    }
}

Bu kapsamlı stil kuralı yalnızca üst öğe ağacında .card ile .card__content öğeleri arasına yerleştirilen <img> öğelerini hedefler. Üst ve alt sınırları olan bu kapsam oluşturma türü, genellikle halka kapsamı olarak adlandırılır.

:scope seçici

Varsayılan olarak, kapsama dahil olan tüm stil kuralları, kapsam köküne göre belirlenir. Kapsam oluşturma kök öğesinin kendisini hedeflemek de mümkündür. Bunun için :scope seçiciyi kullanın.

@scope (.card) {
    :scope {
        /* Selects the matched .card itself */
    }
    img {
       /* Selects img elements that are a child of .card */
    }
}

Kapsamlı stil kurallarının içindeki seçicilerin başına :scope eklenir. İsterseniz :scope ifadesini kendiniz belirleyerek açıkça belirtebilirsiniz. Alternatif olarak CSS Nesting'den & seçiciyi de ekleyebilirsiniz.

@scope (.card) {
    img {
       /* Selects img elements that are a child of .card */
    }
    :scope img {
        /* Also selects img elements that are a child of .card */
    }
    & img {
        /* Also selects img elements that are a child of .card */
    }
}

Kapsam oluşturma sınırı, kapsam köküyle belirli bir ilişki olmasını zorunlu kılmak için :scope sözde sınıfını kullanabilir:

/* .content is only a limit when it is a direct child of the :scope */
@scope (.media-object) to (:scope > .content) { ... }

Kapsam oluşturma sınırı, :scope kullanılarak kapsam köklerinin dışındaki öğelere de referans verebilir. Örneğin:

/* .content is only a limit when the :scope is inside .sidebar */
@scope (.media-object) to (.sidebar :scope .content) { ... }

Kapsamlı stil kurallarının kendilerinin alt ağacın dışına çıkamayacağını unutmayın. Kapsama dahil olmayan öğeler seçilmeye çalışıldığından, :scope + p gibi seçimler geçersizdir.

@scope ve kesinlik

@scope başlangıcında kullandığınız seçiciler, içerdiği seçicilerin kesinliğini etkilemez. Aşağıdaki örnekte, img seçicinin kesinliği (0,0,1) şeklindedir.

@scope (#sidebar) {
    img { /* Specificity = (0,0,1) */
        …
    }
}

:scope özelliğinin kesinliği, normal bir sanal sınıf olan (0,1,0) değeridir.

@scope (#sidebar) {
    :scope img { /* Specificity = (0,1,0) + (0,0,1) = (0,1,1) */
        …
    }
}

Aşağıdaki örnekte & öğesi, kapsam kökü için kullanılan seçiciye dahili olarak, :is() seçicisinin içine yerleştirilmiş şekilde yeniden yazılır. Sonunda, tarayıcı eşleşmeyi yapmak için seçici olarak :is(#sidebar, .card) img kullanır. Bu işlem, artırma olarak bilinir.

@scope (#sidebar, .card) {
    & img { /* desugars to `:is(#sidebar, .card) img` */
        …
    }
}

&, :is() kullanılarak sadeleştirildiğinden, & öğesinin spesifikliği :is() özgüllük kurallarına göre hesaplanır: & işlevinin spesifikliği, en spesifik bağımsız değişkenidir.

Bu örneğe göre :is(#sidebar, .card), en belirgin bağımsız değişkeni olan #sidebar ile ilgili olarak (1,0,0) haline gelir. Bunu img özelliğinin ((0,0,1)) kesinliğiyle birleştirdiğinizde tüm karmaşık seçici için spesifik olarak (1,0,1) elde edersiniz.

@scope (#sidebar, .card) {
    & img { /* Specificity = (1,0,0) + (0,0,1) = (1,0,1) */
        …
    }
}

@scope içinde :scope ile & arasındaki fark

Belirginliğin hesaplanma şekliyle ilgili farklılıkların yanı sıra, :scope ile & arasındaki başka bir fark da :scope öğesinin eşleşen kapsam kökünü, & ise kapsam kökünü eşleştirmek için kullanılan seçiciyi temsil etmesidir.

Bu nedenle, & öğesini birden fazla kez kullanmak mümkündür. Bu, bir kapsam kökü içindeki kapsam kökünü eşleştiremeyeceğiniz için yalnızca bir kez kullanabileceğiniz :scope ile zıttır.

@scope (.card) {
  & & { /* Selects a `.card` in the matched root .card */
  }
  :scope :scope { /* ❌ Does not work */
    …
  }
}

Başlangıçsız kapsam

<style> öğesiyle satır içi stiller yazarken, herhangi bir kapsam kökü belirtmeyerek stil kurallarının kapsamını <style> öğesinin kapsayan üst öğesine ayarlayabilirsiniz. Bunu @scope başlangıcını atlayarak yaparsınız.

<div class="card">
  <div class="card__header">
    <style>
      @scope {
        img {
          border-color: green;
        }
      }
    </style>
    <h1>Card Title</h1>
    <img src="…" height="32" class="hero">
  </div>
  <div class="card__content">
    <p><img src="…" height="32"></p>
  </div>
</div>

Yukarıdaki örnekte, div, <style> öğesinin üst öğesi olduğundan, kapsamdaki kurallar yalnızca card__header sınıf adına sahip div içindeki öğeleri hedefler.

Bu şelalede @scope

@scope, CSS Basamaklaması'nın içine yeni bir ölçüt daha ekler: yakınlık kapsamı. Adım, belirginlikten sonra, ancak görünüş sırasından önce gelir.

CSS Cascade&#39;in görselleştirilmesi.

Spesifikasyona göre:

Farklı kapsam köklerine sahip stil kurallarında görünen bildirimler karşılaştırılırken, en az nesil veya eş öğe öğe içeren bildirim, kapsam kökü ile kapsamlı stil kuralı konusu arasında atlar.

Bu yeni adım, bir bileşenin çeşitli varyasyonlarını iç içe yerleştirirken yararlı olur. @scope özelliğinin henüz kullanılmadığı şu örneği ele alalım:

<style>
    .light { background: #ccc; }
    .dark  { background: #333; }
    .light a { color: black; }
    .dark a { color: white; }
</style>
<div class="light">
    <p><a href="#">What color am I?</a></p>
    <div class="dark">
        <p><a href="#">What about me?</a></p>
        <div class="light">
            <p><a href="#">Am I the same as the first?</a></p>
        </div>
    </div>
</div>

Bu küçük işaretleme parçasını görüntülerken üçüncü bağlantı, .light sınıfının uygulandığı bir div alt öğesi olmasına rağmen black yerine white olur. Bunun nedeni, şelalenin kazananı belirlemek için kullandığı görünüm ölçütü sıralamasıdır. En son .dark a açıklandığı için .light a kuralından kazanacak

Kapsam oluşturma yakınlık ölçütü ile bu sorun artık çözülür:

@scope (.light) {
    :scope { background: #ccc; }
    a { color: black;}
}

@scope (.dark) {
    :scope { background: #333; }
    a { color: white; }
}

Kapsamdaki her iki a seçici de aynı spesifikliğe sahip olduğundan kapsam belirleme yakınlık ölçütü devreye girer. Her iki seçiciyi de kapsam köklerine yakınlıklarına göre değerlendirir. Bu üçüncü a öğesi için .light kapsam kökü için yalnızca bir durak, .dark bir duraklama ikidir. Bu nedenle, .light konumundaki a seçici kazanır.

Kapanış notu: Stil yalıtımı değil, seçici izolasyonu

Dikkat edilmesi gereken önemli bir nokta, @scope özelliğinin seçicilerin erişimini sınırlandırdığı, stil izolasyonu sunmadığıdır. Devralınan alt mülklere ait mülkler, @scope alt sınırından sonra da devralınmaya devam eder. Bu özelliklerden biri de color özelliğidir. color, donut kapsamı dahilinde olan bir öğe tanımlanırken halkanın deliğindeki çocuklara devralınmaya devam eder.

@scope (.card) to (.card__content) {
  :scope {
    color: hotpink;
  }
}
bakın

Yukarıdaki örnekte, .card__content öğesi ve alt öğeleri .card değerini devraldıkları için hotpink rengine sahiptir.

(rustam burkhanov'un Unsplash'teki kapak fotoğrafı)