Δαμάζοντας το Cascade με BEM και σύγχρονους επιλογείς CSS PlatoBlockchain Data Intelligence. Κάθετη αναζήτηση. Ολα συμπεριλαμβάνονται.

Δαμάζοντας το Cascade με BEM και σύγχρονους επιλογείς CSS

BEM. Όπως όλες οι τεχνικές στον κόσμο της ανάπτυξης front-end, γράφοντας CSS σε μορφή BEM μπορεί να είναι πολωτική. Αλλά είναι –τουλάχιστον στο συννεφάκι μου στο Twitter– μια από τις πιο αγαπημένες μεθοδολογίες CSS.

Προσωπικά, πιστεύω ότι το BEM είναι καλό και νομίζω ότι πρέπει να το χρησιμοποιήσετε. Αλλά καταλαβαίνω επίσης γιατί μπορεί να όχι.

Ανεξάρτητα από τη γνώμη σας για το BEM, προσφέρει πολλά πλεονεκτήματα, το μεγαλύτερο είναι ότι βοηθά στην αποφυγή συγκρούσεων ιδιαιτεροτήτων στο CSS Cascade. Αυτό συμβαίνει επειδή, εάν χρησιμοποιηθούν σωστά, οποιοιδήποτε επιλογείς γραμμένοι σε μορφή BEM θα πρέπει να έχουν την ίδια βαθμολογία ειδικότητας (0,1,0). Έχω δημιουργήσει το CSS για πολλούς ιστότοπους μεγάλης κλίμακας όλα αυτά τα χρόνια (σκεφτείτε την κυβέρνηση, τα πανεπιστήμια και τις τράπεζες) και σε αυτά τα μεγαλύτερα έργα διαπίστωσα ότι το BEM λάμπει πραγματικά. Η σύνταξη CSS είναι πολύ πιο διασκεδαστική όταν έχετε εμπιστοσύνη ότι τα στυλ που γράφετε ή επεξεργάζεστε δεν επηρεάζουν κάποιο άλλο μέρος του ιστότοπου.

Υπάρχουν πράγματι εξαιρέσεις όπου κρίνεται απολύτως αποδεκτό να προστεθεί ειδικότητα. Για παράδειγμα: το :hover και :focus ψευδοτάξεις. Αυτά έχουν βαθμολογία ειδικότητας 0,2,0. Ένα άλλο είναι ψευδοστοιχεία — όπως ::before και ::after — τα οποία έχουν βαθμολογία ιδιαιτερότητας 0,1,1. Ωστόσο, για το υπόλοιπο αυτού του άρθρου, ας υποθέσουμε ότι δεν θέλουμε άλλη ιδιαιτερότητα. 🤓

Αλλά δεν είμαι πραγματικά εδώ για να σας πουλήσω στο BEM. Αντίθετα, θέλω να μιλήσω για το πώς μπορούμε να το χρησιμοποιήσουμε μαζί με σύγχρονους επιλογείς CSS — σκεφτείτε :is(), :has(), :where()κ.λπ. — να κερδίσω ακόμη περισσότερο Έλεγχος του ο Καταρράκτης.

Τι συμβαίνει με τους σύγχρονους επιλογείς CSS;

Η Προδιαγραφές επιλογέων CSS επιπέδου 4 μας δίνει μερικούς ισχυρούς νέους τρόπους επιλογής στοιχείων. Μερικά από τα αγαπημένα μου περιλαμβάνουν :is(), :where(), να :not(), καθένα από τα οποία υποστηρίζεται από όλα τα σύγχρονα προγράμματα περιήγησης και είναι ασφαλές στη χρήση σχεδόν σε οποιοδήποτε έργο στις μέρες μας.

