Menjinakkan Cascade Dengan BEM dan Selector CSS Modern PlatoBlockchain Data Intelligence. Pencarian Vertikal. Ai.

Menjinakkan Kaskade Dengan BEM dan Selektor CSS Modern

BEM. Seperti semua teknik di dunia pengembangan front-end, menulis CSS dalam format BEM dapat terpolarisasi. Tapi itu – setidaknya di gelembung Twitter saya – salah satu metodologi CSS yang lebih disukai.

Secara pribadi, menurut saya BEM bagus, dan menurut saya Anda harus menggunakannya. Tapi saya juga mengerti mengapa Anda mungkin tidak.

Terlepas dari pendapat Anda tentang BEM, ini menawarkan beberapa manfaat, yang terbesar adalah membantu menghindari bentrokan spesifisitas dalam CSS Cascade. Itu karena, jika digunakan dengan benar, setiap penyeleksi yang ditulis dalam format BEM harus memiliki skor spesifisitas yang sama (0,1,0). Saya telah merancang CSS untuk banyak situs web berskala besar selama bertahun-tahun (pikirkan pemerintah, universitas, dan bank), dan pada proyek yang lebih besar inilah saya menemukan bahwa BEM benar-benar bersinar. Menulis CSS jauh lebih menyenangkan ketika Anda yakin bahwa gaya yang Anda tulis atau edit tidak memengaruhi bagian lain dari situs.

Sebenarnya ada pengecualian di mana dianggap benar-benar dapat diterima untuk menambahkan kekhususan. Misalnya: di :hover dan :focus kelas semu. Mereka memiliki skor spesifisitas 0,2,0. Lainnya adalah elemen semu — seperti ::before dan ::after - yang memiliki skor spesifisitas 0,1,1. Namun untuk sisa artikel ini, mari kita asumsikan kita tidak menginginkan kekhususan lainnya. 🤓

Tapi saya tidak benar-benar di sini untuk menjual Anda di BEM. Sebaliknya, saya ingin berbicara tentang bagaimana kita dapat menggunakannya bersama dengan penyeleksi CSS modern — pikirkan :is(), :has(), :where(), dll. — untuk mendapatkan genap lebih kendali atas kaskade.

Apa ini tentang penyeleksi CSS modern?

Grafik Spesifikasi CSS Selectors Level 4 memberi kita beberapa cara (ish) baru yang ampuh untuk memilih elemen. Beberapa favorit saya termasuk :is(), :where(), dan :not(), yang masing-masing didukung oleh semua browser modern dan aman digunakan di hampir semua proyek saat ini.

