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 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ĩ về cách tạo một phần tử <button> thích ứng màu sắc, thích ứng và dễ tiếp cận. Dùng thử bản minh hoạxem nguồn

Các nút được tương tác qua bàn phím và chuột trong giao diện sáng và tối.

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

Tổng quan

Hỗ trợ trình duyệt

  • 1
  • 12
  • 1
  • ≤4

Nguồn

Phần tử <button> được xây dựng để tương tác của người dùng. Các sự kiện click của ứng dụng sẽ kích hoạt từ bàn phím, chuột, thao tác chạm, giọng nói, v.v. với các quy tắc thông minh về thời gian. Phiên bả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 sử dụng cá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 xuất hiện trong cách nhúng Codepen trước đó. <button> không có loại sẽ thích ứng khi ở 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 tháng này, mỗi nút sẽ có các kiểu để giúp phân biệt ý định của người dùng một cách trực quan. 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ẽ có văn bản nhấn màu xanh dương để các nút này trông quảng bá hơn một chút so với các 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ị ở dạng chứ không phải dưới dạng, với các bổ sung thú vị cho các nút biểu tượng và nút tùy chỉnh.

Các nút cũng có lớp giả để CSS dùng trong việc định kiểu. Các lớp này cung cấp các hook CSS để tuỳ chỉnh cảm giác của nút: :hover dùng khi chuột nằm trên nút, :active dùng khi chuột hoặc bàn phím đang nhấn và :focus hoặc :focus-visible để hỗ trợ định kiểu công nghệ hỗ trợ.

button:hover {}
button:active {}
button:focus {}
button:focus-visible {}
Bản xem trước tập hợp cuối cùng gồm 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 gồm 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 theo thông số kỹ thuật HTML, tôi đã thêm một nút có biểu tượng và một nút có lớp btn-custom tuỳ chỉnh.

