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, có khả năng phản hồi 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 phần tử <button> có khả năng thích ứng với màu sắc, có tính 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 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, thì đây là phiên bản video của bài đăng này trên YouTube:

Tổng quan

Browser Support

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

Source

Phần tử <button> được tạo để người dùng tương tác. Sự kiện click này 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. Thẻ 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 mà không cần tuỳ chỉnh. Bạn cũng có thể 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 phần nhúng Codepen trước đó. <button> không có kiểu sẽ thích ứng với việc nằm trong <form>, thay đổi thành kiểu 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 GUI Challenge (Thử thách về giao diện người dùng) của tháng này, mỗi nút sẽ có kiểu để giúp phân biệt trực quan mục đích của nút. Nút đặt lại sẽ có màu cảnh báo vì đây là nút huỷ bỏ và nút gửi sẽ có văn bản nhấn màu xanh dương để xuất hiện nổi bật hơn một chút so với các nút thông thường.

Bản xem trước của bộ nút cuối cùng thuộc tất cả các loại, xuất hiện trong biểu mẫu và không xuất hiện 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.
Bản xem trước của nhóm cuối cùng gồm tất cả các loại nút, xuất hiện trong một biểu mẫu và không xuất hiện trong một biểu mẫu, với những điểm bổ sung thú vị cho các nút biểu tượng và nút tuỳ chỉnh

Các nút cũng có cá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 hook CSS để tuỳ chỉnh cảm giác của nút: :hover khi chuột ở trên nút, :active khi chuột hoặc bàn phím đang nhấn và :focus hoặc :focus-visible để hỗ trợ trong việc tạo kiểu cho công nghệ hỗ trợ.

button:hover {}
button:active {}
button:focus {}
button:focus-visible {}
Bản xem trước của nhóm cuối cùng gồm tất cả các loại nút trong giao diện tối.
Xem trước nhóm 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 đượ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, hoạt động như một nút gửi. Tôi cũng chuyển đổi chiến lược biểu tượng, từ SVG nội tuyến sang SVG được che, để đả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 tổ hợp khá phức tạp tại thời điểm này. Giữa các loại nút, lớp giả và việc nằm trong hay ngoài biểu mẫu, có hơn 20 tổ hợp nút. CSS có thể giúp chúng ta trình bày rõ ràng từng điểm!

Hỗ trợ tiếp cận

Các phần tử nút có thể truy cập một cách tự nhiên nhưng có một số điểm cải tiến thường gặp.

Di chuột và tập trung cùng nhau

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

button:is(:hover, :focus) {
  
}
Hãy dùng thử bản minh hoạ!

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

Tôi muốn tạo hiệu ứng cho vòng tiêu điểm đối với 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 hiệu ứng cho đường viền di chuyển ra khỏi nút 5px, nhưng chỉ khi nút không hoạt động. Thao tác này sẽ tạo ra hiệu ứng khiến vòng tiêu điểm 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 đạt yêu cầu

Có ít nhất 4 tổ hợp màu khác nhau giữa màu sáng và màu 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:

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

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 trê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 bị mất thị lực. Rất may 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ờ đó những người bị mất thị lực sẽ không bị làm phiền bởi hình ảnh nút trang trí:

<button>
  <svg … aria-hidden="true">...</svg>
  Icon Button
</button>
Chrome DevTools cho thấy cây hỗ trợ tiếp cận cho nút. Cây này bỏ qua hình ảnh nút vì có aria-hidden được đặt thành true.
Công cụ của Chrome cho nhà phát triển cho thấy 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

Kiểu

Trong phần tiếp theo này, 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 những 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 trong 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 cho phù hợp. Sau đó, một thuộc tính tuỳ chỉnh duy nhất được dùng để giữ giá trị hiện tại của giao diện và được chỉ định cho một thuộc tính CSS. Sau đó, bạn có thể cập nhật thuộc tính tuỳ chỉnh duy 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 có tính khai báo và rõ ràng. Việc gián tiếp và trừu tượng hoá được chuyển vào thuộc tính tuỳ chỉnh --_bg. Đây hiện là thuộc tính "phản ứng" duy nhất; --_bg-light--_bg-dark là các thuộc tính 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à ban đầu có vẻ hơi phức tạp. :where() được dùng nên bạn không cần phải chỉ định cụ thể khi tuỳ chỉnh nút. Các nút thường được điều chỉnh cho các trường hợp thay thế và bộ chọn :where() đảm bảo rằng nhiệm vụ diễn ra dễ dàng. Bên trong :where(), mỗi loại nút đều được chọn, bao gồm cả ::file-selector-buttonkhông thể 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ẽ được đặt trong bộ chọn này. Đã đến lúc xem xét tất cả các thuộc tính tuỳ 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 trong quá trình trình bày, sau đó chia sẻ các ngữ cảnh tối và giảm chuyển động ở cuối phần này.

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

