הכלי לבחירת מדיה עשירה (בחירה בהתאמה אישית)

תכונות לאינטרנט: תג <select> שניתן להתאים אישית

בעבר היה קשה לעצב את הרכיב המקורי <select>, והוא יכול היה להכיל רק אפשרויות של טקסט פשוט. המאפיין appearance: base-select מציע דרך הצהרתית, ב-CSS בלבד, להצטרף למצב שניתן להתאמה אישית של הרכיב <select>. כך המפתחים יכולים לכלול תוכן HTML עשיר, כמו תמונות, קובצי SVG ופריסות מורכבות, בתוך רכיבי <option>, תוך שמירה על נגישות מקורית למקלדת ושילוב טפסים. התבנית הזו מאפשרת להחליף רכיבי בחירה כבדים ומותאמים אישית ברכיבים סטנדרטיים וייעודיים.

איך להטמיע

כדי להטמיע כלי לבחירת מדיה עשירה באמצעות Customizable Select API:

  1. הבעת הסכמה לסגנונות בסיסיים: מחילים את appearance: base-select גם על הרכיב <select> וגם על בורר הצבעים הפנימי שלו באמצעות הרכיב המדומה ::picker(select). הפעולה הזו משנה את מנתח ה-HTML של הדפדפן לתוכן שבתוך <select>.
  2. הגדרת תוכן הכפתור: משתמשים ברכיבים רגילים של <button> ו-<selectedcontent> בתוך <select> כדי להגדיר מה יוצג כשהבורר סגור. הרכיב <selectedcontent> משקף באופן אוטומטי את התוכן של האפשרות שנבחרה. השדה הזה נדרש אם רוצים להציג בכפתור את התוכן העשיר שנבחר כרגע.
  3. שימוש בתוכן עשיר בתוך Options: עכשיו אפשר להוסיף תמונות, קובצי SVG ותגי HTML אחרים בתוך תגי <option>. לפני ה-API הזה, הדפדפן היה מסיר תגים מתגי <option> ומציג רק טקסט פשוט.
  4. עיצוב ה-Popover: מעצבים את רשימת האפשרויות הנפתחת באמצעות הרכיב המדומה ::picker(select). הוא מוצג בשכבה העליונה, כך שאין צורך להיאבק עם z-index. האפשרויות ממוקמות באמצעות Anchor Positioning API באופן מקורי.

קוד לדוגמה: בורר תפקידים עשיר

<label for="role-picker">Select your role</label>
<select class="custom-select" name="role" id="role-picker">
  <button>
    <selectedcontent></selectedcontent> <!-- Mirrors the selected option's content automatically so you do not need JS to update the button -->
  </button>

  <!-- Define concise aria-label values on options whose mirrored rich content would otherwise read awkwardly as a concatenated string -->
  <option value="frontend" aria-label="Frontend Developer">
    <svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
      <rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect>
    </svg>
    <div class="option-text">
      <span class="option-title">Frontend Developer</span>
      <span class="option-desc">React, Vue, CSS</span>
    </div>
  </option>

  <option value="backend" aria-label="Backend Developer">
    <svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
      <rect x="2" y="12" width="20" height="14" rx="2" ry="2"></rect>
    </svg>
    <div class="option-text">
      <span class="option-title">Backend Developer</span>
      <span class="option-desc">Node.js, Python</span>
    </div>
  </option>
</select>
select.custom-select,
select.custom-select::picker(select) {
  appearance: base-select; /* MUST opt-in both the select and its picker to enable the customizable state, otherwise browser standard rendering applies */
}

select.custom-select {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 12px;
  background-color: #1e293b;
  border: 1px solid #334155;
  border-radius: 8px;
  color: #f1f5f9;
  cursor: pointer;
}

select.custom-select::picker(select) {
  background-color: #0f172a;
  border: 1px solid #334155;
  border-radius: 8px;
  padding: 8px;
  box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.5);
  width: anchor-size(width); /* Uses Anchor Positioning API to keep the dropdown precisely aligned to the button trigger width */
}

select.custom-select option {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px;
  border-radius: 4px;
  cursor: pointer;
}

select.custom-select option:hover {
  background-color: #1e293b;
}

/* Remove standard OS checkmark for base-select */
select.custom-select option::before {
  display: none;
}

/* MANDATORY: Provide multiple visual indicators (e.g., prominent background color and bold title font) to communicate the checked state cleanly */
select.custom-select option:checked {
  background-color: #3b82f6;
  color: #ffffff;
}
select.custom-select option:checked .option-title {
  font-weight: 700;
}

הטמעה אסטרטגית ושיטות מומלצות

  • מומלץ להשתמש ב-appearance: base-select כשצריך פריסות מורכבות של אפשרויות (סמלים, תיאורים, תמונות) שבעבר היו אפשריות רק עם מסגרות כבדות של ממשק משתמש ב-JavaScript.
  • אל תשתמשו ברכיבי אד-הוק אם אתם מבחינים בעיכובים בביצועים. הדפדפן מטפל במיקוד מקלדת מקורי באופן מקורי.
  • חשוב להביא בחשבון את הרינדור של השכבה העליונה. הכלי לבחירת קבצים מוצג בשכבה העליונה, כלומר הוא מבטל את ה-z-index היחסי של תוכן הדף.
  • מומלץ להסתיר פרטים משניים (כמו תיאורים) במצב הכפתור אם הם תופסים יותר מדי מקום, על ידי עיצוב של .custom-select selectedcontent .option-desc { display: none; }.
  • בודקים את ההתנהגות של הפריסה. ההגדרה appearance: base-select מסירה את התנהגות ברירת המחדל של הדפדפן, שבה גודל הבחירה נקבע לפי רוחב האפשרות הארוכה ביותר. יכול להיות שתצטרכו להגדיר רוחב קבוע או להשתמש באילוצים של flex/grid כדי למנוע שינויי פריסה.
  • מוודאים של-<select> יש מאפיין name ו-<label> משויך. כך אפשר לוודא שגם עם ממשק משתמש מותאם אישית, הרכיב נשאר נגיש לקוראי מסך ופועל בצורה תקינה עם שליחת טפסים רגילים.
  • כדי לוודא ששליחת הטופס פועלת בדיוק כמו קודם, צריך להשתמש במאפייני value רגילים באפשרויות.

שיטות חלופיות

חלופות ותמיכה בדפדפנים ל-<select> שניתן להתאמה אישית

בדפדפנים שעדיין לא תומכים ב-appearance: base-select, הרכיב <select> משתנה בצורה חלקה לתפריט נפתח רגיל של מערכת ההפעלה.

  • התעלמות מתוכן שאינו טקסט: דפדפנים ישנים יותר מסירים תגי HTML (כמו <svg> או <div>) בתוך תגי <option> ומציגים רק את צומתי הטקסט. חשוב לוודא שתוכן הטקסט של ה-<option> קריא ובעל משמעות בפני עצמו.
  • טיפול במבנה HTML: מנתחי HTML רגילים עשויים להתעלם מהתגים <button> ו-<selectedcontent> בתוך <select> או להתייחס אליהם כלא תקינים. אם אתם רואים טקסט רגיל כחלופה קריאה, אין צורך ב-polyfills כבדים של JavaScript לשיפור הדרגתי.
document.addEventListener("DOMContentLoaded", () => {
  // Check if browser supports base-select value
  if (!CSS.supports("appearance", "base-select")) {
    // Custom select overrides are not supported natively.
  }
});