Tạo thành phần nút

Thông tin tổng quan cơ bản về cách tạo các thành phần <button> thích ứng với màu sắc, thích ứng và dễ tiếp cận.

Trong bài đăng này, tôi muốn chia sẻ suy nghĩ của mình về cách tạo một thành phần <button> thích ứng với màu sắc, phản hồi và hỗ trợ tiếp cận. Dùng thử bản minh hoạxem nguồn

Người dùng có thể tương tác với các nút thông qua bàn phím và chuột trong giao diện sáng và tối.

Nếu bạn thích xem video hơn, sau đây là phiên bản của bài đăng này trên YouTube:

Tổng quan

Hỗ trợ trình duyệt

  • Chrome: 1.
  • Edge: 12.
  • Firefox: 1.
  • Safari: 1.

Nguồn

Phần tử <button> được xây dựng để hỗ trợ tương tác của người dùng. Sự kiện click của nó kích hoạt từ bàn phím, chuột, thao tác chạm, giọng nói và nhiều thiết bị khác, với các quy tắc thông minh về thời gian. Thư viện này cũng đi kèm với một số kiểu mặc định trong mỗi trình duyệt, vì vậy, bạn có thể sử dụng trực tiếp các kiểu này mà không cần tuỳ chỉnh. Sử dụng color-scheme để chọn cả nút sáng và tối do trình duyệt cung cấp.

Ngoài ra, còn có nhiều loại nút, mỗi loại nút được hiển thị trong phần nhúng Codepen trước đó. <button> không có loại sẽ thích ứng với việc nằm trong <form>, thay đổi thành loại gửi.

<!-- buttons -->
<button></button>
<button type="submit"></button>
<button type="button"></button>
<button type="reset"></button>

<!-- button state -->
<button disabled></button>

<!-- input buttons -->
<input type="button" />
<input type="file">

Trong Thử thách GUI của tháng này, mỗi nút sẽ có một số kiểu để giúp phân biệt ý định của người dùng thông qua hình ảnh. Nút đặt lại sẽ có màu cảnh báo vì chúng có tính phá hoại và nút gửi sẽ nhận được văn bản nhấn màu xanh dương để chúng xuất hiện với mức độ quảng cáo cao hơn một chút so với nút thông thường.

Xem trước tập hợp cuối cùng của tất cả các loại nút, hiển thị trong một biểu mẫu và không phải trong một biểu mẫu, với các bổ sung thú vị cho nút biểu tượng và nút tuỳ chỉnh.
Xem trước tập hợp hoàn thiện của tất cả các loại nút, hiển thị trong biểu mẫu chứ không phải trong biểu mẫu, với những điểm bổ sung hữu ích cho nút biểu tượng và nút tuỳ chỉnh

Nút cũng có lớp giả để CSS sử dụng cho việc tạo kiểu. Các lớp này cung cấp các móc CSS để tuỳ chỉnh cảm giác của nút: :hover cho khi chuột di qua nút, :active cho khi chuột hoặc bàn phím đang nhấn và :focus hoặc :focus-visible để hỗ trợ tạo kiểu công nghệ hỗ trợ.

button:hover {}
button:active {}
button:focus {}
button:focus-visible {}
Bản xem trước bộ hoàn chỉnh của tất cả các loại nút trong giao diện tối.
Xem trước tập hợp cuối cùng của tất cả các loại nút trong giao diện tối

Markup (note: đây là tên ứng dụng)

Ngoài các loại nút do quy cách HTML cung cấp, tôi đã thêm một nút có biểu tượng và một nút có lớp tuỳ chỉnh btn-custom.

<button>Default</button>
<input type="button" value="
<input>"/>
<button>
 
<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
   
<path d="..." />
 
</svg>
  Icon
</button>
<button type="submit">Submit</button>
<button type="button">Type Button</button>
<button type="reset">Reset</button>
<button disabled>Disabled</button>
<button class="btn-custom">Custom</button>
<input type="file">

Sau đó, để kiểm thử, mỗi nút sẽ được đặt bên trong một biểu mẫu. Bằng cách này, tôi có thể đảm bảo kiểu được cập nhật phù hợp cho nút mặc định, nút này hoạt động như nút gửi. Tôi cũng chuyển đổi chiến lược biểu tượng, từ SVG cùng dòng sang SVG được che, để đảm bảo cả hai đều hoạt động tốt như nhau.

<form>
 
<button>Default</button>
  <input type="button" value="
<input>"/>
 
<button>Icon <span data-icon="cloud"></span></button>
 
<button type="submit">Submit</button>
 
<button type="button">Type Button</button>
 
<button type="reset">Reset</button>
 
<button disabled>Disabled</button>
 
