Rich Media-Auswahl (anpassbare Auswahl)

Webfunktionen: Anpassbares <select>

Das native <select>-Element war bisher schwer zu gestalten und konnte nur Nur-Text-Optionen enthalten. Die Property appearance: base-select bietet eine deklarative, reine CSS-Methode, um einen anpassbaren Status für das <select>-Element zu aktivieren. So können Entwickler Rich-HTML-Inhalte wie Bilder, SVGs und komplexe Layouts in <option>-Elemente einfügen und gleichzeitig die native Tastaturbedienung und Formularintegration beibehalten. Verwenden Sie dieses Muster, um komplexe, benutzerdefinierte Auswahlelemente durch standardmäßige, native Elemente zu ersetzen.

Implementierung

So implementieren Sie eine Rich-Media-Auswahl mit der Customizable Select API:

  1. Basisstile aktivieren: Wenden Sie appearance: base-select sowohl auf das <select>-Element als auch auf die interne Auswahl mit dem Pseudoelement ::picker(select) an. Dadurch wird der HTML-Parser des Browsers für den Inhalt innerhalb des <select> geändert.
  2. Buttoninhalt definieren: Verwenden Sie Standard-<button>- und <selectedcontent>-Elemente innerhalb des <select>-Elements, um festzulegen, was angezeigt wird, wenn die Auswahl geschlossen ist. Das <selectedcontent>-Element spiegelt automatisch den Inhalt der ausgewählten Option wider. Dies ist erforderlich, wenn Sie den aktuell ausgewählten Rich Content auf der Schaltfläche anzeigen möchten.
  3. Rich Content in Optionen verwenden: Sie können jetzt Bilder, SVGs und andere HTML-Tags in <option>-Tags platzieren. Vor dieser API wurden Tags aus <option>-Tags entfernt und nur Nur-Text gerendert.
  4. Popover gestalten: Gestalten Sie die Drop-down-Liste mit den Optionen, indem Sie das Pseudoelement ::picker(select) als Ziel festlegen. Es wird in der obersten Ebene gerendert, sodass Sie sich nicht mit z-index herumschlagen müssen. Optionen werden nativ über die Anchor Positioning API positioniert.

Beispielcode: Rich Role Picker

<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;
}

Strategische Implementierung und Best Practices

  • DO use appearance: base-select when you need complex options layouts (icons, descriptions, images) that was previously only possible with heavy JavaScript UI frameworks.
  • Verwenden Sie KEINE Ad-hoc-Elemente, wenn Sie Leistungseinbußen feststellen. Der Browser verarbeitet den nativen Tastaturfokus nativ.
  • BERÜCKSICHTIGEN Sie das Rendering der obersten Ebene. Die Auswahl wird auf der obersten Ebene gerendert, d. h., sie überschreibt den relativen z-index des Seiteninhalts.
  • EMPFOHLEN: Sekundäre Details (z. B. Beschreibungen) im Schaltflächenstatus ausblenden, wenn sie zu viel Platz einnehmen, indem Sie .custom-select selectedcontent .option-desc { display: none; } formatieren.
  • Layoutverhalten testen Wenn Sie appearance: base-select festlegen, wird das Standardverhalten des Browsers entfernt, bei dem die Größe des Auswahlfelds anhand der Breite der längsten Option festgelegt wird. Möglicherweise müssen Sie eine feste Breite festlegen oder Flex-/Grid-Einschränkungen verwenden, um Layout Shifts zu verhindern.
  • Achte darauf, dass dein <select> ein name-Attribut und ein zugehöriges <label> hat. So bleibt die Komponente auch bei einer benutzerdefinierten UI für Screenreader zugänglich und funktioniert korrekt mit Standardformularübermittlungen.
  • Verwenden Sie standardmäßige value-Attribute für Optionen, damit das Senden von Formularen genau wie zuvor funktioniert.

Fallback-Strategien

Fallbacks und Browserunterstützung für anpassbare <select>

In Browsern, die appearance: base-select noch nicht unterstützen, wird das <select>-Element zu einem Standard-Dropdown-Menü des Betriebssystems herabgestuft.

  • Nicht-Text-Inhalte werden ignoriert: Ältere Browser entfernen HTML-Tags (wie <svg> oder <div>) innerhalb von <option>-Tags und rendern nur die Textknoten. Der Textinhalt des <option>-Elements muss lesbar und für sich genommen aussagekräftig sein.
  • Umgang mit HTML-Strukturen: Standardparser ignorieren möglicherweise die <button>- und <selectedcontent>-Tags innerhalb von <select> oder behandeln sie als ungültig. Für die progressive Verbesserung sind keine umfangreichen JavaScript-Polyfills erforderlich, wenn Sie Standardtext als lesbaren Fallback betrachten.
document.addEventListener("DOMContentLoaded", () => {
  // Check if browser supports base-select value
  if (!CSS.supports("appearance", "base-select")) {
    // Custom select overrides are not supported natively.
  }
});