API chọn có thể tuỳ chỉnh cung cấp một cách khai báo, dựa trên CSS để tạo ảnh động cho các phần tử <select> và bộ chọn thả xuống của chúng. Bằng cách kết hợp appearance: base-select với các kỹ thuật ảnh động CSS hiện đại (chẳng hạn như @starting-style và hành vi chuyển đổi allow-discrete), bạn có thể tạo hiệu ứng chuyển đổi giao diện người dùng mượt mà, cao cấp cho các phần tử lớp trên cùng mà không cần dựa vào các thư viện JavaScript nặng.
Trước đây, bạn không thể tạo ảnh động cho trình đơn thả xuống lựa chọn gốc vì giao diện người dùng của các trình đơn này được hiển thị bên ngoài các quy tắc ràng buộc về khung nhìn có thể truy cập. Với appearance: base-select, bộ chọn có thể được tạo kiểu và tạo ảnh động như mọi phần tử trang khác.
Cách triển khai
Cách triển khai bộ chọn ảnh động:
- Chọn sử dụng tính năng tuỳ chỉnh: Áp dụng
appearance: base-selectcho cả phần tử<select>và phần tử giả::picker(select). - Bật hiệu ứng chuyển đổi tự động định cỡ (Không bắt buộc): Xác định
interpolate-size: allow-keywords(thường là trên:root) để cho phép trình duyệt chuyển đổi giữa các giá trị chỉ số riêng biệt nhưheight: autovàheight: 0. - Tạo ảnh động cho vùng chứa lớp trên cùng: Áp dụng kiểu xuất hiện/biến mất chuẩn cho
::picker(select). Để đảm bảo hiệu ứng chuyển đổi độ mờ hoạt động khi di chuyển giữadisplay: nonevàdisplay: block, bạn phải sử dụngtransition-behavior: allow-discrete(thường được viết cùng dòng dưới dạngtransition: display 0.3s allow-discrete). - Kết nối với trạng thái mở bằng
@starting-style: Sử dụng@starting-styleđể xác định kiểu cơ sở mà trình duyệt sẽ tính toán trước khi quá trình chuyển đổi bắt đầu. Ví dụ: nếu bạn muốn hiệu ứng mờ dần, hãy đặt độ mờ thành0bên trong khối@starting-style. - Xoay biểu tượng: Sử dụng bộ chọn tiêu điểm hoặc bộ chọn đang hoạt động của phần tử giả như
:open::picker-iconđể áp dụng hiệu ứng chuyển đổi (chẳng hạn như xoay hoặc dịch chuyển) cho chỉ báo mũi tên.
Mã ví dụ: Chọn tỷ lệ và hiệu ứng mờ dần mượt mà
Ví dụ sau đây minh hoạ một thành phần chọn tuỳ chỉnh được tạo kiểu bằng ảnh động trang chuẩn cho vùng chứa bộ chọn.
<!-- 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;
}
}
Triển khai chiến lược và các phương pháp hay nhất
- NÊN sử dụng
@starting-stylekhi bạn cần ảnh động kích hoạt chính xác khi một phần tử chuyển từ trạng tháidisplay: nonesang trạng thái hiển thị. - KHÔNG sử dụng tính năng khoá cuộn đặc biệt. Các phần tử lớp trên cùng do "base-select" quản lý phải cho phép các hành vi đóng phông nền tự nhiên.
- NÊN xác minh lựa chọn ưu tiên về việc giảm chuyển động. Luôn gói các quy tắc ràng buộc về ảnh động trong một truy vấn đa phương tiện
prefers-reduced-motionđể đảm bảo môi trường dễ tiếp cận cho những người bị say tàu xe. - Kiểm thử hành vi của bố cục. Việc đặt
appearance: base-selectsẽ xoá hành vi mặc định của trình duyệt là định cỡ lựa chọn dựa trên chiều rộng của lựa chọn dài nhất. Bạn có thể cần đặt chiều rộng cố định hoặc sử dụng các quy tắc ràng buộc về flex/grid để ngăn bố cục thay đổi. - Đảm bảo
<select>của bạn có thuộc tínhnamevà<label>được liên kết. Điều này đảm bảo rằng ngay cả khi có giao diện người dùng tuỳ chỉnh, thành phần này vẫn có thể truy cập được đối với trình đọc màn hình và hoạt động chính xác với các lượt gửi biểu mẫu chuẩn. - Sử dụng các thuộc tính
valuechuẩn cho các lựa chọn để đảm bảo việc gửi biểu mẫu hoạt động chính xác như trước.
Chiến lược dự phòng
Phương án dự phòng và khả năng hỗ trợ của trình duyệt cho <select> có thể tuỳ chỉnh
Đối với các trình duyệt chưa hỗ trợ appearance: base-select, phần tử <select> sẽ hạ cấp một cách linh hoạt xuống trình đơn thả xuống của hệ điều hành tiêu chuẩn.
- Bỏ qua nội dung không phải văn bản: Các trình duyệt cũ loại bỏ thẻ HTML (như
<svg>hoặc<div>) bên trong thẻ<option>và chỉ hiển thị các nút văn bản. Đảm bảo nội dung văn bản của<option>dễ đọc và có ý nghĩa riêng. - Xử lý cấu trúc HTML: Trình phân tích cú pháp chuẩn có thể bỏ qua các thẻ
<button>và<selectedcontent>bên trong<select>hoặc coi các thẻ đó là không hợp lệ. Bạn không cần phải sử dụng các polyfill JavaScript nặng để cải tiến tăng dần nếu bạn xem văn bản chuẩn là một phương án dự phòng dễ đọc.
document.addEventListener("DOMContentLoaded", () => {
// Check if browser supports base-select value
if (!CSS.supports("appearance", "base-select")) {
// Custom select overrides are not supported natively.
}
});