
Lạm dụng các lựa chọn có thể tùy chỉnh
Abusing Customizable Selects
Các trình duyệt Chromium mới đây đã hỗ trợ tùy chỉnh phần tử `<select>`, cho phép các developer nhúng mã HTML tùy ý vào bên trong thẻ `<option>`. Đây là một thay đổi lớn so với giới hạn văn bản thuần túy trước đây. Nhờ đó, chúng ta có thể tạo ra các UI component phong phú và có style đẹp mắt hơn, ví dụ điển hình là demo "folder stack" cho select. Các dev nên tận dụng pseudo-element `::picker()` để có toàn quyền kiểm soát việc style cho select và khám phá CSS để tùy chỉnh giao diện. Lưu ý là các trình duyệt không hỗ trợ sẽ tự động hiển thị lại theo kiểu select tiêu chuẩn.
Các trình duyệt web luôn cung cấp các tính năng mới, nhưng có gì thú vị nếu chúng ta không thể xây dựng những điều ngớ ngẩn và thú vị với chúng? Trong bài viết này, chúng ta hãy xem qua một số bản demo mà tôi đã thực hiện bằng cách sử dụng...
Các trình duyệt web luôn cung cấp các tính năng mới, nhưng có gì thú vị nếu chúng ta không thể tạo ra những điều ngớ ngẩn và thú vị với chúng?
Trong bài viết này, chúng ta hãy xem qua một số bản demo mà tôi đã tạo bằng cách sử dụng tính năng có thể tùy chỉnh mới và xem qua các bước cũng như kỹ thuật chính mà tôi đã sử dụng để triển khai chúng.
Tôi hy vọng chúng sẽ khiến bạn hào hứng như tôi về các lựa chọn tùy chỉnh và cung cấp cho bạn đủ kiến thức để bắt đầu tạo các lựa chọn của riêng mình. Bạn biết đấy, ý tưởng của bạn có thể hữu ích hơn ý tưởng của tôi và có thể vì những lý do chính đáng, nhưng tôi thích hơi quá nhiệt tình với những ý tưởng ngớ ngẩn vì điều đó mang lại cho tôi cơ hội học hỏi tốt hơn.
Trước khi chúng ta bắt đầu, xin nói một chút về hỗ trợ trình duyệt: các bản demo trong bài viết này chỉ chạy trên các trình duyệt dựa trên Chrome gần đây vì đó là nơi triển khai các lựa chọn có thể tùy chỉnh ngay bây giờ. Tuy nhiên, tính năng này được thiết kế theo cách không làm hỏng các trình duyệt không hỗ trợ. Xét cho cùng, phần tử được tùy chỉnh vẫn là phần tử . Vì vậy, nếu trình duyệt bạn đang sử dụng không hỗ trợ các lựa chọn có thể tùy chỉnh, bạn sẽ chỉ thấy các lựa chọn và tùy chọn thông thường trong các bản demo này và điều đó thật tuyệt. Nó sẽ kém vui hơn nhiều.
Chồng thư mục cong
Hãy bắt đầu với bản demo đầu tiên: một chồng thư mục để chọn, với một bước thay đổi:
Trước tiên chúng ta sẽ bắt đầu với một số mã HTML. Chúng ta không cần nhiều đánh dấu phức tạp ở đây vì mỗi tùy chọn chỉ là tên của thư mục. Chúng ta có thể vẽ các biểu tượng thư mục sau này chỉ bằng CSS.
Bạn sẽ nhận thấy rằng chúng tôi đã sử dụng các phần tử bên trong Các phần tử để bao bọc từng tên thư mục. Điều đó sẽ hữu ích cho việc tạo kiểu cho tên thư mục đã chọn sau này. Mặc dù đây chỉ là một nhưng việc có thể thực hiện được điều này là một sự thay đổi khá lớn so với những gì có thể làm được trước đây.
Đó là bởi vì, cho đến gần đây, các chỉ có thể chứa văn bản, bởi vì đó là thứ duy nhất có thể xuất hiện bên trong các tùy chọn của một vùng chọn. Trình phân tích cú pháp HTML hiện đã được nới lỏng để cho phép nhúng nhiều phần tử HTML hơn vào các tùy chọn. Những trình duyệt không hỗ trợ các lựa chọn có thể tùy chỉnh sẽ bỏ qua các phần tử bổ sung này và chỉ hiển thị văn bản.
Vậy, cho đến thời điểm hiện tại, ngăn xếp thư mục của chúng ta trông như sau:
Tiếp theo, đây là điều quan trọng nhất bạn cần làm để chọn tham gia tính năng chọn có thể tùy chỉnh: hãy đặt lại giao diện mặc định của phần chọn và phần thả xuống của nó bằng cách sử dụng phần tử giả ::picker():
chọn,
::picker(chọn) {
ngoại hình: chọn cơ sở;
Quy tắc CSS này giúp ích rất nhiều cho chúng ta: nó mở ra toàn bộ khả năng tạo kiểu cho toàn bộ vùng chọn, bao gồm nút, danh sách thả xuống và các tùy chọn. Nếu không có sự chọn tham gia này, bạn sẽ có một lựa chọn tiêu chuẩn.
Bây giờ hãy tạo kiểu cho vùng chọn, bắt đầu với phần nút của nó. Trước tiên, chúng ta sẽ loại bỏ biểu tượng bộ chọn bằng cách sử dụng phần tử giả ::picker-icon mới để ẩn nó:
select::picker-icon {
hiển thị: không có;
Tiếp theo, hãy thêm một chút kiểu để tạo nút trông đẹp mắt:
Và đây là nút chọn mới của chúng tôi:

Bây giờ chúng ta hãy chuyển sự chú ý của chúng ta sang phần thả xuống vì đây là nơi điều kỳ diệu xảy ra.
Trong phần chọn, danh sách thả xuống chứa tất cả các tùy chọn và xuất hiện khi bạn nhấp vào nút. Rất nhiều kiểu mặc định của trình duyệt đã áp dụng cho nó để đặt vị trí, màu nền, lề, v.v. Vì vậy, chúng ta sẽ phải vô hiệu hóa và ghi đè rất nhiều thứ.
Trong bản demo của chúng tôi, chúng tôi không muốn hiển thị danh sách thả xuống. Thay vào đó, chúng tôi muốn mỗi tùy chọn riêng lẻ (mỗi thư mục trong trường hợp này) xuất hiện như thể nổi phía trên trang mà không có phần tử vùng chứa.
Để thực hiện việc này, hãy sử dụng phần tử giả ::picker(select) để đặt kiểu của chúng ta:
Và với điều này, danh sách thả xuống không còn hiển thị nữa và nó không còn hạn chế các tùy chọn hoặc cắt chúng nếu chúng tràn khu vực thả xuống.
Điều này mang lại cho chúng tôi những cải tiến sau:
Bây giờ là lúc chúng ta chuyển sự chú ý sang các phần tử tùy chọn. Trước tiên, hãy thay thế biểu tượng dấu kiểm bằng một biểu tượng đĩa nhỏ bằng cách sử dụng phần tử giả ::checkmark:
tùy chọn::đánh dấu {
nội dung: "●";
màu sắc: #222;
Phần tử giả này giúp bạn dễ dàng thay đổi hình dạng, màu sắc hoặc thậm chí kích thước của dấu kiểm.
Ngoài ra, hãy thêm một phần tử giả bổ sung cho mỗi tùy chọn bằng cách sử dụng option::trước để hiển thị biểu tượng cảm xúc thư mục bên cạnh mỗi tùy chọn. Và, với một chút tinh chỉnh CSS, chúng ta sẽ có kết quả này:

Bây giờ chúng ta có một danh sách các thư mục nổi trên đầu trang khi chúng ta nhấp vào nút chọn. Nó cũng hoạt động giống như bất kỳ lựa chọn nào khác, bằng chuột hoặc bằng bàn phím, vì vậy chúng tôi chỉ có thể cảm ơn trình duyệt vì đã duy trì khả năng truy cập đầu vào trong khi chúng tôi tận hưởng CSS.
Bây giờ, hãy áp dụng một số chuyển đổi CSS để làm cho ngăn xếp các thư mục trông cong hơn một chút, để nó trông bắt mắt hơn.
Để đạt được điều này, chúng ta sẽ cần thêm một cú pháp CSS mới. Thật không may, cú pháp này chưa được phổ biến rộng rãi: hàm sibling-index(). Hàm này trả về chỉ mục của phần tử trong phần tử anh chị em của nó. Hàm sibling-count() cũng tồn tại và nó trả về tổng số anh chị em, nhưng chúng ta sẽ không cần nó ở đây.
Có quyền truy cập vào chỉ mục của phần tử hiện tại trong phần tử anh chị em của nó có nghĩa là chúng ta có thể tạo kiểu cho từng tùy chọn tùy thuộc vào vị trí của nó trong danh sách thả xuống chọn. Đây chính xác là những gì chúng ta cần để làm cho các tùy chọn xuất hiện ở góc độ lớn dần.
Đây là mã:
tùy chọn {
--rotation-offset: -4deg;
xoay: calc(sibling-index() * var(--rotation-offset));
Trong đoạn mã này, trước tiên chúng tôi tạo một thuộc tính tùy chỉnh có tên là --rotation-offset, thuộc tính này xác định góc mà mỗi tùy chọn sẽ xoay so với tùy chọn trước đó. Sau đó, chúng tôi sử dụng giá trị này với thuộc tính rotate, nhân giá trị của nó với sibling-index(). Bằng cách đó, tùy chọn đầu tiên được xoay -4 độ, tùy chọn thứ hai -8 độ, tùy chọn thứ ba -12 độ, v.v.
Bây giờ, chỉ điều đó thôi là chưa đủ để tạo ảo giác về một chồng thư mục cong vì mỗi thư mục sẽ xoay quanh điểm gốc riêng của nó, điểm này nằm ở góc trên cùng bên trái của mỗi thư mục theo mặc định. Ngay bây giờ, chúng tôi nhận được điều này:

Hãy sử dụng thuộc tính transform-origin để đặt điểm gốc chung mà tất cả các tùy chọn sẽ xoay quanh đó. Vì transform-origin liên quan đến từng phần tử riêng lẻ nên chúng ta cần sử dụng lại hàm sibling-index() để di chuyển tất cả các điểm gốc lên và sang phải để tất cả chúng đều ở cùng một vị trí:
tùy chọn {
--rotation-offset: -4deg;
xoay: calc(sibling-index() * var(--rotation-offset));
nguồn gốc biến đổi: phải calc(sibling-index() * -1.5rem);
Và với điều này, chúng ta nhận được kết quả như sau:

Bước cuối cùng là tạo hiệu ứng cho các tùy chọn. Nó trông rất đẹp nhưng chúng tôi muốn chồng thư mục cong dần cho đến khi đạt được hình dạng cuối cùng. Điều đó sẽ khiến câu chuyện trở nên sinh động và thú vị hơn khi tương tác.
Hãy đặt lại chế độ xoay của tùy chọn theo mặc định và áp dụng chuyển đổi với chức năng nới lỏng:
Và bây giờ, hãy chỉ áp dụng góc xoay phù hợp khi phần chọn được mở:
select:open tùy chọn {
xoay: calc(sibling-index() * -1 * var(--rotation-offset));
Thật không may, những điều trên là chưa đủ. Theo mặc định, quá trình chuyển đổi CSS không được kích hoạt khi một phần tử xuất hiện, đó là trường hợp đối với các tùy chọn của chúng tôi. Rất may, đã có cách khắc phục sự cố này: @starting-style tại quy tắc. Quy tắc này cho phép chúng tôi xác định trạng thái ban đầu của các tùy chọn, giúp quá trình chuyển đổi có thể phát ngay khi các tùy chọn xuất hiện:
@starting-style {
chọn: tùy chọn mở {
xoay: 0 độ;
}
Một điều nữa để làm cho nó thậm chí còn đẹp hơn. Chúng ta hãy trì hoãn mỗi lần chuyển đổi so với lần chuyển đổi trước đó để làm cho nó trông giống như mỗi thư mục xuất hiện sau thư mục trước đó một chút. Để đạt được điều này, hãy sử dụng hàm sibling-index() một lần nữa để nhân với độ trễ chuyển tiếp ngắn:
tùy chọn {
độ trễ chuyển tiếp: calc((sibling-index() - 1) * 0,01s);
Bây giờ chúng ta đã triển khai một chồng thư mục hoạt hình, cong, cong bằng phần tử ! Hãy xem bản demo và mã trong CodePen tiếp theo:
CSS có được nhiều khả năng mới mỗi năm. Tôi hy vọng bản demo này sẽ giúp bạn hiểu rõ hơn về một số chức năng mới này. Việc xây dựng nó đã giúp tôi hiểu được rất nhiều khái niệm mới đối với tôi. Nó cũng khiến tôi rất hào hứng với tính năng chọn tùy chỉnh. Nhiều đến mức tôi cũng đã tạo các bản demo khác. Vì vậy, chúng ta hãy xem xét thêm hai trong số họ. Tuy nhiên, lần này chúng ta sẽ đi nhanh hơn và chỉ đánh dấu những phần quan trọng nhất.
Bộ bài hình quạt
Đối với bản demo thứ hai, chúng tôi sẽ tạo một công cụ chọn thẻ mở ra theo kiểu bộ bài có hình quạt:
Đánh dấu HTML cho bản demo này hơi khác so với bản demo trước. Mỗi thẻ có một ít nội dung để hiển thị, vì vậy hãy tạo một vài phần tử cho mỗi tùy chọn:
Điều thú vị khác về mã HTML mà chúng ta sẽ sử dụng ở đây là việc bổ sung phần tử trống ngay bên dưới thẻ mở :
trống này phục vụ một mục đích rất cụ thể: nó ngăn chặn hành vi mặc định xảy ra.
Trong lựa chọn tùy chỉnh, trình duyệt sẽ tự động hiển thị nội dung của tùy chọn hiện được chọn (trong trường hợp này là mặt thẻ) trong khu vực nút của lựa chọn. Và nó thực hiện điều này bằng cách tạo một phần tử có tên phản ánh tùy chọn đã chọn. Tuy nhiên, trong bản demo của chúng tôi, chúng tôi muốn nút luôn hiển thị mặt sau của bộ bài chứ không phải lá bài đã chọn. Để đạt được điều này, chúng tôi ghi đè hành vi mặc định bằng cách giới thiệu của riêng mình. Điều này yêu cầu trình duyệt không chèn phần tử của chính nó và cho phép chúng ta tạo kiểu cho phần tử :

Bây giờ, đối với phần thả xuống, giống như trong bản demo trước, chúng tôi không muốn hiển thị phần tử vùng chứa thả xuống, vì vậy, chúng tôi cũng sẽ ghi đè các kiểu nền, đường viền và kiểu tràn mặc định như chúng tôi đã làm trước đây.
Quan trọng hơn, vị trí của bộ bài khi mở ra rất quan trọng. Chúng tôi muốn nó tỏa ra từ boong tàu và vẫn ở chính giữa phía trên boong tàu.
Trong phần lựa chọn có thể tùy chỉnh, phần thả xuống, tức là phần tử giả ::picker(select), được định vị tương ứng với phần nút nhờ định vị neo, điều này thật tuyệt vì chúng ta có thể ghi đè lên nó!
Trong trường hợp của chúng ta, hãy ghi đè căn chỉnh liên quan đến neo, tức là nút, bằng cách sử dụng thuộc tính position-area:
::picker(select) {
vị trí-khu vực: trung tâm trung tâm;
chèn: 0;
Chúng tôi cũng đang đặt thuộc tính inset thành 0 tại đây. Điều này đặt tất cả các thuộc tính top, right, bottom và left thành 0 trong một khai báo duy nhất, giúp phần thả xuống có thể sử dụng toàn bộ không gian có sẵn, thay vì bị trình duyệt hạn chế xuất hiện ở bên cạnh nút chọn.
Cuối cùng, hãy làm cho các thẻ xuất hiện cạnh nhau thay vì ở phía trên nhau:
Khi phần tử chọn được mở và các tùy chọn hiển thị, bây giờ chúng ta thấy điều này:

Bước tiếp theo là xoay từng thẻ để các tùy chọn xuất hiện theo kiểu xòe ra, với thẻ ở giữa thẳng, các thẻ ở bên trái xoay dần về bên trái và các thẻ ở bên phải xoay về phía bên phải.
Để làm điều này, bạn đã đoán được rồi, chúng ta sẽ sử dụng lại thuộc tính sibling-index(). Lần này chúng ta cũng sẽ sử dụng thuộc tính sibling-count():
tùy chọn {
--card-fan-xoay: 7deg;
--card-fan-lây lan: -11vmin;
--option-index: calc(sibling-index() - 1);
--center: calc(sibling-count() / 2);
--offset-from-center: calc(var(--option-index) - var(--center));
xoay: calc(var(--offset-from-center) * var(--card-fan-rotation));
dịch: calc(var(--offset-from-center) * var(--card-fan- Lan truyền)) 0;
nguồn gốc biến đổi: trung tâm 75vmin;
Trong đoạn mã trên, chúng tôi đang tính toán độ lệch của từng thẻ so với thẻ ở giữa và chúng tôi đang sử dụng giá trị này để xoay từng thẻ theo gia số 7 độ. Ví dụ: trong một bộ bài có 9 lá bài, lá bài ngoài cùng bên trái (tức là lá bài đầu tiên) sẽ có độ lệch -4 và sẽ được xoay một góc -4 * 7 = -28 độ, trong khi lá bài ngoài cùng bên phải sẽ được xoay 28 độ.
Chúng tôi cũng sử dụng thuộc tính translate để đưa các thẻ lại gần nhau thành hình quạt và thuộc tính `transform-origin` để làm cho tất cả trông thật hoàn hảo.
Cuối cùng, hãy kết hợp tất cả lại với nhau bằng cách tạo hiệu ứng cho phần mở đầu của bộ bài. Để thực hiện việc này, chúng ta có thể xác định chuyển đổi CSS trên thuộc tính --card-fan-rotation tùy chỉnh. Hoạt hình nó từ 0 đến 7 độ là tất cả những gì chúng ta cần để tạo ra ảo ảnh mà chúng ta đang theo đuổi. Tạo hoạt ảnh cho một thuộc tính tùy chỉnh cần một vài bước.
Trước tiên, hãy xác định loại thuộc tính tùy chỉnh để trình duyệt có thể tạo hoạt ảnh chính xác:
@property --card-fan-rotation {
cú pháp: '';
kế thừa: sai;
giá trị ban đầu: 7deg;
Thứ hai, hãy sử dụng @starting-style theo quy tắc, giống như trong bản demo trước, để cho phép quá trình chuyển đổi CSS phát khi các tùy chọn xuất hiện:
@starting-style {
chọn: tùy chọn mở {
--card-fan-xoay: 0deg;
}
Sau đó, đặt góc xoay bắt đầu khi đóng phần tử chọn và xác định chuyển đổi CSS:
tùy chọn {
--card-fan-xoay: 0deg;
quá trình chuyển đổi: --card-fan-xoay 0,2 giây dễ dàng;
Và cuối cùng, hãy đặt góc cuối cùng khi vùng chọn được mở:
select:open tùy chọn {
--card-fan-xoay: ban đầu;
Chúng ta có thể sử dụng giá trị `ban đầu` ở trên thay vì mã hóa lại giá trị 7deg vì giá trị này đã được xác định là giá trị ban đầu trong quy tắc @property ở trên.
Vậy là xong, bộ bài với phần mở đầu hoạt hình của chúng tôi đã sẵn sàng! Hãy xem mã hoàn chỉnh và bản demo trực tiếp trong CodePen này:
Tôi thật ngạc nhiên khi các lựa chọn có thể tùy chỉnh cho phép bạn đẩy mọi thứ đến mức nào. Bạn không chỉ có thể ghi đè giao diện của nút và các tùy chọn của nút mà còn có thể thay đổi cách mọi thứ được định vị và thậm chí cả hoạt ảnh.
Hãy kết thúc bằng một bản demo cuối cùng.
Bộ chọn biểu tượng cảm xúc xuyên tâm
Giống như trong bản demo trước, ở đây chúng tôi muốn các biểu tượng cảm xúc được đặt ở giữa nút chọn. Để đạt được điều này, hãy ghi đè vị trí neo mặc định của phần thả xuống.
Lần này, chúng ta sẽ sử dụng hàm anchor() để đặt tọa độ trên cùng và bên trái của vùng chứa thả xuống:
Trong đoạn mã này, thuộc tính --radius là bán kính của vòng tròn biểu tượng cảm xúc. Và vì các lựa chọn có thể tùy chỉnh đã sử dụng định vị neo nên chúng ta có thể sử dụng hàm anchor() để định vị menu thả xuống tương ứng với nút.
Bây giờ chúng ta cần định vị các tùy chọn theo hình tròn, bên trong danh sách thả xuống. Hóa ra, CSS hiện cũng biết lượng giác nên chúng tôi sẽ sử dụng các hàm cos() và sin() cùng với Hàm sibling-index() và sibling-count():
Và chúng ta đây:
Bản demo cuối cùng cũng chứa một chút mã để tạo hiệu ứng khi mở các tùy chọn, nhưng chúng tôi sẽ không đi sâu vào chi tiết trong bài viết này.
Để tìm hiểu thêm và chơi thử bản demo trực tiếp, hãy xem CodePen này:
Kết thúc
Tạm thời chỉ vậy thôi. Tôi hy vọng những bản demo này đã giúp bạn hiểu thêm một chút về cách tùy chỉnh các lựa chọn có thể tùy chỉnh và cảm thấy hứng thú khi thực sự sử dụng tính năng này trong một dự án thực tế.
Xin lưu ý rằng ngay cả khi được tùy chỉnh, phần tử vẫn là và sẽ hoạt động tốt trong các trình duyệt không hỗ trợ. Vì vậy, ngay cả khi tính năng này vẫn còn ở giai đoạn đầu, bạn vẫn có thể sử dụng nó như một tính năng nâng cao tiến bộ tuyệt vời.
Tác giả: speckx