<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 được đặt bên trong một biểu mẫu. Bằng cách này, tôi có thể đảm bảo các kiểu được cập nhật phù hợp cho nút mặc định (nút 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 bị che giấu, để đảm bảo cả hai đều hoạt động hiệu quả 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 kết hợp khá áp đảo vào lúc này. Có hơn 20 tổ hợp nút giữa các loại nút, lớp giả và trạng thái ở trong hoặc bên ngoài một biểu mẫu. Đó là một lợi ích mà CSS có thể giúp chúng tôi trình bày rõ ràng từng yếu tố!

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ố tính năng 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 lại với nhau 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 cân nhắc các kiểu bàn phím và công nghệ hỗ trợ.

button:is(:hover, :focus) {
  …
}
Dùng thử bản minh hoạ!

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

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

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

Đảm bảo độ tương phản màu truyền

Có ít nhất 4 tổ hợp màu khác nhau giữa sáng và tối cần cân nhắc về độ tương phản màu: nút, nút gửi, nút đặt lại và nút đã tắt. VisBug được dùng ở đây để kiểm tra và hiện tất cả điểm số cùng một lúc:

Ẩn biểu tượng để những người không nhìn thấy được

Khi tạo một nút biểu tượng, biểu tượng đó phải hỗ trợ trực quan cho văn bản trên nút. Điều này cũng có nghĩa là biểu tượng không có giá trị đối với người khiếm thị. May mắn là trình duyệt 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 khiếm thị không còn bận tâm đến các hình ảnh nút trang trí:

<button>
  <svg … aria-hidden="true">...</svg>
  Icon Button
</button>
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 bỏ qua hình ảnh nút vì đã đặt giá trị ẩn của aria 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 bỏ qua hình ảnh nút vì đã đặt aria-hidden được đặt thành true (đúng)

Kiểu

Trong phần tiếp theo này, trước tiên, tôi sẽ thiết lập 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 thuộc tính tuỳ chỉnh thích ứng

Chiến lược thuộc tính tuỳ chỉnh được dùng trong Thử thách GUI này rất giống với chiến lược được dùng để tạo bảng phối màu. Đối với một hệ thống màu sáng và tối thích ứng, một thuộc tính tuỳ chỉnh cho từng 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 sẽ đượ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, rồi 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 đều mang tính khai báo và rõ ràng. Lệnh gián tiếp và trừu tượng được giảm tải vào 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ần hiểu 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 theo điều kiện.

Chuẩn bị để đảm bảo 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 khác nhau và lúc đầu hơi gây choáng ngợp. :where() được dùng nên việc tuỳ chỉnh nút không yêu cầu cụ thể. Các nút thường được điều chỉnh cho phù hợp với các tình huống thay thế và bộ chọn :where() giúp đảm bảo việc thao tác trở nên dễ dàng. Bên trong :where(), mỗi loại nút được chọn, bao gồm cả ::file-selector-button, không thể sử dụng 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ả các thuộc tính tuỳ chỉnh sẽ nằm trong phạm vi bên trong bộ chọn này. Đã đến lúc xem xét tất cả các thuộc tính tùy chỉnh! Có khá nhiều thuộc tính tuỳ chỉnh được dùng trong nút này. Tôi sẽ mô tả từng nhóm khi chúng ta tiếp tục, sau đó chia sẻ ngữ cảnh chuyển động tối và giảm ở cuối phần.

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

Các nút và biểu tượng gửi là những nơi tuyệt vời để tạo hiệu ứng 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 nút không phải là màu trắng hoặc đen, mà là các phiên bản làm tối hoặc sáng của --_accent bằng cách sử dụng hsl() và bám sát màu 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 nút tuân theo cùng một mẫu hsl(), ngoại trừ nút giao diện sáng — các nút được đặt thành màu trắng để giao diện của nút xuất hiện gần với người dùng hoặc phía trước các giao diện khác:

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

Phông nền của nút

Màu nền này dùng để làm cho một bề mặt xuất hiện phía sau các khu vực khác, rất hữu ích đối với nền của hoạt động đầ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 đơn vị ch, độ 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 có thể chỉ cần tăng font-size và tăng tỷ lệ nút:

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

Đường viền nút

Bán kính đường viền nút được lưu trữ trong một thuộc tính tuỳ chỉnh để dữ liệu nhập vào 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 đánh dấu 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 các hoạt động tương tác này sau này trong bài đăng này, nhưng cuối cùng các thay đổi này được sử 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 trên 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 ở đầu nút, cải thiện mức độ dễ đọc và thêm một lớp đẹ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

Các biểu tượng có kích thước của 2 ký tự nhờ đơn vị độ dài tương đối ch, giúp biểu tượng điều chỉnh tỷ lệ tương ứng với văn bản trên nút. Màu biểu tượng dựa trên --_accent-color để có màu thích ứng và 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à tối, chúng cần chuyển cả màu sắc và độ mờ. Bóng của giao diện sáng đạt hiệu quả nhất khi chúng tinh tế và được phủ màu dựa trên màu bề mặt mà chúng phủ lên. Bóng của giao diện tối cần tối hơn và bão hoà nhiều hơn để có thể phủ 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ạnh và màu sắc thích ứng, tôi có thể tập hợp được 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 3D đôi chút, hiệu ứng đổ bóng 1px sẽ tạo ra ảo giác:

--_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 cho màu thích ứng, tôi tạo 2 thuộc tính tĩnh để giữ lại 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ả cơ sở lưu trú cùng nhau trong bộ chọn

Tất cả thuộc tính tuỳ chỉnh trong một 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 xuất hiện cạnh nhau trong giao diện sáng và tối.

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

Giá trị của mẫu đạo cụ tĩnh -light-dark trở nên rõ ràng khi bạn đặt đạo cụ cho 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);
  }
}

Việc này không chỉ đọc tốt mà người dùng các nút tuỳ chỉnh này còn có thể sử dụng các đạo cụ trần mà vẫn yên tâm rằng các nút đó sẽ điều chỉnh cho phù hợp với lựa chọn ưu tiên của người dùng.

Điều chỉnh chuyển động giảm dần

Nếu người dùng truy cập này vẫn có chuyển động, hãy gán --_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 chung

Các nút và phương thức nhập cần có phông chữ được đặt 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ẽ định kiểu các phông chữ đó. Quy tắc này cũng áp dụng cho letter-spacing. Việc đặt line-height thành 1.5 sẽ đặt kích thước hộp chữ cái để văn bản có một số 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 các 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 đầu vào. ::file-selector-button phần tử giả là một phần tử, 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 tạo kiểu con trỏ theo kiểu pointer, giúp nút cho người dùng chuột biết rằng đó là kiểu tương tác. Sau đó, tôi thêm touch-action: manipulation để khiến các lượt nhấp không cần phải chờ và quan sát thấy có khả năng xảy ra lượt nhấp đúp, giúp các nút có cảm giác nhanh 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 và đường viền

Tiếp theo, tôi tuỳ chỉnh kích thước phông chữ, màu 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 được 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 các kiểu trước đó.

Bóng

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

: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 cho thấy các nút sau khi áp dụng các kiểu trước đó.

Bố cục