:is() dan :where() pada dasarnya adalah hal yang sama kecuali bagaimana pengaruhnya terhadap spesifisitas. Secara khusus, :where() selalu memiliki skor spesifisitas 0,0,0. Ya, bahkan :where(button#widget.some-class) tidak memiliki kekhususan. Sementara itu, kekhususan dari :is() adalah elemen dalam daftar argumennya dengan kekhususan tertinggi. Jadi, kita sudah memiliki perbedaan perselisihan Cascade antara dua penyeleksi modern yang dapat kita kerjakan.

Yang luar biasa kuat :has() kelas semu relasional juga mendapatkan dukungan browser dengan cepat (dan merupakan fitur baru CSS terbesar sejak itu kisi, Menurut pendapat saya). Namun, pada saat penulisan, dukungan browser untuk :has() belum cukup baik untuk digunakan dalam produksi dulu.

Biar saya tempelkan salah satu pseudo-class itu di BEM saya dan…

/* ❌ specificity score: 0,2,0 */
.something:not(.something--special) {
  /* styles for all somethings, except for the special somethings */
}

Ups! Lihat skor spesifisitas itu? Ingat, dengan BEM kami idealnya ingin semua penyeleksi kami memiliki skor spesifisitas 0,1,0. Kenapa 0,2,0 buruk? Pertimbangkan contoh yang sama ini, diperluas:

.something:not(.something--special) {
  color: red;
}
.something--special {
  color: blue;
}

Meskipun selektor kedua terakhir dalam urutan sumber, spesifisitas selektor pertama lebih tinggi (0,2,0) menang, dan warna .something--special elemen akan diatur ke red. Artinya, dengan asumsi BEM Anda ditulis dengan benar dan elemen yang dipilih memiliki keduanya .something kelas dasar dan .something--special kelas pengubah diterapkan untuk itu dalam HTML.

Digunakan secara sembarangan, kelas semu ini dapat memengaruhi Cascade dengan cara yang tidak terduga. Dan ketidakkonsistenan semacam inilah yang dapat membuat sakit kepala, terutama pada basis kode yang lebih besar dan lebih kompleks.

Sial. Jadi sekarang apa?

Ingat apa yang saya katakan tentang :where() dan fakta bahwa kekhususannya adalah nol? Kita dapat menggunakannya untuk keuntungan kita:

/* ✅ specificity score: 0,1,0 */
.something:where(:not(.something--special)) {
  /* etc. */
}

Bagian pertama dari pemilih ini (.something) mendapatkan skor spesifisitasnya yang biasa 0,1,0. Tapi :where() — dan semua yang ada di dalamnya — memiliki kekhususan 0, yang tidak meningkatkan spesifisitas pemilih lebih jauh.

:where() memungkinkan kita untuk bersarang

Orang-orang yang tidak terlalu peduli dengan saya tentang kekhususan (dan itu mungkin banyak orang, untuk bersikap adil) sudah cukup bagus dalam hal bersarang. Dengan beberapa sapuan keyboard tanpa beban, kita mungkin berakhir dengan CSS seperti ini (perhatikan bahwa saya menggunakan Sass untuk singkatnya):

.card { ... }

.card--featured {
  /* etc. */  
  .card__title { ... }
  .card__title { ... }
}

.card__title { ... }
.card__img { ... }

Dalam contoh ini, kami memiliki a .card komponen. Ketika itu adalah kartu "unggulan" (menggunakan .card--featured class), judul dan gambar kartu perlu diberi gaya berbeda. Tapi, seperti kita sekarang ketahuilah, kode di atas menghasilkan skor spesifisitas yang tidak konsisten dengan sistem kami yang lain.

Seorang nerd spesifisitas yang sangat keras mungkin telah melakukan ini sebagai gantinya:

.card { ... }
.card--featured { ... }
.card__title { ... }
.card__title--featured { ... }
.card__img { ... }
.card__img--featured { ... }

Itu tidak terlalu buruk, kan? Terus terang, ini adalah CSS yang indah.

Ada sisi negatifnya dalam HTML. Penulis BEM berpengalaman mungkin sangat menyadari logika template kikuk yang diperlukan untuk menerapkan kelas pengubah secara kondisional ke beberapa elemen. Dalam contoh ini, template HTML perlu menambahkan secara bersyarat --featured kelas pengubah menjadi tiga elemen (.card, .card__title, dan .card__img) meskipun mungkin lebih banyak lagi dalam contoh dunia nyata. Itu banyak if pernyataan.

Grafik :where() pemilih dapat membantu kita menulis lebih sedikit logika template — dan lebih sedikit kelas BEM untuk boot — tanpa menambah tingkat kekhususan.

.card { ... }
.card--featured { ... }

.card__title { ... }
:where(.card--featured) .card__title { ... }

.card__img { ... }
:where(.card--featured) .card__img { ... }

Ini hal yang sama tetapi di Sass (perhatikan trailing ampersand):

.card { ... }
.card--featured { ... }
.card__title { 
  /* etc. */ 
  :where(.card--featured) & { ... }
}
.card__img { 
  /* etc. */ 
  :where(.card--featured) & { ... }
}

Apakah Anda harus memilih pendekatan ini atau tidak daripada menerapkan kelas pengubah ke berbagai elemen turunan adalah masalah preferensi pribadi. Tapi setidaknya :where() memberi kita pilihan sekarang!

Bagaimana dengan HTML non-BEM?

Kita tidak hidup di dunia yang sempurna. Terkadang Anda perlu berurusan dengan HTML yang berada di luar kendali Anda. Misalnya, skrip pihak ketiga yang menyuntikkan HTML yang perlu Anda gaya. Markup itu sering tidak ditulis dengan nama kelas BEM. Dalam beberapa kasus, gaya tersebut tidak menggunakan kelas sama sekali kecuali ID!

Sekali lagi, :where() mendukung kita. Solusi ini sedikit retas, karena kita perlu mereferensikan kelas elemen di suatu tempat lebih jauh di atas pohon DOM yang kita tahu ada.

/* ❌ specificity score: 1,0,0 */
#widget {
  /* etc. */
}

/* ✅ specificity score: 0,1,0 */
.page-wrapper :where(#widget) {
  /* etc. */
}

Mereferensikan elemen induk terasa sedikit berisiko dan membatasi. Bagaimana jika kelas induk itu berubah atau tidak ada karena alasan tertentu? Solusi yang lebih baik (tapi mungkin sama-sama meretas) adalah dengan menggunakan :is() alih-alih. Ingat, kekhususan dari :is() sama dengan pemilih paling spesifik dalam daftar pemilihnya.

Jadi, alih-alih mereferensikan kelas yang kita tahu (atau berharap!) ada :where(), seperti pada contoh di atas, kita bisa mereferensikan kelas buatan dan menandai.

/* ✅ specificity score: 0,1,0 */
:is(.dummy-class, body) :where(#widget) {
  /* etc. */
}

Selalu hadir body akan membantu kami memilih kami #widget elemen, dan keberadaan .dummy-class kelas di dalam sama :is() memberikan body pemilih skor spesifisitas yang sama sebagai kelas (0,1,0)… dan penggunaan :where() memastikan pemilih tidak lebih spesifik dari itu.

Itu dia!

Begitulah cara kami memanfaatkan fitur pengelolaan spesifisitas modern dari :is() dan :where() pseudo-class bersama dengan pencegahan benturan spesifisitas yang kita dapatkan saat menulis CSS dalam format BEM. Dan dalam waktu yang tidak terlalu lama, sekali :has() memperoleh dukungan Firefox (saat ini didukung di belakang bendera pada saat penulisan) kami mungkin ingin memasangkannya dengan :where() untuk membatalkan kekhususannya.

Apakah Anda menggunakan penamaan BEM atau tidak, saya harap kita dapat setuju bahwa memiliki konsistensi dalam kekhususan pemilih adalah hal yang baik!

Stempel Waktu:

Lebih dari Trik CSS