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:
- Basisstile aktivieren: Wenden Sie
appearance: base-selectsowohl 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. - 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. - 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. - 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 mitz-indexherumschlagen 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-selectwhen 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-indexdes 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-selectfestlegen, 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>einname-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.
}
});