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

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

תמיכה בדפדפנים

  • Chrome: 105.
  • Edge:‏ 105.
  • Firefox: 110.
  • Safari: 16.

מקור

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

תמיכה בדפדפנים

  • Chrome: ‏ 111.
  • קצה: 111.
  • Firefox: לא נתמך.
  • Safari: 18.

מקור

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

המפרט של מודול הכליאה של CSS ברמה 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 על ידי הקצאת container-name ל-.card-list והפניה אליו בשאילתת הסגנון.

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

 <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 מעבר למאפיינים מותאמים אישית. האפשרות הזו כבר כלולה ברמת המפרט הנוכחית, אבל עדיין לא הופעלה באף דפדפן. הערכת ההקשר הבוליאני צפויה להתווסף לרמת המפרט הנוכחית אחרי פתרון הבעיה הקיימת, ואילו שאילתות טווח מתוכננות לרמה הבאה של המפרט.