Các nút và biểu tượng gửi là nơi lý tưởng để 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 chữ trên nút không phải là màu trắng hoặc đen, mà là 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 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 nút tuân theo cùng một mẫu hsl(), ngoại trừ các nút có 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 khiến chúng xuất hiện gần với 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

Màu nền này dùng để làm cho một vùng hiển thị xuất hiện phía sau các vùng hiển thị khác, hữu ích cho nền của đầ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, chiều 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ỷ lệ nút một cách tương ứng:

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

Đường viền nút

Bán kính đường viền của nút được lưu trữ trong một thuộc tính tuỳ chỉnh để đầu 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 làm nổi bật khi di chuột lên nút

Các thuộc tính này thiết lập một thuộc tính kích thước để chuyển đổi khi tương tác và màu làm nổi bật tuân theo hệ thống màu thích ứng. Chúng ta sẽ đề cập đến cách các thành phần này tương tác với nhau ở phần sau của bài đăng này, nhưng cuối cùng thì các thành phần này được dùng cho 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 nút

Mỗi nút đều có kiểu đổ bóng văn bản mờ. Điều này giúp văn bản nằm phía trên nút, cải thiện khả năng đọc và thêm một lớp hoàn thiện đẹ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 2 ký tự nhờ vào đơn vị chiều dài tương đối ch. Điều này sẽ giúp biểu tượng có tỷ lệ tương ứng với văn bản trên 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

Để thích ứng với ánh sáng và bóng tối một cách phù hợp, bóng cần thay đổi cả màu sắc và độ mờ. Bóng đổ trong giao diện sáng sẽ hiệu quả nhất khi có màu sắc tinh tế và phủ lên màu bề mặt mà chúng chồng lên. Đổ bóng trong 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 tối hơn của bề mặt.

--_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 các màu và độ đậm thích ứng, tôi có thể tạo ra 2 độ 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ó vẻ hơi giống 3D, một 1px box-shadow 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 cho màu sắc thích ứng, tôi tạo 2 thuộc tính tĩnh để lưu giữ các lựa chọn của 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ác thuộc tính 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 song song trong giao diện sáng và tối.

Thích ứng với giao diện tối

Giá trị của mẫu thuộc tính tĩnh -light-dark sẽ rõ ràng khi bạn đặt các thuộc tính 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ỉ đọc tốt, mà người dùng các nút tuỳ chỉnh này có thể tự tin sử dụng các prop 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.

Chế độ thích ứng giảm chuyển động

Nếu người dùng truy cập này không gặp 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 được chia sẻ

Bạn cần đặt phông chữ của các nút và dữ liệu đầu vào thành inherit để chúng khớp với các phông chữ còn lại của trang; nếu không, trình duyệt sẽ tạo kiểu cho chúng. Điều 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 thư để văn bản có một khoảng trống ở trên và bên 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 bạn áp dụng các kiểu trước đó.

Tạo kiểu cho 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, phần tử giả ::file-selector-button mới là phần 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à chế độ cảm ứng

Trước tiên, tôi tạo kiểu cho con trỏ theo kiểu pointer. Điều này giúp nút cho người dùng chuột biết rằng nút này có thể tương tác. Sau đó, tôi thêm touch-action: manipulation để các lượt nhấp không cần phải đợi và quan sát một lượt nhấp đúp tiềm ẩn, 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 sắc và đường viền

Tiếp theo, tôi tuỳ chỉnh cỡ 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 bạn á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 tuyệt vời. text-shadow thích ứng với ánh sáng và bóng tối, tạo ra một giao diện tinh tế dễ chịu cho văn bản nút nằm gọn gàng trên nền. Đối với box-shadow, 3 bóng được chỉ định. Đầu tiên, --_shadow-2 là một hiệu ứng đổ 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 vát lên một chút. Đổ bóng cuối cùng cho phần làm nổi bật khi di chuột, ban đầu có kích thước là 0, nhưng sau đó sẽ được chỉ định kích thước và chuyển đổi để có vẻ như lớn dần lê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 cho thấy các nút sau khi bạn áp dụng các kiểu trước đó.

