תחילת העבודה עם שאילתות סגנון

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

תמיכה בדפדפן

  • Chrome: 105.
  • קצה: 105.
  • Firefox: 110.
  • Safari: 16.

מקור

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

תמיכה בדפדפן

  • Chrome: 111.
  • Edge:‏ 111.
  • Firefox: לא נתמך.
  • Safari: 18.

מקור

המשמעות היא שיש לנו שליטה לוגית רבה יותר על סגנונות ב-CSS, ואפשר להפריד טוב יותר בין הלוגיקה של האפליקציה ושכבת הנתונים שלה לבין הסגנונות שלה.

המפרט של CSS Containment Module Level 3, שכולל שאילתות גודל וסגנון, מאפשר לשלוח שאילתות לגבי כל סגנונות מהורה, כולל צמדי מאפיין וערך, כמו font-weight: 800. עם זאת, בשלב ההשקה של התכונה הזו, שאילתות סגנון פועלות כרגע רק עם ערכי מאפיינים מותאמים אישית של CSS. השיטה הזו עדיין שימושית מאוד לשילוב סגנונות ולהפרדת נתונים מעיצוב. נראה איך משתמשים בשאילתות סגנון עם מאפיינים מותאמים אישית של CSS:

תחילת העבודה עם שאילתות סגנון

נניח שיש לנו את הקוד הבא ב-HTML:

<ul class="card-list">
  <li class="card-container">
    <div class="card">
      ...
    </div>
  </li>
</ul>

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

שליחת שאילתות להורים ישירים

תרשים של שאילתת סגנון.

בניגוד לשאילתות סגנון, אין צורך להחיל בלימת גבולות באמצעות המאפיינים container-type או container על .card-container כדי של-.card תהיה אפשרות לשלוח שאילתות על הסגנונות של ההורה הישיר. עם זאת, עלינו להחיל את הסגנונות (ערכי מאפיינים מותאמים אישית במקרה הזה) על קונטיינר (.card-container במקרה הזה) או על כל רכיב שמכיל את הרכיב שאנחנו מעצבים ב-DOM. אנחנו לא יכולים להחיל סגנונות שעליהם אנחנו שולחים שאילתות על האלמנט הישיר שאנחנו מעצבים באמצעות השאילתה הזו, כי הפעולה הזו עלולה לגרום ללולאות אינסופיות.

כדי לשלוח שאילתה ישירות להורה, אפשר לכתוב:

/* styling .card based on the value of --theme on .card-container */
@container style(--theme: warm) {
  .card {
    background-color: wheat;
    border-color: brown; 
    ...
  }
}

יכול להיות ששמתם לב ששאילתת הסגנון עוטפת את השאילתה ב-style(). המטרה היא להבדיל בין ערכי גודל לבין סגנונות. לדוגמה, אפשר לכתוב שאילתה על הרוחב של מאגר התגים בתור @container (min-width: 200px) { … }. הסגנונות יחולו אם מאגר האב יהיה ברוחב של 200 פיקסלים לפחות. עם זאת, min-width יכול להיות גם מאפיין CSS, ואפשר לשלוח שאילתה לגבי הערך של min-width ב-CSS באמצעות שאילתות סגנון. לכן כדאי להשתמש ב-wrapper של style() כדי להבהיר את ההבדל: @container style(min-width: 200px) { … }.

עיצוב של הורים לא ישירים

אם רוצים לשלוח שאילתה לגבי סגנונות של רכיב שאינו הורה ישיר, צריך להקצות לרכיב הזה container-name. לדוגמה, אפשר להחיל סגנונות על .card על סמך הסגנונות של .card-list. לשם כך, נותנים ל-.card-list את הערך container-name, ומפנים אליו בשאילתת הסגנון.

/* styling .card based on the value of --moreGlobalVar on .card-list */
@container cards style(--moreGlobalVar: value) {
  .card {
    ...
  }
}

באופן כללי, מומלץ לתת למאגרי התגים שמות כדי שיהיה ברור מה השאילתה וכדי לאפשר גישה קלה יותר לקונטיינרים האלה. דוגמה אחת לאפשרות הזו היא אם רוצים לעצב רכיבים ישירות בתוך .card. בלי קונטיינר בעל שם ב-.card-container, הם לא יכולים לשלוח שאילתה ישירות אליו.

אבל בפועל, הרבה יותר הגיוני. דוגמאות:

שאילתות סגנון בפעולה

תמונה לדוגמה עם כמה כרטיסי מוצרים, חלקם עם תגים &#39;חדש&#39; או &#39;מלאי מוגבל&#39;, והכרטיס &#39;מלאי מוגבל&#39; עם רקע אדום.

שאילתות סגנון שימושיות במיוחד כאשר יש רכיב לשימוש חוזר עם מספר וריאציות, או כאשר אין לכם שליטה על כל הסגנונות אבל אתם צריכים להחיל שינויים במקרים מסוימים. בדוגמה הזו מוצגת קבוצה של כרטיסי מוצרים שיש להם את אותו רכיב כרטיס. בכרטיסי מוצרים מסוימים מופיעים פרטים או הערות נוספים, כמו 'חדש' או 'מלאי נמוך', שמופעל על ידי מאפיין מותאם אישית בשם --detail. בנוסף, אם מוצר נמצא בסטטוס 'מלאי נמוך', הוא יופיע עם גבול אדום עמוק. סביר להניח שהמידע הזה עובר רינדור על ידי השרת, ואפשר להחיל אותו על הכרטיסים באמצעות סגנונות מוטבעים, כמו בדוגמה הבאה:

 <div class="product-list">
  <div class="product-card-container" style="--detail: new">
    <div class="product-card">
      <div class="media">
        <img .../>
      <div class="comment-block"></div>
    </div>
  </div>
  <div class="meta">
    ...
  </div>
  </div>
  <div class="product-card-container" style="--detail: low-stock">
    ...
  </div>
  <div class="product-card-container">
    ...
  </div>
  ...
