Elemen <select> native secara historis sulit untuk diberi gaya dan hanya dapat berisi opsi teks biasa. Properti appearance: base-select menawarkan cara deklaratif khusus CSS untuk mengaktifkan status yang dapat disesuaikan untuk elemen <select>. Hal ini memungkinkan developer menyertakan konten HTML multimedia—seperti gambar, SVG, dan tata letak yang kompleks—di dalam elemen <option>, sekaligus mempertahankan aksesibilitas keyboard native dan integrasi formulir. Gunakan pola ini untuk mengganti komponen pilihan yang berat dan dibuat khusus dengan elemen native standar.
Cara Menerapkan
Untuk menerapkan pemilih multimedia menggunakan Customizable Select API:
- Memilih ikut serta dalam gaya dasar: Terapkan
appearance: base-selectke elemen<select>dan pemilih internalnya menggunakan elemen semu::picker(select). Tindakan ini akan mengubah parser HTML browser untuk konten di dalam<select>. - Tentukan Konten Tombol: Gunakan elemen
<button>dan<selectedcontent>standar di dalam<select>untuk menentukan apa yang ditampilkan saat pemilih ditutup. Elemen<selectedcontent>otomatis mencerminkan konten opsi yang dipilih. Hal ini diperlukan jika Anda ingin menampilkan konten lengkap yang saat ini dipilih di tombol. - Menggunakan Konten Lengkap di dalam Opsi: Anda kini dapat menempatkan gambar, SVG, dan tag HTML lainnya di dalam tag
<option>. Sebelum API ini, browser akan menghapus tag dari tag<option>dan hanya merender teks biasa. - Menata Gaya Popover: Tata gaya daftar opsi drop-down dengan menargetkan elemen pseudo
::picker(select). Elemen ini dirender di lapisan atas, yang berarti Anda tidak perlu bersaing denganz-index. Opsi diposisikan menggunakan Anchor Positioning API secara native.
Contoh Kode: Pemilih Peran Kaya
<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;
}
Penerapan Strategis & Praktik Terbaik
- LAKUKAN penggunaan
appearance: base-selectsaat Anda memerlukan tata letak opsi yang kompleks (ikon, deskripsi, gambar) yang sebelumnya hanya dapat dilakukan dengan framework UI JavaScript yang berat. - JANGAN gunakan elemen ad-hoc jika Anda melihat adanya keterlambatan performa; browser menangani fokus keyboard native secara native.
- LAKUKAN pertimbangan untuk rendering lapisan atas. Alat pilih dirender di lapisan atas, yang berarti alat ini mengganti
z-indexrelatif konten halaman. - LAKUKAN penyembunyian detail sekunder (seperti deskripsi) dalam status tombol jika terlalu banyak memakan ruang, dengan menata gaya
.custom-select selectedcontent .option-desc { display: none; }. - Menguji perilaku tata letak. Menetapkan
appearance: base-selectakan menghapus perilaku browser default yang mengubah ukuran pilihan berdasarkan lebar opsi terpanjangnya. Anda mungkin perlu menetapkan lebar tetap atau menggunakan batasan flex/grid untuk mencegah pergeseran tata letak. - Pastikan
<select>Anda memiliki atributnamedan<label>terkait. Hal ini memastikan bahwa meskipun dengan UI kustom, komponen tetap dapat diakses oleh pembaca layar dan berfungsi dengan benar dengan pengiriman formulir standar. - Gunakan atribut
valuestandar pada opsi untuk memastikan pengiriman formulir berfungsi sama persis seperti sebelumnya.
Strategi Penggantian
Penggantian & dukungan browser untuk <select> yang Dapat Disesuaikan
Untuk browser yang belum mendukung appearance: base-select, elemen <select> akan menurun secara halus menjadi dropdown sistem operasi standar.
- Konten Non-Teks Diabaikan: Browser lama menghapus tag HTML (seperti
<svg>atau<div>) di dalam tag<option>dan hanya merender node teks. Pastikan konten teks<option>dapat dibaca dan bermakna dengan sendirinya. - Penanganan Struktur HTML: Parser standar dapat mengabaikan tag
<button>dan<selectedcontent>di dalam<select>atau memperlakukannya sebagai tidak valid. Polyfill JavaScript yang berat tidak mutlak diperlukan untuk progressive enhancement jika Anda menganggap teks standar sebagai pengganti yang dapat dibaca.
document.addEventListener("DOMContentLoaded", () => {
// Check if browser supports base-select value
if (!CSS.supports("appearance", "base-select")) {
// Custom select overrides are not supported natively.
}
});