Bố cục

Tôi đã đặt cho nút này bố cục flexbox, cụ thể là bố cục inline-flex sẽ 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 các 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à các phần tử 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 cho thấy các nút sau khi bạn áp dụng các kiểu trước đó.

Giãn cách

Đối với khoảng cách giữa các nút, tôi đã sử dụng gap để ngăn các nút anh em chạm vào nhau và các thuộc tính logic cho khoảng đệm để khoảng cách giữa các nút 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 cho thấy các nút sau khi bạn áp dụng các kiểu trước đó.

Trải nghiệm người dùng khi dùng chuột và thao tác chạm

Phần tiếp theo này chủ yếu dành cho người dùng thiết bị di động có màn hình cảm ứ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 thường thấy trên các thiết bị cảm ứng khi người dùng nhấn và giữ một nút, đồng thời hệ điều hành 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 các ứ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 none. 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à những tính năng khác của nút rấ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 sẽ xoá 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 chỉ định cho thuộc tính transition:

: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 của điểm nổi bật bằng bóng để tạo cho nút một diện mạo tiêu điểm đẹp, có vẻ như đang 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 mang đến cho nút một giao diện tiêu điểm đẹp, có vẻ như tăng lê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 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 bằng cách sử dụng các thuộc tính logic nội tuyến và theo 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ị bóp méo. Cuối cùng, tôi chọn biểu tượng có đường viền và chỉ định các kiểu đó ở đây bằng fill: noneround đầu và điểm nối đường thẳ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 bạn á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ó vẻ ngoài nổi bật hơn 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 thành 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 bạn á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ó sẵn một số dấu hiệu cảnh báo để cảnh báo người dùng về hành vi có khả năng gây hại của họ. Tôi cũng chọn tạo kiểu cho nút giao diện sáng bằng nhiều điểm nhấn mà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 nền sáng hoặc tối thích hợp, 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ĩ 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ẽ thay đổi từ đỏ đậm sang đỏ nhạt. Tôi điều chỉnh màu đường viền cho phù hợ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 cho thấy các nút sau khi bạn áp dụng các kiểu trước đó.

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

Các nút bị vô hiệu hoá thường có độ tương phản màu kém trong quá trình cố gắng làm dịu nút bị vô hiệu hoá để nút đó trông ít hoạt động hơn. Tôi đã kiểm thử từng bộ màu và đảm bảo chúng đạt yêu cầu, điều chỉnh giá trị độ sáng HSL cho đến khi điểm số đạt yêu cầu 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 bạn áp dụng các kiểu trước đó.

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

Nút nhập tệp là một vùng chứa cho một khoảng 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 tạo kiểu cho khoảng. Vùng chứa được cung 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 vùng chứa 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ột màu thích ứng tối hơn các bề mặt khác, vì vậy, màu này sẽ xuất hiện phía sau nút chọn tệp.

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

Nút chọn tệp và nút loại đầu vào được chỉ định cụ thể 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 khoảng trống 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 cho thấy các nút sau khi bạn áp dụng các kiểu trước đó.

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

Tôi đã đặt nền tối hơn cho các nút hành động chính để văn bản có độ tương phản cao hơn, giúp các nút này trông nổi bật 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 bạn áp dụng các kiểu trước đó.

Đang 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ể có màu sắc rất tươi sáng, tương tự như cách các nút chính thường xuất hiện. 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 được tô bằng màu chuyển sắc.

Nút sống động

Để đạt được kiểu nút này, tôi đã ghi đè trực tiếp các thuộc tính cơ sở bằng màu xanh dương. Mặc dù cách này nhanh chóng và dễ dàng, nhưng nó sẽ xoá các thuộc tính thích ứng và có giao diện 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 xuất hiện ở giao diện sáng và tối. Đây là màu xanh dương rất tươi sáng, giống như màu của các nút hành động chính thông thường.

Nút lớn

Kiểu nút này có được bằng cách sửa đổi thuộc tính tuỳ chỉnh --_size. Khoảng đệm và các phần tử khoảng trắng khác có liên quan đến kích thước này, tỷ lệ thuận 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 thấy cách đạt được hiệu ứng này chỉ bằng một vài thuộc tính CSS và mức độ hiệu quả của nút khi xử lý các biểu tượng không phải là 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 làm, vậy bạn sẽ làm như thế 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 một bản minh hoạ, gửi đường liên kết cho tôi qua Twitter và tôi sẽ thêm bản minh hoạ đó 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 ở đây.

Tài nguyên