</div>

בעזרת הנתונים המובְנים האלה, אפשר להעביר ערכים אל --detail ולהשתמש במאפיין ה-CSS המותאם אישית הזה כדי להחיל את הסגנונות:

@container style(--detail: new) {
  .comment-block {
    display: block;
  }
  
  .comment-block::after {
    content: 'New';
    border: 1px solid currentColor;
    background: white;
    ...
  }
}

@container style(--detail: low-stock) {
  .comment-block {
    display: block;
  }
  
  .comment-block::after {
    content: 'Low Stock';
    border: 1px solid currentColor;
    background: white;
    ...
  }
  
  .media-img {
    border: 2px solid brickred;
  }
}

הקוד שלמעלה מאפשר לנו להחיל צ'יפ בשביל --detail: low-stock ו---detail: new, אבל יכול להיות שהבחנתם ליתירות בבלוק הקוד. בשלב זה אין אפשרות להריץ שאילתות רק על נוכחות של --detail עם @container style(--detail), מה שיאפשר שיתוף יעיל יותר של סגנונות ופחות חזרות. היכולת הזו נמצאת כרגע בבדיקה בקבוצת העבודה.

כרטיסי מזג אוויר

בדוגמה הקודמת השתמשנו במאפיין מותאם אישית אחד עם כמה ערכים אפשריים כדי להחיל סגנונות. אבל אפשר גם לשלב בין כמה נכסים מותאמים אישית ולהשתמש בהם בשאילתות. ניקח לדוגמה את כרטיס מזג האוויר הזה:

הדגמה של כרטיסי מזג האוויר.

כדי לקבוע את הסגנון של סמלי הרקע והפסים בצבעים משתנים בכרטיסים האלה, מחפשים מאפייני מזג אוויר כמו 'מעונן', 'גשום' או 'שמשי':

@container style(--sunny: true) {
  .weather-card {
    background: linear-gradient(-30deg, yellow, orange);
  }
  
  .weather-card:after {
    content: url(<data-uri-for-demo-brevity>);
    background: gold;
  }
}

כך אפשר לעצב כל כרטיס בהתאם למאפיינים הייחודיים שלו. אך ניתן גם לעצב עבור שילובים של מאפיינים (נכסים מותאמים אישית) באמצעות השילוב של and באותו אופן כמו של שאילתות מדיה. לדוגמה, יום שהוא גם מעונן וגם שמשי ייראה כך:

@container style(--sunny: true) and style(--cloudy: true) {
    .weather-card {
      background: linear-gradient(24deg, pink, violet);
    }
  
  .weather-card:after {
      content: url(<data-uri-for-demo-brevity>);
      background: violet;
  }
}

הפרדת נתונים מהעיצוב

בשתי ההדגמות האלה, יש יתרון מבני של הפרדת שכבת הנתונים (DOM שתעובד בדף) מהסגנונות המוחלים. הסגנונות נכתבים בתור וריאציות אפשריות שמתאימות לסגנון הרכיבים, ונקודת קצה יכולה לשלוח את הנתונים שישמשו לאחר מכן כדי לעצב את הרכיב לפי סגנון הרכיבים. אפשר להשתמש בערך יחיד, כמו במקרה הראשון, ולעדכן את הערך --detail, או במספר משתנים, כמו במקרה השני (הגדרת --rainy או --cloudy או --sunny. היתרון הכי חשוב הוא שאפשר לשלב גם את הערכים האלה, כך שבדיקה של --sunny ושל --cloudy עשויה להראות מעונן חלקית.

אפשר לעדכן את ערכי המאפיינים בהתאמה אישית באמצעות JavaScript בצורה חלקה, גם בזמן הגדרת מודל ה-DOM (כלומר בזמן פיתוח הרכיב במסגרת) וגם בכל שלב באמצעות <parentElem>.style.setProperty('--myProperty’, <value>). I

הנה דוגמה להדגמה שבה כמה שורות קוד מעדכנות את --theme של לחצן ומחילות סגנונות באמצעות שאילתות סגנון והמאפיין המותאם אישית הזה (--theme):

מעצבים את הכרטיס באמצעות שאילתות סגנון. קוד ה-JavaScript שמשמש לעדכון הערכים של המאפיינים המותאמים אישית הוא:

const themePicker = document.querySelector('#theme-picker')
const btnParent = document.querySelector('.btn-section');

themePicker.addEventListener('input', (e) => {
  btnParent.style.setProperty('--theme', e.target.value);
})

התכונות שמפורטות במאמר הזה הן רק ההתחלה. שאילתות בקונטיינרים יכולות לעזור לכם ליצור ממשקים דינמיים ורלוונטיים. ספציפית לשאילתות סגנון, עדיין יש כמה בעיות פתוחות. אחת היא הטמעת שאילתות סגנון עבור סגנונות CSS מעבר לנכסים מותאמים אישית. האפשרות הזו כבר כלולה ברמת המפרט הנוכחית, אבל עדיין לא הופעלה באף דפדפן. הערכת ההקשר הבוליאני צפויה להתווסף לרמת המפרט הנוכחית אחרי פתרון הבעיה הקיימת, ואילו שאילתות טווח מתוכננות לרמה הבאה של המפרט.