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.
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.
: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.
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
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.
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;
}
}
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)