Animasyonlu Seçim Seçici

Web özellikleri: Özelleştirilebilir <select>

Ö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:

  1. Özelleştirmeyi etkinleştirme: appearance: base-select özelliğini hem <select> öğesine hem de ::picker(select) sözde öğesine uygulayın.
  2. Otomatik boyutlandırma geçişlerini etkinleştirme (isteğe bağlı): Tarayıcının height: auto ve height: 0 gibi ayrı metrik değerleri arasında geçiş yapmasına izin vermek için interpolate-size: allow-keywords (genellikle :root üzerinde) tanımlayın.
  3. Üst katman kapsayıcısına animasyon ekleme: ::picker(select) öğesine standart giriş/çıkış stilleri uygulayın. display: none ve display: block arasında geçiş yaparken opaklık geçişinin çalıştığından emin olmak için transition-behavior: allow-discrete (genellikle satır içi olarak transition: display 0.3s allow-discrete şeklinde yazılır) kullanmanız gerekir.
  4. @starting-style ile açılış durumuna bağlanın: Tarayıcının geçiş başlamadan önce hesaplaması gereken temel stilleri tanımlamak için @starting-style kullanın. Örneğin, öğenin yavaşça görünmesini istiyorsanız @starting-style bloğunda opaklığı 0 olarak ayarlayın.
  5. 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-icon gibi 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: none durumundan görünür duruma geçtiğinde animasyonların tam olarak tetiklenmesi gerekiyorsa @starting-style KULLANIN.
  • 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-motion medya sorgusuna sarmalayın.
  • Düzen davranışını test edin. appearance: base-select ayarı, 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 bir name ö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.
  }
});