:is() και :where() είναι βασικά το ίδιο πράγμα εκτός από το πώς επηρεάζουν την εξειδίκευση. ΕΙΔΙΚΑ, :where() έχει πάντα βαθμολογία ιδιαιτερότητας 0,0,0. Ναι, ακόμη :where(button#widget.some-class) δεν έχει ιδιαιτερότητα. Εν τω μεταξύ, η ιδιαιτερότητα του :is() είναι το στοιχείο στη λίστα ορισμάτων του με την υψηλότερη εξειδίκευση. Έτσι, έχουμε ήδη μια διάκριση Cascade-καβγάς μεταξύ δύο σύγχρονων επιλογέων με τους οποίους μπορούμε να συνεργαστούμε.

Το απίστευτα δυνατό :has() σχεσιακή ψευδο-τάξη είναι επίσης κερδίζει γρήγορα υποστήριξη προγράμματος περιήγησης (και είναι το μεγαλύτερο νέο χαρακτηριστικό του CSS από τότε Πλέγμα, κατά την ταπεινή γνώμη μου). Ωστόσο, τη στιγμή της σύνταξης, η υποστήριξη του προγράμματος περιήγησης για :has() δεν είναι αρκετά καλό για χρήση στην παραγωγή ακόμα.

Ας κολλήσω μια από αυτές τις ψευδο-τάξεις στο BEM μου και…

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

Ωχ! Βλέπετε αυτή τη βαθμολογία ειδικότητας; Θυμηθείτε, με το BEM θέλουμε ιδανικά οι επιλογείς μας να έχουν όλοι μια βαθμολογία ειδικότητας 0,1,0. Γιατί είναι 0,2,0 κακό? Εξετάστε το ίδιο παράδειγμα, διευρυμένο:

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

Παρόλο που ο δεύτερος επιλογέας είναι τελευταίος στη σειρά πηγής, η υψηλότερη ειδικότητα του πρώτου επιλογέα (0,2,0) κερδίζει, και το χρώμα του .something--special στοιχεία θα ρυθμιστούν σε red. Δηλαδή, υποθέτοντας ότι το BEM σας είναι γραμμένο σωστά και το επιλεγμένο στοιχείο έχει και τα δύο .something βασική τάξη και .something--special κλάση τροποποιητή που εφαρμόζεται σε αυτό στο HTML.

Αν χρησιμοποιηθούν απρόσεκτα, αυτές οι ψευδο-κλάσεις μπορούν να επηρεάσουν το Cascade με απροσδόκητους τρόπους. Και είναι αυτού του είδους οι ασυνέπειες που μπορούν να δημιουργήσουν πονοκεφάλους στη γραμμή, ειδικά σε μεγαλύτερες και πιο σύνθετες βάσεις κωδικών.

Dang. Και τώρα τι?

Θυμηθείτε τι έλεγα :where() και το ότι η ιδιαιτερότητά του είναι μηδενική; Μπορούμε να το χρησιμοποιήσουμε προς όφελός μας:

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

Το πρώτο μέρος αυτού του επιλογέα (.something) παίρνει τη συνήθη βαθμολογία ειδικότητάς του 0,1,0. Αλλά :where() — και τα πάντα μέσα σε αυτό — έχουν μια ιδιαιτερότητα 0, το οποίο δεν αυξάνει περαιτέρω την ιδιαιτερότητα του επιλογέα.

:where() μας επιτρέπει να φωλιάζουμε

Οι άνθρωποι που δεν νοιάζονται τόσο πολύ όσο εγώ για την ιδιαιτερότητα (και αυτό είναι πιθανώς πολλοί άνθρωποι, για να είμαστε δίκαιοι) τα είχαν πολύ καλά όταν πρόκειται για τη φωλιά. Με μερικές ανέμελες πινελιές πληκτρολογίου, μπορεί να τελειώσουμε με CSS όπως αυτό (σημειώστε ότι χρησιμοποιώ το Sass για συντομία):

.card { ... }

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

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

Σε αυτό το παράδειγμα, έχουμε ένα .card συστατικό. Όταν είναι μια "επιλεγμένη" κάρτα (χρησιμοποιώντας το .card--featured class), ο τίτλος και η εικόνα της κάρτας πρέπει να διαμορφωθούν διαφορετικά. Όμως, όπως εμείς τώρα ξέρετε, ο παραπάνω κώδικας οδηγεί σε βαθμολογία ειδικότητας που δεν συνάδει με το υπόλοιπο σύστημά μας.

Ένας σκληροπυρηνικός σπασίκλας θα μπορούσε να το έκανε αυτό:

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

Δεν είναι τόσο κακό, σωστά; Ειλικρινά, αυτό είναι όμορφο CSS.

Ωστόσο, υπάρχει ένα μειονέκτημα στην HTML. Οι έμπειροι συντάκτες του BEM έχουν πιθανώς επίπονη επίγνωση της βαριάς λογικής προτύπου που απαιτείται για την υπό όρους εφαρμογή κλάσεων τροποποιητών σε πολλά στοιχεία. Σε αυτό το παράδειγμα, το πρότυπο HTML πρέπει να προσθέσει υπό όρους το --featured κλάση τροποποιητή σε τρία στοιχεία (.card, .card__title, να .card__img) αν και πιθανώς ακόμη περισσότερο σε ένα πραγματικό παράδειγμα. Αυτά είναι πολλά if δηλώσεις.

Η :where() Ο επιλογέας μπορεί να μας βοηθήσει να γράψουμε πολύ λιγότερη λογική προτύπου — και λιγότερες κλάσεις BEM για εκκίνηση — χωρίς να προσθέσουμε το επίπεδο ειδικότητας.

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

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

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

Εδώ είναι το ίδιο πράγμα, αλλά στο Sass (σημειώστε το trailing συμπλεκτικά):

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

Το αν θα πρέπει ή όχι να επιλέξετε αυτήν την προσέγγιση αντί της εφαρμογής κλάσεων τροποποιητών στα διάφορα θυγατρικά στοιχεία είναι θέμα προσωπικής προτίμησης. Αλλά τουλάχιστον :where() μας δίνει την επιλογή τώρα!

Τι γίνεται με HTML που δεν είναι BEM;

Δεν ζούμε σε έναν τέλειο κόσμο. Μερικές φορές χρειάζεται να ασχοληθείτε με HTML που είναι εκτός του ελέγχου σας. Για παράδειγμα, ένα σενάριο τρίτου κατασκευαστή που εισάγει HTML που πρέπει να διαμορφώσετε. Αυτή η σήμανση συχνά δεν γράφεται με ονόματα κλάσεων BEM. Σε ορισμένες περιπτώσεις, αυτά τα στυλ δεν χρησιμοποιούν καθόλου κλάσεις αλλά αναγνωριστικά!

Άλλη μιά φορά, :where() έχει την πλάτη μας. Αυτή η λύση είναι ελαφρώς τυχαία, καθώς πρέπει να αναφερθούμε στην κλάση ενός στοιχείου κάπου πιο πάνω από το δέντρο DOM που γνωρίζουμε ότι υπάρχει.

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

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

Ωστόσο, η αναφορά σε ένα γονικό στοιχείο είναι λίγο επικίνδυνη και περιοριστική. Τι γίνεται αν αυτή η γονική τάξη αλλάξει ή δεν υπάρχει για κάποιο λόγο; Μια καλύτερη (αλλά ίσως εξίσου άστοχη) λύση θα ήταν η χρήση :is() αντι αυτου. Θυμηθείτε, την ιδιαιτερότητα του :is() ισούται με τον πιο συγκεκριμένο επιλογέα στη λίστα επιλογέων του.

Έτσι, αντί να αναφέρουμε μια τάξη στην οποία γνωρίζουμε (ή ελπίζουμε!) να υπάρχει :where(), όπως στο παραπάνω παράδειγμα, θα μπορούσαμε να αναφερθούμε σε μια κατασκευασμένη τάξη και το tags.

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

Το πάντα παρόν body θα μας βοηθήσει να επιλέξουμε το δικό μας #widget στοιχείο, και η παρουσία του .dummy-class τάξη μέσα στο ίδιο :is() δίνει το body επιλογέας την ίδια βαθμολογία ειδικότητας με μια τάξη (0,1,0)… και η χρήση του :where() διασφαλίζει ότι ο επιλογέας δεν γίνεται πιο συγκεκριμένος από αυτό.

Αυτό είναι!

Έτσι μπορούμε να αξιοποιήσουμε τα σύγχρονα χαρακτηριστικά διαχείρισης ιδιαιτεροτήτων του :is() και :where() ψευδο-κλάσεις παράλληλα με την πρόληψη συγκρούσεων ειδικότητας που έχουμε όταν γράφουμε CSS σε μορφή BEM. Και στο όχι και τόσο μακρινό μέλλον, μια φορά :has() κερδίζει υποστήριξη Firefox (προς το παρόν υποστηρίζεται πίσω από μια σημαία τη στιγμή της σύνταξης) πιθανότατα θα θελήσουμε να το ζευγαρώσουμε με το :where() για να αναιρέσουμε την ιδιαιτερότητά του.

Είτε κάνετε all-in για την ονομασία BEM είτε όχι, ελπίζω να συμφωνήσουμε ότι η συνοχή στην ιδιαιτερότητα του επιλογέα είναι καλό!

Σφραγίδα ώρας:

Περισσότερα από Κόλπα CSS