Özelleştirilebilir select API, <select> öğelerini ve açılır seçicilerini canlandırmak için bildirim temelli, CSS tabanlı bir yöntem sunar. appearance: base-select öğesini @starting-style ve allow-discrete geçiş davranışı gibi modern CSS animasyon teknikleriyle birleştirerek ağır JavaScript kitaplıklarına ihtiyaç duymadan üst katman öğeleri için akıcı ve premium kullanıcı arayüzü geçişleri oluşturabilirsiniz.
Daha önce, yerel seçim açılır listelerinin kullanıcı arayüzü, erişilebilir görüntü alanı kısıtlamalarının dışında oluşturulduğu için animasyon eklemek mümkün değildi. appearance: base-select ile seçici, diğer sayfa öğeleri gibi stillendirilebilir ve animasyonlu hale getirilebilir.
Nasıl uygulanır?
Animasyonlu seçim seçici uygulamak için:
- Özelleştirmeyi etkinleştirme:
appearance: base-selectözelliğini hem<select>öğesine hem de::picker(select)sözde öğesine uygulayın. - Otomatik boyutlandırma geçişlerini etkinleştirme (isteğe bağlı): Tarayıcının
height: autoveheight: 0gibi ayrı metrik değerleri arasında geçiş yapmasına izin vermek içininterpolate-size: allow-keywords(genellikle:rootüzerinde) tanımlayın. - Üst katman kapsayıcısına animasyon ekleme:
::picker(select)öğesine standart giriş/çıkış stilleri uygulayın.display: nonevedisplay: blockarasında geçiş yaparken opaklık geçişinin çalıştığından emin olmak içintransition-behavior: allow-discrete(genellikle satır içi olaraktransition: display 0.3s allow-discreteşeklinde yazılır) kullanmanız gerekir. @starting-styleile açılış durumuna bağlanın: Tarayıcının geçiş başlamadan önce hesaplaması gereken temel stilleri tanımlamak için@starting-stylekullanın. Örneğin, öğenin yavaşça görünmesini istiyorsanız@starting-stylebloğunda opaklığı0olarak ayarlayın.- Simgeyi döndürme: Ok göstergesine geçişler (ör. döndürme veya çevirme) uygulamak için sözde öğe odağını veya
:open::picker-icongibi etkin seçicileri kullanın.
Örnek Kod: Yumuşak Seçim Ölçeği ve Soldurma
Aşağıdaki örnekte, seçici kapsayıcısı için standart sayfa animasyonlarıyla stillendirilmiş özel bir seçim gösterilmektedir.
<!-- Always use a <label> linked via 'for' to the select for accessibility -->
<label for="theme-select">Visual Theme</label>
<select id="theme-select" class="animated-select" name="theme">
<!-- The <button> inside <select> becomes the visible trigger when appearance: base-select is used -->
<button>
<!-- <selectedcontent> automatically displays the content of the chosen <option> -->
<selectedcontent></selectedcontent>
</button>
<option value="system">
<!-- MANDATORY: Decorative inline SVGs MUST set aria-hidden="true" to prevent redundant screen reader announcement -->
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect>
<line x1="8" y1="21" x2="16" y2="21"></line>
<line x1="12" y1="17" x2="12" y2="21"></line>
</svg>
System Default
</option>
<option value="light">
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
Light UI
</option>
</select>
/* Opt-in to customizable select */
.animated-select,
.animated-select::picker(select) {
appearance: base-select;
}
/* Enable auto-keyword transitions (usually set globally at :root) */
:root {
interpolate-size: allow-keywords;
}
/* Style the visible trigger and icon rotation */
.animated-select {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 0.875rem 1rem;
font-size: 1rem;
border-radius: 8px;
cursor: pointer;
transition: border-color 0.2s ease;
}
.animated-select::picker-icon {
transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}
.animated-select:open::picker-icon {
transform: rotate(180deg);
}
/*
* The Picker Container
* Uses top-layer animations with `allow-discrete` visibility hooks
*/
.animated-select::picker(select) {
background: white;
border-radius: 12px;
box-shadow: 0 10px 25px -3px rgba(0,0,0,0.1);
padding: 0.5rem;
margin-top: 0.25rem;
width: anchor-size(width);
overflow: hidden;
/* The crucial transition setting for popover animations */
transition:
display 0.4s allow-discrete,
overlay 0.4s allow-discrete,
opacity 0.4s cubic-bezier(0.16, 1, 0.3, 1),
height 0.4s cubic-bezier(0.16, 1, 0.3, 1);
opacity: 0;
height: 0;
}
/* Open State */
.animated-select:open::picker(select) {
opacity: 1;
height: auto;
}
/* @starting-style to hook the transition on initial popover open */
@starting-style {
.animated-select:open::picker(select) {
opacity: 0;
height: 0;
}
}
/* Support for SVG inside Options and Selected Content */
.animated-select option svg,
.animated-select selectedcontent svg {
flex-shrink: 0; /* Prevent icons from shrinking */
width: 1.25rem;
height: 1.25rem;
}
/* MANDATORY: Provide multiple indicators (e.g. bold font and distinct background) for the checked state to avoid color-only state communication */
.animated-select option:checked {
font-weight: 700;
background-color: #f1f5f9;
}
/* Ensure copy-paste safety for users with motion sensitivities */
@media (prefers-reduced-motion: reduce) {
.animated-select::picker(select),
.animated-select::picker-icon {
transition: none !important;
}
}
Stratejik Uygulama ve En İyi Uygulamalar
- Bir öğe
display: nonedurumundan görünür duruma geçtiğinde animasyonların tam olarak tetiklenmesi gerekiyorsa@starting-styleKULLANIN. - Geçici kaydırma kilitleme KULLANMAYIN. "base-select" tarafından yönetilen üst katman öğeleri, doğal arka plan kapatma davranışlarına izin vermelidir.
- Azaltılmış hareket tercihlerini doğrulayın (DO). Hareket hastalığından etkilenen kullanıcılar için erişilebilir ortamlar sağlamak amacıyla animasyon kısıtlamalarını her zaman bir
prefers-reduced-motionmedya sorgusuna sarmalayın. - Düzen davranışını test edin.
appearance: base-selectayarı, seçimi en uzun seçenek genişliğine göre boyutlandırma şeklindeki varsayılan tarayıcı davranışını kaldırır. Düzen kaymalarını önlemek için sabit bir genişlik ayarlamanız veya flex/grid kısıtlamaları kullanmanız gerekebilir. <select>öğenizin birnameözelliğine ve ilişkili bir<label>özelliğine sahip olduğundan emin olun. Bu sayede, özel bir kullanıcı arayüzü olsa bile bileşen ekran okuyucular tarafından erişilebilir durumda kalır ve standart form gönderimleriyle doğru şekilde çalışır.- Form gönderiminin eskisi gibi çalışması için seçeneklerde standart
valueözelliklerini kullanın.
Yedek stratejiler
Özelleştirilebilir <select> için yedekler ve tarayıcı desteği
Henüz appearance: base-select öğesini desteklemeyen tarayıcılar için <select> öğesi, standart bir işletim sistemi açılır listesine sorunsuz bir şekilde indirgenir.
- Metin Olmayan İçerik Yok Sayılır: Eski tarayıcılar,
<option>etiketlerinin içindeki HTML etiketlerini (<svg>veya<div>gibi) kaldırır ve yalnızca metin düğümlerini oluşturur.<option>öğesinin metin içeriğinin okunabilir ve kendi başına anlamlı olduğundan emin olun. - HTML Yapısı İşleme: Standart ayrıştırıcılar,
<select>içindeki<button>ve<selectedcontent>etiketlerini yoksayabilir veya geçersiz olarak değerlendirebilir. Standart metni okunabilir bir yedek olarak görüyorsanız progresif geliştirme için ağır JavaScript polyfill'leri kesinlikle gerekli değildir.
document.addEventListener("DOMContentLoaded", () => {
// Check if browser supports base-select value
if (!CSS.supports("appearance", "base-select")) {
// Custom select overrides are not supported natively.
}
});