Thuộc tính CSS appearance: base-select cho phép bạn tạo kiểu cho phần tử <select> và bộ chọn của phần tử đó (thông qua ::picker(select)) như các phần tử HTML thông thường.
Khi kết hợp với các kỹ thuật bố cục CSS hiện đại như Grid hoặc Flexbox, bạn có thể thoát khỏi danh sách dọc xếp chồng truyền thống và tạo ra trải nghiệm hình ảnh phong phú mà không cần JS tuỳ chỉnh.
Ví dụ: bạn có thể sử dụng display: grid để tạo kiểu cho các lựa chọn dưới dạng lưới thẻ 2D.
Thành phần này phù hợp với công cụ chọn màu, bộ chọn biểu tượng cảm xúc hoặc biến thể sản phẩm, trong đó trình đơn hình ảnh phong phú sẽ hiệu quả hơn danh sách.
Cách triển khai
1. Kích hoạt kiểu cơ bản
Áp dụng appearance: base-select cho cả phần tử <select> và phần tử giả bộ chọn nội bộ select::picker(select):
select.grid-picker {
&, &::picker(select) {
appearance: base-select;
}
}
2. Tạo kiểu cho vùng chứa bộ chọn
Nhắm mục tiêu select:open::picker(select) và áp dụng display: grid (hoặc display: flex).
Xác định các cột và khoảng trống như bạn làm cho bất kỳ vùng chứa nào.
select.grid-picker:open::picker(select) {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1rem;
}
QUAN TRỌNG: Bạn cần có :open. Nếu bạn tạo kiểu cho select::picker(select), bộ chọn sẽ hiển thị ngay cả khi lựa chọn đã đóng!
3. Tạo kiểu cho các lựa chọn
Nhắm đến các phần tử <option> để tạo kiểu cho chúng dưới dạng các mục hoặc thẻ trong lưới.
Bạn có thể thêm hình ảnh, SVG hoặc bố cục phức tạp vào bên trong các thành phần này.
/* Style options as grid cards */
.grid-picker option {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
/* padding, border-radius, background, etc. */
}
4. Tạo kiểu cho lựa chọn đã chọn
Để tạo kiểu cho tuỳ chọn đã chọn, hãy sử dụng option:checked. Ví dụ:
.grid-picker option:checked {
position: relative;
border: 2px solid var(--primary-color);
background-color: oklab(from var(--primary-color) l a b / 8%);
}
Giống như mọi thành phần điều khiển trên giao diện người dùng, đừng chỉ dùng màu sắc để thể hiện trạng thái, mà hãy dùng nhiều chỉ báo hình ảnh (ví dụ: màu sắc, độ dày đường viền, dấu kiểm) để thể hiện lựa chọn nào đang được chọn.
5. (Không bắt buộc) Tạo kiểu cho dấu kiểm của lựa chọn đã chọn
Theo mặc định, trình duyệt sẽ hiển thị dấu kiểm dưới dạng phần tử con đầu tiên của mỗi <option>.
Phần tử này hoạt động giống như ::before có content: "✓" / "", được kết xuất trước ::before do tác giả cung cấp (nếu có).
Nếu bạn không chọn, dấu kiểm sẽ bị ẩn thông qua visibility: hidden.
Bạn có thể tạo kiểu cho chỉ báo này bằng phần tử giả ::checkmark hoặc ẩn hoàn toàn nếu bạn chỉ muốn thể hiện lựa chọn thông qua kiểu hoặc sử dụng chỉ báo tuỳ chỉnh.
Ví dụ: để đặt huy hiệu ở góc trên cùng bên phải của lựa chọn, bạn có thể làm như sau:
select.grid-picker {
> option::checkmark {
/* Hide the checkmark for unselected options */
display: none;
}
> option:checked {
position: relative;
&::checkmark {
position: absolute;
inset-block-start: -.4em;
inset-inline-end: -.5em;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
min-inline-size: 1.6em;
aspect-ratio: 1;
padding-right: 0.12em;
padding-left: 0.05em;
border-radius: 50%;
background: var(--primary-color);
color: white;
font-size: 70%;
font-weight: bold;
}
}
}
Mẹo: Khi vẽ một vùng chứa (ví dụ: hình tròn) xung quanh dấu kiểm, hãy dịch chuyển dấu kiểm lên trên một chút (như chúng tôi đã làm ở đây với padding-bottom và padding-left) để căn giữa theo hình dạng của dấu kiểm.
KHÔNG sử dụng padding-inline-start hoặc padding-block-end cho việc này, vì các thuộc tính này sẽ thay đổi tuỳ thuộc vào chế độ viết, trong khi hình dạng của dấu kiểm thì không.
6. (Không bắt buộc) Tuỳ chỉnh màn hình lựa chọn đã chọn
Theo mặc định, <select> sẽ hiển thị phiên bản chỉ có văn bản của lựa chọn đã chọn.
Để hiển thị nội dung đa dạng thức cho giá trị đã chọn, hãy sử dụng <selectedcontent> bên trong <button> và/hoặc để hiển thị lựa chọn đã chọn theo cách khác (ví dụ: ẩn một số chi tiết nhất định).
Không KHÔNG sử dụng <selectedcontent> mà không có <button> xung quanh, vì <selectedcontent> sẽ chỉ trở thành một phần của bộ chọn, giống như mọi phần tử nội dung khác.
Các phương pháp hay nhất
- 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.
Hỗ trợ tiếp cận
<select> gốc chỉ xử lý thao tác điều hướng bằng mũi tên Lên/Xuống tuyến tính, vì vậy, phím Trái/Phải không có tác dụng gì trong bộ chọn đang mở và phím Lên/Xuống vẫn điều hướng các mục theo tuần tự, bất kể hàng và cột.
Để triển khai tính năng điều hướng không gian theo lưới, bạn có thể chặn keydown và định tuyến tiêu điểm đến lựa chọn gần nhất theo hướng được nhấn (ví dụ: sử dụng getBoundingClientRect()) khi dùng các phím mũi tên.
Để xác định mục tiếp theo/trước đó phù hợp, hãy sử dụng các phương thức DOM hoạt động trên hình học thực tế (ví dụ: getBoundingClientRect()) thay vì phân tích cú pháp các chuỗi CSS như grid-template-columns hoặc grid-column. Các chuỗi này có thể không ổn định và dễ xảy ra lỗi đối với lưới phức tạp, lưới con, RTL và các tình huống bố cục khác.
Tuy nhiên, đừng thiết kế quá phức tạp nếu không cần thiết; hãy sử dụng những bất biến mà bạn biết là đúng về bố cục để đơn giản hoá logic này.
Lưu ý: Thao tác này chỉ áp dụng cho thao tác điều hướng bằng bàn phím. <select> và ::picker() của nó vẫn có vai trò mặc định (menu hoặc listbox) và không thể ghi đè các vai trò này, cũng như không được phép chứa phần tử có role="grid".
Việc lồng một trình bao bọc có role="grid" bên trong bộ chọn hoạt động trong thực tế, nhưng không hợp lệ và có thể khiến người dùng AT ngạc nhiên.
Trong một số trường hợp, việc sử dụng <optgroup> để nhóm các lựa chọn có thể mang lại trải nghiệm tốt cho trình đọc màn hình mà không cần role="grid".
Nếu không, cuối cùng bạn có thể cần phải chuyển sang một giải pháp tuỳ chỉnh bằng cách sử dụng API cửa sổ bật lên, định vị neo và role="grid" để đạt được trải nghiệm người dùng mong muốn cho người dùng trình đọc màn hình.
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.
}
});