Tôi đã tạo cho nút một bố cục flexbox, cụ thể là bố cục inline-flex sẽ phù hợp với nội dung. Sau đó, tôi căn giữa văn bản và căn giữa phần tử con theo chiều dọc và chiều ngang vào giữa. Điều này sẽ giúp các biểu tượng và thành phần nút khác đượ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 cho thấy 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 của nút, tôi đã sử dụng gap để ngăn các nút đồng cấp có thể chạm và các thuộc tính logic cho khoảng đệm để khoảng cách của nút hoạt động với mọi 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 cho thấy 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 cách chạm và chuột

Phần tiếp theo này 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 trên nút. Bạn hầu như có thể nhận thấy nút này trên các thiết bị cảm ứng khi người dùng nhấn và giữ một nút, cũng như hệ điều hành sẽ làm nổi bật văn bản của nút đó.

Thường thì 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 và không phù hợp với kỳ vọng chung của người dùng nút, vì vậy, tôi cũng loại bỏ chúng.

: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 (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 vùng nổi bật bóng để giao diện tiêu điểm đẹp mắt xuất hiện từ 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 của đường viền tiêu điểm từ nút, đồng thời tạo cho giao diện tiêu điểm đẹp mắt xuất hiện từ bên trong nút:

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

Biểu tượng

Để xử lý các biểu tượng, bộ chọn này có thêm một bộ chọn :where() cho 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 với thuộc tính tuỳ chỉnh bằng cách sử dụng các 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 thiết lập thành 0 để biểu tượng này không bao giờ bị méo. Cuối cùng, tôi chọn các biểu tượng có dòng lệnh và chỉ định các kiểu đó ở đây bằng cách dùng các ký tự dòng fill: noneround cũng như nối dòng:

: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 cho thấy các nút sau khi áp dụng các kiểu trước đó.

Tuỳ chỉnh các 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 cho thấy 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 sẵn để cảnh báo người dùng về hành vi có thể phá hoại của chúng. Tôi cũng chọn tạo kiểu cho nút giao diện sáng 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 sáng hoặc tối thích hợp ở cơ bản, sau đó nút 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ĩ màu đỏ sẽ phù hợp hơn với màu đường viền tiêu điểm. Màu văn bản chuyển màu đỏ đậm thành đỏ nhạt. Tôi sẽ làm cho màu đường viền khớp với từ khoá này với từ khoá currentColor:

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

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

Tuỳ chỉnh các nút bị vô hiệu hoá

Thông thường, các nút bị tắt sẽ có độ tương phản màu kém trong khi cố gắng bỏ qua nút đã tắt để nút này có vẻ ít hoạt động hơn. Tôi đã thử nghiệm từng tập hợp màu và đảm bảo rằng chúng đã vượt qua, nhắc đến giá trị độ sáng HSL cho đến khi điểm số đạt 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 cho thấy 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à một vùng chứa cho span và một nút. CSS có thể tạo 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 thể tạo kiểu cho span. Vùng chứa được cấp max-inline-size nên sẽ không lớn hơn mức cần thiết, trong khi inline-size: 100% sẽ cho phép chính nó thu nhỏ và điều chỉnh cho vừa với các vùng chứa có kích thước nhỏ hơn. Màu nền được đặt thành màu thích ứng tối hơn các khu vực khác, vì vậy sẽ nhìn thấy 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à không được ghi đè bằng các kiểu nút khác.

: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 chút không gian.

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

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

Các ngoại lệ đặc biệt về giao diện tối

Tôi đã đặt nền tối cho các nút hành động chính để văn bản có độ tương phản cao hơn, khiến giao diện của chúng trông được quảng bá 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 cho thấy các nút sau khi áp dụng các kiểu trước đó.

Tạo biến thể

Để giải trí và vì tính thiết thực của phương pháp này rất thiết thực, nên 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 dốc.

Nút rực rỡ

Để tạo kiểu nút này, tôi đã trực tiếp ghi đè đạo cụ cơ sở bằng màu xanh dương. Mặc dù quá trình này nhanh chóng và dễ dàng, nhưng loại bỏ các đạo cụ thích ứng và trông giống nhau trong cả giao diện sáng lẫn 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 xuất hiện theo cả giao diện sáng và tối. Nó có màu xanh dương rất rực rỡ như các nút hành động chính thông thường.

Nút lớn

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

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

Nút lớn hiển thị 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 cho biết cách đạt được hiệu ứng này chỉ bằng một vài thuộc tính CSS và khả năng xử lý của nút này đối với các biểu tượng không cùng dòng với SVG.

[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 sẽ xuất hiện trong giao diện sáng và tối.

Kết luận

Giờ bạn đã biết tôi làm được như thế nào, bạn sẽ làm thế nào 🙂

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

Hãy tạo một bản minh hoạ, đường liên kết tweet me và tôi sẽ thêm bản phối lại đó vào phần bả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 để xem ở đây.

Tài nguyên