<button class="btn-custom btn-large" type="button">Large Custom</button>
 
<input type="file">
</form>

Ma trận tổ hợp khá lớn ở thời điểm này. Giữa các loại nút, lớp giả và việc ở trong hoặc ngoài một biểu mẫu, có hơn 20 tổ hợp nút. Thật may là CSS có thể giúp chúng ta diễn đạt rõ ràng từng thuộc tính!

Hỗ trợ tiếp cận

Các phần tử nút có thể truy cập được một cách tự nhiên, nhưng có một số điểm nâng cao phổ biến.

Di chuột và lấy nét cùng nhau

Tôi muốn nhóm :hover:focus cùng với bộ chọn giả chức năng :is(). Điều này giúp đảm bảo giao diện của tôi luôn xem xét bàn phím và các kiểu công nghệ hỗ trợ.

button:is(:hover, :focus) {
 

}
Hãy xem bản minh hoạ!

Vòng tròn lấy nét tương tác

Tôi muốn tạo ảnh động cho vòng tiêu điểm cho người dùng bàn phím và công nghệ hỗ trợ. Tôi thực hiện việc này bằng cách tạo ảnh động cho đường viền cách nút 5px, nhưng chỉ khi nút không hoạt động. Điều này tạo ra hiệu ứng khiến vòng lấy nét thu nhỏ trở lại kích thước nút khi nhấn.

:where(button, input):where(:not(:active)):focus-visible {
 
outline-offset: 5px;
}

Đảm bảo độ tương phản màu đạt yêu cầu

Có ít nhất 4 cách kết hợp màu khác nhau giữa chế độ sáng và tối cần xem xét độ tương phản màu: nút, nút gửi, nút đặt lại và nút bị vô hiệu hoá. VisBug được dùng ở đây để kiểm tra và hiển thị tất cả điểm số cùng một lúc:

Đang ẩn biểu tượng để những người không xem được

Khi tạo nút biểu tượng, biểu tượng phải hỗ trợ trực quan cho văn bản nút. Điều này cũng có nghĩa là biểu tượng này không có giá trị đối với người khiếm thị. May mắn là trình duyệt có cung cấp một cách để ẩn các mục khỏi công nghệ trình đọc màn hình để những người bị mất thị lực không phải lo lắng về hình ảnh nút trang trí:

<button>
 
<svgaria-hidden="true">...</svg>
  Icon Button
</button>
Công cụ dành cho nhà phát triển Chrome hiển thị cây hỗ trợ tiếp cận cho nút này. Cây này bỏ qua hình ảnh nút vì hình ảnh này có aria-hidden được đặt thành true.
Công cụ của Chrome cho nhà phát triển hiển thị cây hỗ trợ tiếp cận cho nút. Cây này bỏ qua hình ảnh nút vì thuộc tính aria-hidden được đặt thành true (đúng)

Kiểu

Trong phần tiếp theo, trước tiên, tôi sẽ thiết lập một hệ thống thuộc tính tuỳ chỉnh để quản lý các kiểu thích ứng của nút. Với các thuộc tính tuỳ chỉnh đó, tôi có thể bắt đầu chọn các phần tử và tuỳ chỉnh giao diện của chúng.

Chiến lược tuỳ chỉnh thích ứng

Chiến lược thuộc tính tuỳ chỉnh được sử dụng trong Thử thách GUI này rất giống với chiến lược được sử dụng trong phần tạo bảng phối màu. Đối với hệ thống màu sáng và tối thích ứng, một thuộc tính tuỳ chỉnh cho mỗi giao diện sẽ được xác định và đặt tên tương ứng. Sau đó, một thuộc tính tuỳ chỉnh duy nhất được dùng để lưu giữ giá trị hiện tại của giao diện và được gán cho một thuộc tính CSS. Sau đó, thuộc tính tuỳ chỉnh duy nhất có thể được cập nhật thành một giá trị khác, sau đó cập nhật kiểu nút.

button {
 
--_bg-light: white;
 
--_bg-dark: black;
 
--_bg: var(--_bg-light);

 
background-color: var(--_bg);
}

@media (prefers-color-scheme: dark) {
  button
{
   
--_bg: var(--_bg-dark);
 
}
}

Điều tôi thích là giao diện sáng và tối được khai báo rõ ràng. Tính năng gián tiếp và trừu tượng được chuyển sang thuộc tính tuỳ chỉnh --_bg, hiện là thuộc tính "phản ứng" duy nhất; --_bg-light--_bg-dark là tĩnh. Bạn cũng có thể thấy rõ rằng giao diện sáng là giao diện mặc định và giao diện tối chỉ được áp dụng có điều kiện.

Chuẩn bị cho tính nhất quán trong thiết kế

