היתרונות של שימוש במאפיינים מותאמים אישית במערכות עיצוב ובספריות רכיבים.
קוראים לי דייב ואני מפתח Front-end בכיר ב-Nordhealth. אני עובדת על העיצוב והפיתוח של מערכת העיצוב Nord, כולל פיתוח רכיבי אינטרנט לספריית הרכיבים שלנו. רציתי לשתף איך פתרנו את הבעיות שקשורות לעיצוב של רכיבי אינטרנט באמצעות מאפיינים מותאמים אישית של CSS, ואת חלק מהיתרונות האחרים של שימוש במאפיינים מותאמים אישית במערכות עיצוב ובספריות רכיבים.
איך אנחנו יוצרים רכיבי אינטרנט
כדי ליצור את רכיבי ה-Web שלנו, אנחנו משתמשים ב-Lit, ספרייה שמספקת הרבה קוד סטנדרטי כמו מצב, סגנונות ברמת ההיקף, תבניות ועוד. Lit הוא לא רק קל, אלא גם מבוסס על ממשקי API מקומיים של JavaScript. המשמעות היא שאנחנו יכולים לספק חבילת קוד יעילה שמנצלת את התכונות שכבר קיימות בדפדפן.
אבל היתרון הכי משמעותי של רכיבי ה-Web הוא שהם פועלים עם כמעט כל מסגרת JavaScript קיימת, או אפילו בלי מסגרת בכלל. אחרי שמפנים לחבילת ה-JavaScript הראשית בדף, השימוש ברכיב אינטרנט דומה מאוד לשימוש ברכיב HTML מקורי. הסימן היחיד שמבטיח שמדובר לא באלמנט HTML מקורי הוא מקף עקום עקבי בתוך התגים. זהו סטנדרט שמציין לדפדפן שמדובר ברכיב אינטרנט.
אנקפסולציה בסגנון Shadow DOM
בדומה לרכיבי HTML מקומיים שיש להם Shadow DOM, כך גם לרכיבי Web Components יש Shadow DOM. Shadow DOM הוא עץ מוסתר של צמתים בתוך רכיב. הדרך הטובה ביותר להמחיש את זה היא לפתוח את בודק האתר ולהפעיל את האפשרות 'הצגת עץ Shadow DOM'. אחרי שתעשו זאת, נסו לבדוק רכיב קלט מקורי בבודק. עכשיו תוכלו לפתוח את הקלט הזה ולראות את כל הרכיבים שבתוכו. אפשר לנסות את זה גם עם אחד מרכיבי ה-Web שלנו – נסו לבדוק את רכיב הקלט בהתאמה אישית שלנו כדי לראות את Shadow DOM שלו.
אחד מהיתרונות (או החסרונות, תלוי אתם) של Shadow DOM הוא אנקפסולציה של סגנונות. אם כותבים CSS בתוך רכיב האינטרנט, הסגנונות האלה לא יכולים לדלוף החוצה ולהשפיע על הדף הראשי או על אלמנטים אחרים. הם נכללים לחלוטין ברכיב. בנוסף, קוד CSS שנכתב לדף הראשי או לרכיב הורה של רכיב אינטרנט לא יכול לדלוף לרכיב האינטרנט.
האנקפסולציה של הסגנונות היא יתרון בספריית הרכיבים שלנו. כך אנחנו יכולים להבטיח יותר שכשמישהו ישתמש באחד מהרכיבים שלנו, הוא ייראה כמו שרצינו, ללא קשר לסגנונות שחלים על דף ההורה. כדי לוודא זאת, אנחנו מוסיפים את all: unset;
לשורש (root) או ל'מארח' של כל רכיבי ה-Web שלנו.
עם זאת, מה קורה אם למישהו שמשתמש ברכיב האינטרנט שלכם יש סיבה לגיטימית לשנות סגנונות מסוימים? אולי יש שורה של טקסט שצריך להגדיל את הניגודיות שלה בגלל ההקשר שלה, או שצריך להגדיל את עובי השוליים? אם אין סגנונות שיכולים להיכנס לרכיב, איך אפשר לבטל את נעילת אפשרויות העיצוב האלה?
כאן נכנסים לתמונה מאפייני CSS מותאמים אישית.
מאפיינים מותאמים אישית של CSS
מאפיינים מותאמים אישית הם מאפייני CSS שאתם יכולים לתת להם שם ולקבוע להם את הערך הרצוי. הדרישה היחידה היא להוסיף להן שתי מקפים בתחילת השם. אחרי שמצהירים על המאפיין המותאם אישית, אפשר להשתמש בערך ב-CSS באמצעות הפונקציה var()
.
לגבי ירושה, כל המאפיינים המותאמים אישית עוברים בירושה, בהתאם להתנהגות הרגילה של ערכים ומאפיינים רגילים של CSS. אפשר להשתמש בכל מאפיין מותאם אישית שהוחל על רכיב הורה, או על הרכיב עצמו, כערך במאפיינים אחרים. אנחנו משתמשים הרבה במאפיינים מותאמים אישית עבור אסימוני העיצוב שלנו, על ידי החלה שלהם על רכיב הבסיס באמצעות מסגרת ה-CSS שלנו. כלומר, כל הרכיבים בדף יכולים להשתמש בערכי האסימונים האלה, בין אם מדובר ברכיב אינטרנט, בכיתה מסייעת של CSS או במפתח שרוצה לבחור ערך מרשימת האסימונים שלנו.
היכולת לרשת מאפיינים מותאמים אישית, באמצעות הפונקציה var()
, מאפשרת לנו לחדור ל-Shadow DOM של רכיבי האינטרנט שלנו ולתת למפתחים שליטה מפורטת יותר כשהם מעצבים את הרכיבים.
מאפיינים מותאמים אישית ברכיב אינטרנט של Nord
בכל פעם שאנחנו מפתחים רכיב למערכת העיצוב שלנו, אנחנו מתייחסים ל-CSS שלו בצורה מושכלת – אנחנו שואפים לקוד פשוט אבל קל לתחזוקה. אסימוני העיצוב שלנו מוגדרים כמאפיינים מותאמים אישית במסגרת ה-CSS הראשית שלנו ברכיב הבסיס.
לאחר מכן, אנחנו מפנים לערכי האסימונים האלה במרכיבים שלנו. במקרים מסוימים, נחייב את הערך ישירות על מאפיין ה-CSS, אבל במקרים אחרים נגדיר למעשה מאפיין מותאם אישית חדש לפי הקשר ונחיל את הערך עליו.
בנוסף, נבצע הפשטה של ערכים מסוימים שספציפיים לרכיב אבל לא נמצאים באסימונים שלנו, ונהפוך אותם לנכס מותאם אישית לפי הקשר. מאפיינים מותאמים אישית שמותאמים להקשר של הרכיב מספקים לנו שני יתרונות עיקריים. ראשית, המשמעות היא שאנחנו יכולים להשתמש ב-CSS בצורה 'יבשה' יותר, כי אפשר להחיל את הערך הזה על כמה מאפיינים בתוך הרכיב.
שנית, השיטה הזו מאפשרת לבצע שינויים במצב הרכיב ובגרסה בצורה נקייה מאוד – צריך לשנות רק את המאפיין המותאם אישית כדי לעדכן את כל המאפיינים האלה, למשל כשאתם מעצבים מצב של מעבר עכבר או מצב פעיל, או במקרה הזה, וריאנט.
אבל היתרון המשמעותי ביותר הוא שכאשר אנחנו מגדירים את המאפיינים המותאמים אישית לפי הקשר הזה ברכיב, אנחנו יוצרים מעין ממשק API מותאם אישית של CSS לכל אחד מהרכיבים שלנו, והמשתמש ברכיב יכול להשתמש בו.
בדוגמה הקודמת מוצג אחד מרכיבי ה-Web שלנו עם נכס מותאם אישית לפי הקשר ששונה באמצעות בורר. התוצאה של הגישה הזו היא רכיב שמספק למשתמש גמישות מספקת בבחירת סגנון, תוך שמירה על רוב הסגנונות בפועל. בנוסף, בתור מפתחי רכיבים, יש לנו את היכולת ליירט את הסגנונות שהמשתמשים מחילים. אם אנחנו רוצים לשנות או להרחיב אחד מהמאפיינים האלה, אנחנו יכולים לעשות זאת בלי שהמשתמש יצטרך לשנות את הקוד שלו.
הגישה הזו יעילה מאוד, לא רק עבורנו כיוצרים של רכיבי מערכת העיצוב שלנו, אלא גם עבור צוות הפיתוח שלנו כשהם משתמשים ברכיבים האלה במוצרים שלנו.
שימוש נוסף במאפיינים מותאמים אישית
נכון למועד כתיבת המאמר, אנחנו לא חושפים את המאפיינים המותאמים אישית לפי הקשר האלה במסמכי התיעוד שלנו. עם זאת, אנחנו מתכננים לעשות זאת כדי שצוות הפיתוח הרחב שלנו יוכל להבין את המאפיינים האלה ולנצל אותם. הרכיבים שלנו ארוזים ב-npm עם קובץ מניפסט, שמכיל את כל מה שצריך לדעת עליהם. לאחר מכן אנחנו משתמשים בקובץ המניפסט כנתונים כשאנחנו פורסים את אתר המסמכים, באמצעות Eleventy והתכונה Global Data שלו. אנחנו מתכננים לכלול את המאפיינים המותאמים אישית לפי הקשר הזה בקובץ הנתונים של המניפסט.
תחום נוסף שאנחנו רוצים לשפר הוא האופן שבו נכסי ה-Custom Properties לפי הקשר יורשים ערכים. לדוגמה, נכון לעכשיו, אם רוצים לשנות את הצבע של שני רכיבי מפריד, צריך לטרגט את שני הרכיבים האלה באופן ספציפי באמצעות בוחרים, או להחיל את המאפיין המותאם אישית ישירות על האלמנט באמצעות מאפיין הסגנון. זה נראה בסדר, אבל יהיה שימושי יותר אם המפתח יוכל להגדיר את הסגנונות האלה ברכיב מכיל או אפילו ברמת הבסיס.
הסיבה לכך שצריך להגדיר את הערך של המאפיין המותאם אישית ישירות ברכיב היא שאנחנו מגדירים אותם באותו רכיב באמצעות הבורר של מארח הרכיב. אסימוני העיצוב הגלובליים שבהם אנחנו משתמשים ישירות ברכיב עוברים ישירות בלי להיפגע מהבעיה הזו, ואפשר אפילו ליירט אותם ברכיבי הורה. איך אפשר ליהנות משני העולמות?
מאפיינים מותאמים אישית פרטיים וציבוריים
מאפיינים מותאמים אישית פרטיים הם רעיון של Lea Verou. מדובר במאפיין מותאם אישית 'פרטי' לפי הקשר ברכיב עצמו, אבל מוגדר כמאפיין מותאם אישית 'ציבורי' עם חלופה.
הגדרת המאפיינים המותאמים אישית לפי הקשר באופן הזה מאפשרת לנו להמשיך לעשות את כל הדברים שעשינו בעבר, כמו לקבל בירושה ערכים של אסימונים גלובליים ולעשות שימוש חוזר בערכים בקוד הרכיב שלנו. אבל הרכיב יקבל בירושה גם הגדרות חדשות של המאפיין הזה על עצמו או על כל רכיב הורה.
אפשר לטעון שהשיטה הזו לא באמת "פרטית", אבל אנחנו עדיין חושבים שזה פתרון אלגנטי למדי לבעיה שחששנו ממנה. כשהזדמנות כזו תופיע, נשתמש בה כדי לטפל בבעיה הזו ברכיבים שלנו, כדי שלצוות הפיתוח תהיה יותר שליטה על השימוש ברכיבים, ועדיין ליהנות מהאמצעי להגנה שהצבנו.
אני מקווה שהמידע הזה על האופן שבו אנחנו משתמשים ברכיבי אינטרנט עם מאפייני CSS מותאמים אישית היה שימושי. נשמח לשמוע את דעתכם. אם תחליטו להשתמש באחת מהשיטות האלה בעבודה שלכם, תוכלו למצוא אותי ב-Twitter @DavidDarnes. אפשר למצוא את Nordhealth גם ב-Twitter @NordhealthHQ, וגם את שאר חברי הצוות שלי, שעבדו קשה כדי ליצור את מערכת העיצוב הזו ולהטמיע את התכונות שצוינו במאמר הזה: @Viljamis, @WickyNilliams ו-@eric_habich.
תמונה ראשית (Hero) של Dan Cristian Pădureț