Platonova podatkovna inteligenca.
Navpično iskanje in Ai.

Ukrotitev kaskade z BEM in sodobnimi izbirniki CSS

Datum:

BEM. Kot na videz vse tehnike v svetu front-end razvoja, pisanje CSS v formatu BEM lahko polarizira. Je pa – vsaj v mojem oblačku na Twitterju – ena izmed bolj priljubljenih metodologij CSS.

Osebno menim, da je BEM dober in mislim, da bi ga morali uporabiti. Razumem pa tudi, zakaj morda ne.

Ne glede na vaše mnenje o BEM ponuja številne prednosti, največja pa je ta, da pomaga pri preprečevanju sporov glede specifičnosti v CSS Cascade. To je zato, ker morajo vsi izbirniki, napisani v formatu BEM, če se pravilno uporabljajo, imeti enako oceno specifičnosti (0,1,0). V preteklih letih sem arhitektiral CSS za številne obsežne spletne strani (pomislite na vlado, univerze in banke) in pri teh večjih projektih sem ugotovil, da BEM resnično blesti. Pisanje CSS je veliko bolj zabavno, če ste prepričani, da slogi, ki jih pišete ali urejate, ne vplivajo na druge dele spletnega mesta.

Dejansko obstajajo izjeme, kjer se zdi povsem sprejemljivo dodajanje specifičnosti. Na primer: :hover in :focus psevdo razredi. Ti imajo oceno specifičnosti 0,2,0. Drugi so psevdo elementi - kot ::before in ::after — ki imajo oceno specifičnosti 0,1,1. Za preostanek tega članka pa predpostavimo, da ne želimo nobene druge specifičnosti. 🤓

Ampak res nisem tukaj, da bi vam prodal BEM. Namesto tega želim govoriti o tem, kako ga lahko uporabimo skupaj s sodobnimi izbirniki CSS – pomislite :is(), :has(), :where(), itd. - pridobiti celo več nadzor nad kaskada.

Kaj je to s sodobnimi izbirniki CSS?

O Specifikacije izbirnikov CSS ravni 4 nam ponuja nekaj zmogljivih novih načinov za izbiro elementov. Nekateri izmed mojih najljubših vključujejo :is(), :where()in :not(), ki jih podpirajo vsi sodobni brskalniki in jih je danes varno uporabljati pri skoraj vseh projektih.