Bộ chọn dùng chung

Bộ chọn sau đây được dùng để nhắm đến tất cả các loại nút và lúc đầu sẽ có đôi chút choáng ngợp. :where() được dùng, vì vậy, bạn không cần làm gì cụ thể để tuỳ chỉnh nút này. Các nút thường được điều chỉnh cho các tình huống thay thế và bộ chọn :where() đảm bảo rằng tác vụ đó dễ dàng. Bên trong :where(), mỗi loại nút được chọn, bao gồm cả ::file-selector-buttonkhông dùng được bên trong :is() hoặc :where().

:where(
  button
,
  input
[type="button"],
  input
[type="submit"],
  input
[type="reset"],
  input
[type="file"]
),
:where(input[type="file"])::file-selector-button {
 

}

Tất cả thuộc tính tuỳ chỉnh sẽ nằm trong phạm vi của bộ chọn này. Đã đến lúc xem lại tất cả các thuộc tính tuỳ chỉnh! Có khá nhiều thuộc tính tuỳ chỉnh được sử dụng trong nút này. Tôi sẽ mô tả từng nhóm trong quá trình thực hiện, sau đó chia sẻ ngữ cảnh chuyển động tối và giảm ở cuối phần này.

Màu nhấn của nút

Nút Gửi và biểu tượng là nơi tuyệt vời để thêm màu sắc:

--_accent-light: hsl(210 100% 40%);
--_accent-dark: hsl(210 50% 70%);
--_accent: var(--_accent-light);

Màu văn bản nút

Màu văn bản của nút không phải là màu trắng hoặc đen, mà là các phiên bản tối hoặc sáng của --_accent bằng cách sử dụng hsl() và tuân theo màu sắc 210:

--_text-light: hsl(210 10% 30%);
--_text-dark: hsl(210 5% 95%);
--_text: var(--_text-light);

Màu nền của nút

Nền của nút tuân theo cùng một mẫu hsl(), ngoại trừ các nút giao diện sáng – các nút này được đặt thành màu trắng để bề mặt của chúng xuất hiện gần người dùng hoặc ở phía trước các bề mặt khác:

--_bg-light: hsl(0 0% 100%);
--_bg-dark: hsl(210 9% 31%);
--_bg: var(--_bg-light);

Nền của nút hợp lý

Màu nền này dùng để làm cho một nền tảng xuất hiện phía sau các nền tảng khác, rất hữu ích cho nền của dữ liệu đầu vào tệp:

--_input-well-light: hsl(210 16% 87%);
--_input-well-dark: hsl(204 10% 10%);
--_input-well: var(--_input-well-light);

Khoảng đệm của nút

Khoảng cách xung quanh văn bản trong nút được thực hiện bằng cách sử dụng đơn vị ch, một độ dài tương đối so với kích thước phông chữ. Điều này trở nên quan trọng khi các nút lớn chỉ có thể tăng font-size và tỷ lệ nút theo tỷ lệ:

--_padding-inline: 1.75ch;
--_padding-block: .75ch;

Đường viền nút

Bán kính đường viền nút được lưu vào một thuộc tính tuỳ chỉnh để dữ liệu đầu vào của tệp có thể khớp với các nút khác. Màu đường viền tuân theo hệ thống màu thích ứng đã thiết lập:

--_border-radius: .5ch;

--_border-light: hsl(210 14% 89%);
--_border-dark: var(--_bg-dark);
--_border: var(--_border-light);

Hiệu ứng làm nổi bật khi di chuột qua nút

Các thuộc tính này thiết lập thuộc tính kích thước để chuyển đổi khi tương tác, và màu đánh dấu tuân theo hệ thống màu thích ứng. Chúng tôi sẽ đề cập đến cách những thao tác này tương tác ở phần sau trong bài đăng này, nhưng cuối cùng, những thao tác này sẽ được dùng để tạo hiệu ứng box-shadow:

--_highlight-size: 0;

--_highlight-light: hsl(210 10% 71% / 25%);
--_highlight-dark: hsl(210 10% 5% / 25%);
--_highlight: var(--_highlight-light);

Độ bóng văn bản của nút

Mỗi nút có một kiểu bóng văn bản tinh tế. Điều này giúp văn bản nằm phía trên nút, cải thiện mức độ dễ đọc và thêm một lớp đánh bóng đẹp mắt cho bản trình bày.

--_ink-shadow-light: 0 1px 0 var(--_border-light);
--_ink-shadow-dark: 0 1px 0 hsl(210 11% 15%);
--_ink-shadow: var(--_ink-shadow-light);

Biểu tượng nút

