אילוף את המפל עם בוררי BEM ו-CSS מודרניים PlatoBlockchain Data Intelligence. חיפוש אנכי. איי.

אילוף את המפל עם בוררי BEM ו-CSS מודרניים

BEM. כמו לכאורה כל הטכניקות בעולם הפיתוח החזיתי, כתיבת CSS בפורמט BEM יכול להיות מקטב. אבל זו - לפחות בבועת הטוויטר שלי - אחת מתודולוגיות ה-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 Selectors רמה 4 נותן לנו כמה דרכים חדשות ועוצמתיות לבחור אלמנטים. כמה מהמועדפים שלי כוללים :is(), :where(), ו :not(), שכל אחד מהם נתמך על ידי כל הדפדפנים המודרניים והוא בטוח לשימוש כמעט בכל פרויקט בימינו.

:is() ו :where() הם בעצם אותו דבר חוץ מהאופן שבו הם משפיעים על הספציפיות. באופן ספציפי, :where() תמיד יש ציון ספציפי של 0,0,0. כן, אפילו :where(button#widget.some-class) אין ספציפיות. בינתיים, הספציפיות של :is() הוא האלמנט ברשימת הטיעונים שלו עם הספציפיות הגבוהה ביותר. אז, כבר יש לנו הבחנה מתפתלת בין שני סלקטורים מודרניים שאנחנו יכולים לעבוד איתם.

העוצמתיים להפליא :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.

בשימוש רשלני, מחלקות פסאודו אלה עלולות להשפיע על המפל בדרכים בלתי צפויות. וסוגי חוסר העקביות האלה יכולים ליצור כאבי ראש בהמשך הדרך, במיוחד בבסיסי קוד גדולים ומורכבים יותר.

דאנג. אז מה עכשיו?

תזכור על מה אמרתי :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() selector יכול לעזור לנו לכתוב הרבה פחות לוגיקה של תבנית - ופחות מחלקות BEM לאתחל - מבלי להוסיף לרמת הספציפיות.

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

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

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

הנה אותו דבר אבל ב-Sass (שים לב לעקרון אמפרסנדרים):

.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(), כמו בדוגמה לעיל, נוכל להתייחס למחלקה מורכבת ול- תָג.

/* ✅ 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() זוכה לתמיכה בפיירפוקס (זה נתמך כרגע מאחורי דגל בזמן הכתיבה) סביר להניח שנרצה לשייך אותו ל-:where() כדי לבטל את הספציפיות שלו.

בין אם אתה הולך על הכל על מתן שמות BEM ובין אם לא, אני מקווה שנוכל להסכים שעקביות בספציפיות הבורר היא דבר טוב!

בול זמן:

עוד מ טריקים של CSS