:is() in :where() so v bistvu ista stvar, razen glede tega, kako vplivajo na specifičnost. Natančneje, :where() vedno ima oceno specifičnosti 0,0,0. Ja, celo :where(button#widget.some-class) nima posebnosti. Medtem pa specifičnost :is() je element na svojem seznamu argumentov z največjo specifičnostjo. Tako že imamo kaskadno razlikovanje med dvema sodobnima izbirnikoma, s katerima lahko delamo.

Neverjetno močan :has() relacijski psevdorazred je tudi hitro pridobiva podporo brskalnika (in je največja nova funkcija CSS od Mreža, po mojem skromnem mnenju). Vendar pa je v času pisanja podpora brskalnika za :has() še ni dovolj dober za uporabo v proizvodnji.

Naj vtaknem enega od teh psevdorazredov v svoj BEM in ...

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

Ups! Vidite to oceno specifičnosti? Ne pozabite, da z BEM idealno želimo, da imajo vsi naši selektorji oceno specifičnosti 0,1,0. Zakaj je 0,2,0 slab? Razmislite o tem istem primeru, razširjenem:

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

Čeprav je drugi izbirnik zadnji v izvornem vrstnem redu, je višja specifičnost prvega izbirnika (0,2,0) zmaga in barva .something--special elementi bodo nastavljeni na red. To pomeni, da je vaš BEM pravilno napisan in ima izbrani element oboje .something osnovni razred in .something--special razred modifikatorja, uporabljen zanj v HTML-ju.

Ob neprevidni uporabi lahko ti psevdo-razredi vplivajo na Cascade na nepričakovane načine. In prav te vrste nedoslednosti lahko povzročajo preglavice, zlasti na večjih in kompleksnejših kodnih bazah.

prekleto Kaj pa zdaj?

Zapomni si, o čem sem govoril :where() in dejstvo, da je njegova specifičnost enaka nič? To lahko uporabimo sebi v prid:

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

Prvi del tega izbirnika (.something) dobi svojo običajno oceno specifičnosti 0,1,0. Vendar :where() — in vse v njem — ima specifičnost 0, kar pa posebnosti selektorja ne povečuje.

:where() nam omogoča gnezdenje

Ljudje, ki jim specifičnost ni tako pomembna kot jaz (in to je verjetno veliko ljudi, če smo pošteni), so se imeli kar dobro, ko gre za gnezdenje. Z nekaj brezskrbnimi potezami po tipkovnici bomo morda končali s takim CSS (upoštevajte, da uporabljam Sass zaradi kratkosti):

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

V tem primeru imamo a .card komponento. Ko gre za »predstavljeno« kartico (z uporabo .card--featured razreda), je treba naslov in sliko kartice oblikovati drugače. Ampak, kot mi zdaj veste, zgornja koda povzroči oceno specifičnosti, ki ni skladna s preostalim delom našega sistema.

Namesto tega bi zagrizeni piflar s specifičnostjo morda naredil tole:

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

To ni tako slabo, kajne? Odkrito povedano, to je čudovit CSS.

Vendar pa je HTML slaba stran. Izkušeni avtorji BEM se verjetno boleče zavedajo okorne logike predloge, ki je potrebna za pogojno uporabo razredov modifikatorjev za več elementov. V tem primeru mora predloga HTML pogojno dodati --featured razred modifikatorja na tri elemente (.card, .card__titlein .card__img), čeprav verjetno še bolj v primeru iz resničnega sveta. To je veliko if izjave.

O :where() selektor nam lahko pomaga napisati veliko manj logike predloge — in manj razredov BEM za zagon — brez dodajanja ravni specifičnosti.

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

Tukaj je ista stvar, vendar v Sass (upoštevajte zaključek ampersand):

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

Ali bi se morali odločiti za ta pristop namesto uporabe modifikatorskih razredov za različne podrejene elemente ali ne, je stvar osebnih preferenc. Ampak vsaj :where() nam daje izbiro zdaj!

Kaj pa ne-BEM HTML?

Ne živimo v popolnem svetu. Včasih se morate ukvarjati s HTML-jem, ki je izven vašega nadzora. Na primer, skript tretje osebe, ki vstavi HTML, ki ga morate stilizirati. Ta oznaka pogosto ni napisana z imeni razredov BEM. V nekaterih primerih ti slogi sploh ne uporabljajo razredov, ampak ID-je!

Ponovno, :where() ima naš hrbet. Ta rešitev je rahlo hekerska, saj se moramo sklicevati na razred elementa nekje višje v drevesu DOM, za katerega vemo, da obstaja.

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

Čeprav se sklicevanje na nadrejeni element zdi nekoliko tvegano in omejujoče. Kaj pa, če se ta nadrejeni razred spremeni ali ga iz nekega razloga ni? Boljša (vendar morda enako zlobna) rešitev bi bila uporaba :is() namesto tega. Ne pozabite, specifičnost :is() je enak najbolj specifičnemu izbirniku na njegovem izbirnem seznamu.

Torej, namesto sklicevanja na razred, za katerega vemo (ali upamo!), da obstaja :where(), kot v zgornjem primeru, bi se lahko sklicevali na sestavljen razred in <body> oznaka.

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

Vedno prisotni body nam bo pomagal izbrati naše #widget element in prisotnost .dummy-class razred znotraj istega :is() daje body izbirnik enak rezultat specifičnosti kot razred (0,1,0)… in uporaba :where() zagotavlja, da izbirnik ne postane bolj specifičen od tega.

To je to!

Tako lahko izkoristimo sodobne funkcije za upravljanje specifičnosti :is() in :where() psevdo-razredi skupaj s preprečevanjem trkov specifičnosti, ki ga dobimo pri pisanju CSS v formatu BEM. In v ne tako oddaljeni prihodnosti, enkrat :has() pridobi podporo za Firefox (trenutno je podprt za zastavico v času pisanja) ga bomo verjetno želeli združiti z :where(), da razveljavimo njegovo specifičnost.

Ne glede na to, ali se odločite za poimenovanje BEM ali ne, upam, da se lahko strinjamo, da je doslednost v specifičnosti izbirnika dobra stvar!

spot_img

Najnovejša inteligenca

spot_img

Klepetajte z nami

Zdravo! Kako vam lahko pomagam?