Biểu tượng có kích thước bằng hai ký tự nhờ đơn vị ch có độ dài tương đối. Điều này sẽ giúp biểu tượng điều chỉnh theo tỷ lệ với văn bản của nút. Màu biểu tượng dựa vào --_accent-color để có màu thích ứng và nằm trong giao diện.

--_icon-size: 2ch;
--_icon-color: var(--_accent);

Bóng nút

Để bóng thích ứng đúng cách với ánh sáng và bóng tối, bóng cần thay đổi cả màu sắc và độ mờ. Bóng đổ trong giao diện sáng sẽ đẹp nhất khi có màu sắc tinh tế và được phủ lên màu bề mặt. Bóng của giao diện tối cần tối hơn và bão hoà hơn để có thể phủ lên các màu bề mặt tối hơn.

--_shadow-color-light: 220 3% 15%;
--_shadow-color-dark: 220 40% 2%;
--_shadow-color: var(--_shadow-color-light);

--_shadow-strength-light: 1%;
--_shadow-strength-dark: 25%;
--_shadow-strength: var(--_shadow-strength-light);

Với màu sắc và độ đậm nhạt thích ứng, tôi có thể kết hợp hai độ sâu của bóng:

--_shadow-1: 0 1px 2px -1px hsl(var(--_shadow-color)/calc(var(--_shadow-strength) + 9%));

--_shadow-2:
 
0 3px 5px -2px hsl(var(--_shadow-color)/calc(var(--_shadow-strength) + 3%)),
 
0 7px 14px -5px hsl(var(--_shadow-color)/calc(var(--_shadow-strength) + 5%));

Hơn nữa, để các nút có giao diện hơi 3D, bóng hộp 1px sẽ tạo ra ảo ảnh:

--_shadow-depth-light: 0 1px var(--_border-light);
--_shadow-depth-dark: 0 1px var(--_bg-dark);
--_shadow-depth: var(--_shadow-depth-light);

Hiệu ứng chuyển đổi nút

Theo mẫu dành cho màu thích ứng, tôi tạo 2 thuộc tính tĩnh để giữ các tuỳ chọn hệ thống thiết kế:

--_transition-motion-reduce: ;
--_transition-motion-ok:
  box-shadow
145ms ease,
  outline-offset
145ms ease
;
--_transition: var(--_transition-motion-reduce);

Tất cả thuộc tính cùng nhau trong bộ chọn

:where(
  button
,
  input
[type="button"],
  input
[type="submit"],
  input
[type="reset"],
  input
[type="file"]
),
:where(input[type="file"])::file-selector-button {
 
--_accent-light: hsl(210 100% 40%);
 
--_accent-dark: hsl(210 50% 70%);
 
--_accent: var(--_accent-light);



--_text-light: hsl(210 10% 30%);
 
--_text-dark: hsl(210 5% 95%);
 
--_text: var(--_text-light);



--_bg-light: hsl(0 0% 100%);
 
--_bg-dark: hsl(210 9% 31%);
 
--_bg: var(--_bg-light);



--_input-well-light: hsl(210 16% 87%);
 
--_input-well-dark: hsl(204 10% 10%);
 
--_input-well: var(--_input-well-light);



--_padding-inline: 1.75ch;
 
--_padding-block: .75ch;



--_border-radius: .5ch;
 
--_border-light: hsl(210 14% 89%);
 
--_border-dark: var(--_bg-dark);
 
--_border: var(--_border-light);



--_highlight-size: 0;
 
--_highlight-light: hsl(210 10% 71% / 25%);
 
--_highlight-dark: hsl(210 10% 5% / 25%);
 
--_highlight: var(--_highlight-light);



--_ink-shadow-light: 0 1px 0 hsl(210 14% 89%);
 
--_ink-shadow-dark: 0 1px 0 hsl(210 11% 15%);
 
--_ink-shadow: var(--_ink-shadow-light);



--_icon-size: 2ch;
 
--_icon-color-light: var(--_accent-light);
 
--_icon-color-dark: var(--_accent-dark);
 
--_icon-color: var(--accent, var(--_icon-color-light));



--_shadow-color-light: 220 3% 15%;
 
--_shadow-color-dark: 220 40% 2%;
 
--_shadow-color: var(--_shadow-color-light);
 
--_shadow-strength-light: 1%;
 
--_shadow-strength-dark: 25%;
 
--_shadow-strength: var(--_shadow-strength-light);
 
--_shadow-1: 0 1px 2px -1px hsl(var(--_shadow-color)/calc(var(--_shadow-strength) + 9%));
 
--_shadow-2:
   
0 3px 5px -2px hsl(var(--_shadow-color)/calc(var(--_shadow-strength) + 3%)),
   
0 7px 14px -5px hsl(var(--_shadow-color)/calc(var(--_shadow-strength) + 5%))
 
;



--_shadow-depth-light: hsl(210 14% 89%);
 
--_shadow-depth-dark: var(--_bg-dark);
 
--_shadow-depth: var(--_shadow-depth-light);



--_transition-motion-reduce: ;
 
--_transition-motion-ok:
    box-shadow
145ms ease,
    outline-offset
145ms ease
 
;
 
--_transition: var(--_transition-motion-reduce);
}

