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

Yalnızca DOM'unuzun sınırlı bir alt ağacındaki öğeleri seçmek için @scope'u nasıl kullanacağınızı öğrenin.

Tarayıcı desteği

  • Chrome: 118.
  • Edge: 118.
  • Firefox: İşaretli.
  • Safari: 17.4.

Kaynak

Hassas CSS seçici yazma sanatı

Seçicileri yazarken iki dünya arasında kaybolabilirsiniz. Bir yandan da hangi öğeleri seçeceğiniz konusunda oldukça net olmanız gerekir. Öte yandan, seçicilerinizin geçersiz kılınmasının kolay olmasını ve DOM yapısına sıkı bir şekilde 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) değerine sahip oldukça yüksek bir özelliği vardır. Bu durum, kodunuz büyüdükçe istisnayı 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 öğenin seçicisi olarak yalnızca img yazmak da istemezsiniz. Aksi takdirde sayfanızdaki tüm resim öğeleri seçilir.

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ştirdi. Örneğin:

  • BEM gibi metodolojiler, seçtiğiniz öğelerde ayrıntılı olmaya izin verirken belirginliği düşük tutmak için bu öğeye card__img card__img--hero sınıfı vermenizi zorunlu kılar.
  • Kapsamlı CSS veya Stillendirilmiş Bileşenler gibi JavaScript tabanlı çözümler, sayfanızın diğer tarafındaki öğeleri hedeflemelerini önlemek için seçicilerinize rastgele oluşturulmuş dize (ör. sc-596d7e0e-4) ekleyerek tüm seçicilerinizi yeniden yazar.
  • Bazı kitaplıklarda seçiciler tamamen kaldırılır ve stil tetikleyicilerini doğrudan işaretlemeye yerleştirmeniz istenir.

Peki bunların hiçbirine ihtiyacınız yoksa ne olur? CSS, yüksek özgünlükte veya DOM'unuza sıkı sıkıya bağlı seçiciler yazmanıza gerek kalmadan hangi öğeleri seçeceğiniz konusunda oldukça spesifik olmanızı sağlayacak bir yöntem sunsa ne olurdu? İşte bu noktada @scope devreye girer. @scope, yalnızca DOM'unuzun bir alt ağacındaki öğeleri seçmenize olanak tanır.

@scope ile tanışın

@scope ile seçicilerinizin erişimini sınırlayabilirsiniz. Bunu, hedeflemek istediğiniz alt ağacın üst sınırını belirleyen kapsama kökünü ayarlayarak yaparsınız. Kapsamlı bir kök kümesi kullanıldığında, kapsamlı stil kuralları olarak adlandırılan kapsayıcı stil kuralları yalnızca DOM'un bu sınırlı alt ağacından seçim yapabilir.

Örneğin, yalnızca .card bileşenindeki <img> öğelerini hedeflemek için .card bileşenini @scope at-kuralının kapsam kökü olarak ayarlayın.

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

Kapsamlı stil kuralı img { … }, yalnızca eşleşen .card öğesinin kapsamında olan <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 da @scope at-kuralı'nın alt sınırı belirleyen bir kapsama sınırı da kabul ettiği gerçeğini kullanmaktır.

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

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

belgesine bakın.

:scope seçici

Varsayılan olarak, tüm kapsamlı stil kuralları kapsamlandırma köküne göredir. Kapsamlandırma kök öğesinin kendisini de hedeflemek 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 ön ekini ekleyerek bu konuda açıkça belirtebilirsiniz. Alternatif olarak, CSS İç İçe Yerleştirme'den & seçicisini 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 sınırı, kapsam köküyle belirli bir ilişki gerektirmek 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 alt ağaçtan kaçamayacağını unutmayın. Kapsam dışındaki öğeleri seçmeye çalıştığı için :scope + p gibi seçimler geçersizdir.

@scope ve kesinlik

@scope için giriş bölümünde kullandığınız seçiciler, kapsanan seçicilerin özgünlüğünü etkilemez. Aşağıdaki örnekte, img seçicinin kesinliği (0,0,1) şeklindedir.

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

:scope, normal bir sözde sınıf olan (0,1,0) ile aynı özgünlüğe sahiptir.

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

Aşağıdaki örnekte, dahili olarak &, kapsamlandırma kökü için kullanılan seçiciye yeniden yazılır ve bir :is() seçicisinin içine sarılır. Sonuç olarak tarayıcı, eşleştirmeyi yapmak için seçici olarak :is(#sidebar, .card) img değerini kullanır. Bu işleme şeker azaltma denir.

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

&, :is() kullanılarak şekerden arındırıldığı için &'ün özgünlüğü :is() özgünlük kurallarına göre hesaplanır: &'ün özgünlüğü, en ayrıntılı bağımsız değişkeninin özgünlüğüdür.

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 kesinliğiyle ((0,0,1)) 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, kapsamlandırma kökü içinde bir kapsamlandırma kökünü eşleştiremediğ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 */
    
  }
}

Girişsiz kapsam

<style> öğesiyle satır içi stiller yazarken herhangi bir kapsamlandırma kökü belirtmeden stil kurallarını <style> öğesinin kapsayıcı üst öğesine göre belirleyebilirsiniz. Bunu yapmak için @scope'ün girişini atlarsı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, kapsamlı kurallar yalnızca div öğesinin içinde bulunan ve sınıf adı card__header olan öğeleri hedefler. Bunun nedeni, div öğesinin <style> öğesinin üst öğesi olmasıdır.

.

Şelalede @scope

@scope, CSS basamaklı düzeni içinde yeni bir ölçüt de ekler: kapsama yakınlığı. Bu adım, özgünlükten sonra ancak görünme 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 beyanlar karşılaştırılırken, kapsam kökü ile kapsamlı stil kuralı öznesi arasında en az nesil veya kardeş öğe atlaması olan beyan kazanır.

Bu yeni adım, bir bileşenin çeşitli varyasyonlarını iç içe yerleştirirken kullanışlıdır. Henüz @scope kullanmayan ş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, skalanın kazananı belirlemek için kullandığı görünüm kriteridir. .dark a'ün en son ilan edildiğini görür ve .light a kuralı uyarınca kazanır

adlı kaleme göz atın.

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

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

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

Her iki kapsamlı a seçici de aynı özgünlüğe sahip olduğundan kapsama yakınlığı ölçütü devreye girer. Her iki seçiciyi de kapsam köklerine olan yakınlıklarına göre ağırlıklandırır. Üçüncü a öğesi için .light kapsama köküne yalnızca bir sıçrama, .dark köküne ise iki sıçrama yapılır. Bu nedenle, .light içindeki a seçici kazanır.

göz atın.

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

@scope'ün seçicilere erişimi sınırladığı, stil izolasyonu sunmadığı önemli bir noktadı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. Bir donut kapsamının içinde olduğunu beyan ederken color, donutun deliğinin içindeki alt öğelere de devralınır.

@scope (.card) to (.card__content) {
  :scope {
    color: hotpink;
  }
}
kalemine göz atın.

Yukarıdaki örnekte, .card__content öğesi ve alt öğeleri, .card öğesinden değer devraldığı için hotpink rengi kullanır.

(Kapak fotoğrafı: Rustam Burkhanov, Unsplash)