Các nút mặc định hiển thị trong giao diện sáng và tối cạnh nhau.

Điều chỉnh giao diện tối

Giá trị của mẫu thành phần tĩnh -light-dark sẽ trở nên rõ ràng khi đặt các thành phần giao diện tối:

@media (prefers-color-scheme: dark) {
 
:where(
    button
,
    input
[type="button"],
    input
[type="submit"],
    input
[type="reset"],
    input
[type="file"]
 
),
 
:where(input[type="file"])::file-selector-button {
   
--_bg: var(--_bg-dark);
   
--_text: var(--_text-dark);
   
--_border: var(--_border-dark);
   
--_accent: var(--_accent-dark);
   
--_highlight: var(--_highlight-dark);
   
--_input-well: var(--_input-well-dark);
   
--_ink-shadow: var(--_ink-shadow-dark);
   
--_shadow-depth: var(--_shadow-depth-dark);
   
--_shadow-color: var(--_shadow-color-dark);
   
--_shadow-strength: var(--_shadow-strength-dark);
 
}
}

Không chỉ dễ đọc, mà người dùng các nút tuỳ chỉnh này cũng có thể tự tin sử dụng các thành phần hỗ trợ trần mà không lo chúng sẽ không thích ứng phù hợp với lựa chọn ưu tiên của người dùng.

Giảm việc điều chỉnh chuyển động

Nếu người dùng truy cập này không có vấn đề gì với chuyển động, hãy chỉ định --_transition cho var(--_transition-motion-ok):

@media (prefers-reduced-motion: no-preference) {
 
:where(
    button
,
    input
[type="button"],
    input
[type="submit"],
    input
[type="reset"],
    input
[type="file"]
 
),
 
:where(input[type="file"])::file-selector-button {
   
--_transition: var(--_transition-motion-ok);
 
}
}

Một số kiểu dùng chung

Bạn cần đặt phông chữ của các nút và dữ liệu đầu vào thành inherit để khớp với phông chữ còn lại của trang; nếu không, trình duyệt sẽ tạo kiểu cho các nút và dữ liệu đầu vào đó. Điều này cũng áp dụng cho letter-spacing. Khi bạn đặt line-height thành 1.5, kích thước hộp chữ cái sẽ được đặt để văn bản có khoảng trống ở phía trên và phía dưới:

:where(
  button
,
  input
[type="button"],
  input
[type="submit"],
  input
[type="reset"],
  input
[type="file"]
),
:where(input[type="file"])::file-selector-button {
 
/* …CSS variables */

 
font: inherit;
 
letter-spacing: inherit;
 
line-height: 1.5;
 
border-radius: var(--_border-radius);
}

Ảnh chụp màn hình cho thấy các nút sau khi áp dụng kiểu trước đó.

Tạo kiểu nút

Điều chỉnh bộ chọn

Bộ chọn input[type="file"] không phải là phần nút của dữ liệu đầu vào, mà là phần tử giả ::file-selector-button, vì vậy, tôi đã xoá input[type="file"] khỏi danh sách:

:where(
  button
,
  input
[type="button"],
  input
[type="submit"],
  input
[type="reset"],
  input
[type="file"]
),
:where(input[type="file"])::file-selector-button {
 
}

Điều chỉnh con trỏ và thao tác chạm

Trước tiên, tôi định kiểu con trỏ thành kiểu pointer. Kiểu này giúp nút cho người dùng chuột biết rằng nút đó có thể tương tác. Sau đó, tôi thêm touch-action: manipulation để thực hiện các lượt nhấp mà không cần phải chờ và quan sát một cú nhấp đúp có thể xảy ra, giúp các nút có cảm giác mượt mà hơn:

:where(
  button
,
  input
[type="button"],
  input
[type="submit"],
  input
[type="reset"]
),
:where(input[type="file"])::file-selector-button {
 
cursor: pointer;
 
touch-action: manipulation;
}

Màu sắc và đường viền

Tiếp theo, tôi tuỳ chỉnh kích thước phông chữ, nền, văn bản và màu đường viền bằng cách sử dụng một số thuộc tính tuỳ chỉnh thích ứng đã thiết lập trước đó:

:where(
  button
,
  input
[type="button"],
  input
[type="submit"],
  input
[type="reset"]
),
:where(input[type="file"])::file-selector-button {
 


 
font-size: var(--_size, 1rem);
 
font-weight: 700;
 
background: var(--_bg);
 
color: var(--_text);
 
border: 2px solid var(--_border);
}

Ảnh chụp màn hình cho thấy các nút sau khi áp dụng kiểu trước đó.

Bóng

Các nút này được áp dụng một số kỹ thuật tuyệt vời. text-shadow thích ứng với chế độ sáng và tối, tạo ra giao diện tinh tế, dễ chịu cho văn bản nút nằm gọn trên nền. Đối với box-shadow, hệ thống sẽ chỉ định 3 bóng đổ. Đầu tiên, --_shadow-2 là một bóng hộp thông thường. Bóng thứ hai là một thủ thuật đánh lừa thị giác, khiến nút có vẻ như được bo tròn một chút. Bóng cuối cùng là cho phần đánh dấu khi di chuột, ban đầu có kích thước là 0, nhưng sau đó sẽ được cung cấp kích thước và chuyển đổi để có vẻ như phát triển từ nút.

:where(
  button
,
  input
[type="button"],
  input
[type="submit"],
  input
[type="reset"]
),
:where(input[type="file"])::file-selector-button {
 


 
box-shadow:
    var
(--_shadow-2),
    var
(--_shadow-depth),
   
0 0 0 var(--_highlight-size) var(--_highlight)
 
;
 
text-shadow: var(--_ink-shadow);
}

Ảnh chụp màn hình hiển thị các nút sau khi áp dụng các kiểu trước đó.

Bố cục

Tôi đã đặt bố cục flexbox cho nút này, cụ thể là bố cục inline-flex phù hợp với nội dung của nút. Sau đó, tôi căn giữa văn bản, đồng thời căn chỉnh phần tử con theo chiều dọc và chiều ngang vào chính giữa. Điều này sẽ giúp các biểu tượng và các thành phần nút khác căn chỉnh đúng cách.

:where(
  button
,
  input
[type="button"],
  input
[type="submit"],
  input
[type="reset"]
),
:where(input[type="file"])::file-selector-button {
 


 
display: inline-flex;
 
justify-content: center;
 
align-items: center;
 
text-align: center;
}

Ảnh chụp màn hình hiển thị các nút sau khi áp dụng các kiểu trước đó.

Giãn cách

Đối với khoảng cách nút, tôi đã sử dụng gap để các nút không chạm vào nhau và các thuộc tính logic để khoảng đệm hoạt động cho tất cả bố cục văn bản.

:where(
  button
,
  input
[type="button"],
  input
[type="submit"],
  input
[type="reset"]
),
:where(input[type="file"])::file-selector-button {
 


 
gap: 1ch;
 
padding-block: var(--_padding-block);
 
padding-inline: var(--_padding-inline);
}

Ảnh chụp màn hình hiển thị các nút sau khi áp dụng các kiểu trước đó.

Trải nghiệm người dùng bằng chuột và cảm ứng

Phần tiếp theo chủ yếu dành cho người dùng cảm ứng trên thiết bị di động. Thuộc tính đầu tiên, user-select, dành cho tất cả người dùng; thuộc tính này ngăn văn bản làm nổi bật văn bản nút. Điều này chủ yếu xảy ra trên các thiết bị cảm ứng khi người dùng nhấn và giữ một nút, hệ điều hành sẽ làm nổi bật văn bản của nút đó.

Nhìn chung, tôi nhận thấy đây không phải là trải nghiệm người dùng với các nút trong ứng dụng tích hợp sẵn, vì vậy, tôi tắt tính năng này bằng cách đặt user-select thành không có. Nhấn vào màu đánh dấu (-webkit-tap-highlight-color) và trình đơn theo bối cảnh của hệ điều hành (-webkit-touch-callout) là các tính năng khác của nút tập trung vào web không phù hợp với kỳ vọng chung của người dùng về nút. Vì vậy, tôi cũng xoá các tính năng này.

:where(
  button
,
  input
[type="button"],
  input
[type="submit"],
  input
[type="reset"]
),
:where(input[type="file"])::file-selector-button {
 


 
user-select: none;
 
-webkit-tap-highlight-color: transparent;
 
-webkit-touch-callout: none;
}

Kiểu chuyển cảnh

Biến --_transition thích ứng được gán cho thuộc tính transition (hiệu ứng chuyển đổi):

:where(
  button
,
  input
[type="button"],
  input
[type="submit"],
  input
[type="reset"]
),
:where(input[type="file"])::file-selector-button {
 


 
transition: var(--_transition);
}

Khi di chuột, trong khi người dùng không chủ động nhấn, hãy điều chỉnh kích thước làm nổi bật bóng để tạo ra một giao diện tập trung đẹp mắt, có vẻ như phát triển từ bên trong nút:

:where(
  button
,
  input
[type="button"],
  input
[type="submit"],
  input
[type="reset"]
):where(:not(:active):hover) {
 
--_highlight-size: .5rem;
}

Khi lấy tiêu điểm, hãy tăng độ lệch đường viền tiêu điểm so với nút, đồng thời tạo cho tiêu điểm một giao diện đẹp mắt, có vẻ như phát triển từ bên trong nút:

:where(button, input):where(:not(:active)):focus-visible {
 
outline-offset: 5px;
}

Biểu tượng

Để xử lý biểu tượng, bộ chọn có thêm bộ chọn :where() cho các phần tử con SVG trực tiếp hoặc các phần tử có thuộc tính tuỳ chỉnh data-icon. Kích thước biểu tượng được đặt bằng thuộc tính tuỳ chỉnh sử dụng thuộc tính logic cùng dòng và khối. Màu nét vẽ được đặt, cũng như drop-shadow để khớp với text-shadow. flex-shrink được đặt thành 0 để biểu tượng không bao giờ bị vỡ. Cuối cùng, tôi chọn các biểu tượng có đường viền và gán các kiểu đó ở đây bằng các đầu dòng và điểm nối dòng fill: noneround:

:where(
  button
,
  input
[type="button"],
  input
[type="submit"],
  input
[type="reset"]
) > :where(svg, [data-icon]) {
 
block-size: var(--_icon-size);
 
inline-size: var(--_icon-size);
 
stroke: var(--_icon-color);
 
filter: drop-shadow(var(--_ink-shadow));

 
flex-shrink: 0;
 
fill: none;
 
stroke-linecap: round;
 
stroke-linejoin: round;
}

Ảnh chụp màn hình hiển thị các nút sau khi áp dụng các kiểu trước đó.

Tuỳ chỉnh nút gửi

Tôi muốn các nút gửi có giao diện được quảng bá một chút và tôi đã đạt được điều này bằng cách đặt màu văn bản của các nút làm màu nhấn:

:where(
 
[type="submit"],
  form
button:not([type],[disabled])
) {
 
--_text: var(--_accent);
}

Ảnh chụp màn hình hiển thị các nút sau khi áp dụng các kiểu trước đó.

Tuỳ chỉnh nút đặt lại

Tôi muốn các nút đặt lại có một số dấu hiệu cảnh báo tích hợp để cảnh báo người dùng về hành vi có thể gây hại. Tôi cũng chọn tạo kiểu cho nút giao diện sáng bằng các điểm nhấn màu đỏ nhiều hơn so với giao diện tối. Bạn có thể tuỳ chỉnh bằng cách thay đổi màu cơ bản sáng hoặc tối thích hợp và nút này sẽ cập nhật kiểu:

:where([type="reset"]) {
 
--_border-light: hsl(0 100% 83%);
 
--_highlight-light: hsl(0 100% 89% / 20%);
 
--_text-light: hsl(0 80% 50%);
 
--_text-dark: hsl(0 100% 89%);
}

Tôi cũng nghĩ rằng màu đường viền tiêu điểm sẽ phù hợp với màu nhấn là màu đỏ. Màu văn bản sẽ điều chỉnh từ màu đỏ đậm sang màu đỏ nhạt. Tôi sẽ làm cho màu đường viền khớp với màu này bằng từ khoá currentColor:

:where([type="reset"]):focus-visible {
 
outline-color: currentColor;
}

Ảnh chụp màn hình hiển thị các nút sau khi áp dụng các kiểu trước đó.

Tuỳ chỉnh các nút đã tắt

Rất thường thấy các nút bị vô hiệu hoá có độ tương phản màu kém trong quá trình cố gắng làm giảm nút bị vô hiệu hoá để nút đó có vẻ ít hoạt động hơn. Tôi đã kiểm thử từng tập hợp màu và đảm bảo chúng đã vượt qua, đẩy giá trị độ sáng HSL cho đến khi điểm số được truyền trong Công cụ cho nhà phát triển hoặc VisBug.

:where(
  button
,
  input
[type="button"],
  input
[type="submit"],
  input
[type="reset"]
)[disabled] {
 
--_bg: none;
 
--_text-light: hsl(210 7% 40%);
 
--_text-dark: hsl(210 11% 71%);

 
cursor: not-allowed;
 
box-shadow: var(--_shadow-1);
}

Ảnh chụp màn hình hiển thị các nút sau khi áp dụng các kiểu trước đó.

Tuỳ chỉnh các nút nhập tệp

Nút nhập tệp là vùng chứa span và nút. CSS có thể định kiểu cho vùng chứa đầu vào một chút, cũng như nút lồng nhau, nhưng không phải cho span. Vùng chứa được cấp max-inline-size để không lớn hơn mức cần thiết, trong khi inline-size: 100% sẽ tự thu nhỏ và vừa với các vùng chứa nhỏ hơn. Màu nền được đặt thành màu thích ứng tối hơn các bề mặt khác, vì vậy, màu nền sẽ nằm phía sau nút bộ chọn tệp.

:where(input[type="file"]) {
 
inline-size: 100%;
 
max-inline-size: max-content;
 
background-color: var(--_input-well);
}

Nút bộ chọn tệp và các nút loại dữ liệu đầu vào được cung cấp riêng appearance: none để xoá mọi kiểu do trình duyệt cung cấp mà các kiểu nút khác không ghi đè.

:where(input[type="button"]),
:where(input[type="file"])::file-selector-button {
 
appearance: none;
}

Cuối cùng, lề được thêm vào inline-end của nút để đẩy văn bản span ra khỏi nút, tạo ra một khoảng trống.

:where(input[type="file"])::file-selector-button {
 
margin-inline-end: var(--_padding-inline);
}

Ảnh chụp màn hình hiển thị các nút sau khi áp dụng các kiểu trước đó.

Các trường hợp ngoại lệ đặc biệt về giao diện tối

Tôi đã cho các nút hành động chính ở nền tối hơn để văn bản có độ tương phản cao hơn, giúp các nút này giao diện được quảng bá nhiều hơn một chút.

@media (prefers-color-scheme: dark) {
 
:where(
   
[type="submit"],
   
[type="reset"],
   
[disabled],
    form
button:not([type="button"])
 
) {
   
--_bg: var(--_input-well);
 
}
}

Ảnh chụp màn hình hiển thị các nút sau khi áp dụng các kiểu trước đó.

Tạo biến thể

Để cho vui và vì tính thực tế, tôi chọn trình bày cách tạo một vài biến thể. Một biến thể rất sống động, tương tự như giao diện của các nút chính. Một biến thể khác có kích thước lớn. Biến thể cuối cùng có biểu tượng tô màu nền chuyển màu.

Nút sống động

Để tạo kiểu nút này, tôi đã ghi đè các thuộc tính cơ sở trực tiếp bằng màu xanh dương. Mặc dù cách này nhanh chóng và dễ dàng, nhưng sẽ xoá các thuộc tính thích ứng và trông giống nhau trong cả giao diện sáng và tối.

.btn-custom {
 
--_bg: linear-gradient(hsl(228 94% 67%), hsl(228 81% 59%));
 
--_border: hsl(228 89% 63%);
 
--_text: hsl(228 89% 100%);
 
--_ink-shadow: 0 1px 0 hsl(228 57% 50%);
 
--_highlight: hsl(228 94% 67% / 20%);
}

Nút tuỳ chỉnh hiển thị ở chế độ sáng và tối. Màu này có màu xanh dương rất sống động như các nút hành động chính thông thường.

Nút lớn

Kiểu nút này được tạo bằng cách sửa đổi thuộc tính tuỳ chỉnh --_size. Khoảng đệm và các thành phần không gian khác tương ứng với kích thước này, điều chỉnh theo tỷ lệ với kích thước mới.

.btn-large {
 
--_size: 1.5rem;
}

Nút lớn xuất hiện bên cạnh nút tuỳ chỉnh, lớn hơn khoảng 150 lần.

Nút biểu tượng

Hiệu ứng biểu tượng này không liên quan gì đến kiểu nút của chúng ta, nhưng nó cho biết cách đạt được hiệu ứng đó chỉ bằng một vài thuộc tính CSS và mức độ hiệu quả của nút trong việc xử lý các biểu tượng không phải SVG nội tuyến.

[data-icon="cloud"] {
 
--icon-cloud: url("https://api.iconify.design/mdi:apple-icloud.svg") center / contain no-repeat;

 
-webkit-mask: var(--icon-cloud);
 
mask: var(--icon-cloud);
 
background: linear-gradient(to bottom, var(--_accent-dark), var(--_accent-light));
}

Một nút có biểu tượng xuất hiện trong giao diện sáng và tối.

Kết luận

Giờ bạn đã biết cách tôi thực hiện điều đó, bạn sẽ làm cách nào‽ 🙂

Hãy đa dạng hoá các phương pháp và tìm hiểu tất cả các cách xây dựng trên web.

Hãy tạo bản minh hoạ, gửi đường liên kết cho tôi trên Twitter và tôi sẽ thêm bản minh hoạ đó vào phần phối lại của cộng đồng ở bên dưới!

Bản phối lại của cộng đồng

Chưa có nội dung nào.

Tài nguyên