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ạ và xem nguồn
Nếu bạn thích xem video, hãy xem phiên bản video của bài đăng này trên YouTube:
Tổng quan
Phần tử <button>
được tạo để tương tác với người dùng. Sự kiện click
của ứng dụng này 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ó kiểu để giúp phân biệt rõ ràng ý định của nút đó. Nút đặt lại sẽ có màu cảnh báo vì chúng có tác động huỷ bỏ, còn nút gửi sẽ có văn bản nhấn nhá màu xanh dương để chú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.
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 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 {}
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 rằng 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 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à nằm trong hoặc ngoài 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 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
và :focus
với bộ chọn giả lập 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) {
…
}
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:
Ẩn biểu tượng khỏ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 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 thay, 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 bị làm phiền bởi hình ảnh nút trang trí:
<button>
<svg … aria-hidden="true">...</svg>
Icon Button
</button>
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 đó, bạn có thể cập nhật một thuộc tính tuỳ chỉnh 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
và --_bg-dark
là tĩnh. Bạn cũng có thể đọc 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 sẽ hơi khó hiểu. :where()
được sử dụng để tuỳ chỉnh nút không cần có thông tin cụ thể. 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 đều đượ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 của 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 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
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 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, 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 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 trên nút, cải thiện khả năng đọc và thêm một lớp trình bày đẹp mắt.
--_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ủ màu theo màu của 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à 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, để tạo cho các nút có giao diện hơi 3D, hiệu ứng đổ bóng hộp 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 hai thuộc tính tĩnh để chứa 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); }
Điều chỉnh giao diện tối
Giá trị của mẫu thuộc tính tĩnh -light
và -dark
sẽ trở nên 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ỉ dễ đọc, mà người dùng các nút tuỳ chỉnh này cũng có thể sử dụng các thành phần hỗ trợ trần một cách tự tin vì chúng sẽ 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
. Việc đặt line-height
thành 1.5
sẽ đặt kích thước hộp thư để cung cấp cho văn bản một số không gian ở trên và 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);
}
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 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
để các lượt nhấp không cần phải chờ và quan sát một lượt nhấp đúp tiềm ẩn, giúp các nút cảm thấy 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 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);
}
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 khiến mắt người dùng có cảm giác nút này đượ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);
}
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 và căn chỉnh theo chiều dọc và chiều ngang các phần tử con vào 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;
}
Giãn cách
Đối với khoảng cách nút, tôi đã sử dụng gap
để các nút đồng cấp 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);
}
Trải nghiệm người dùng khi chạm và dùng chuột
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 biểu tượng có đường viền và gán các kiểu đó ở đây bằng các đầu và điểm nối đường viền fill: none
và round
:
: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;
}
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 thành màu nhấn:
:where(
[type="submit"],
form button:not([type],[disabled])
) {
--_text: var(--_accent);
}
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;
}
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 bộ màu và đảm bảo chúng đã vượt qua, điều chỉnh giá trị độ sáng HSL cho đến khi điểm số vượt qua trong DevTools 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);
}
Tuỳ chỉnh nút nhập tệp
Nút nhập tệp là vùng chứa cho một span và một nút. CSS có thể định kiểu cho vùng chứa dữ liệu đầu vào một chút, cũng như nút lồng nhau, nhưng không thể định kiểu 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 đậm 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);
}
Các trường hợp ngoại lệ đặc biệt về 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 có giao diện được quảng bá một chút.
@media (prefers-color-scheme: dark) {
:where(
[type="submit"],
[type="reset"],
[disabled],
form button:not([type="button"])
) {
--_bg: var(--_input-well);
}
}
Tạo biến thể
Để vui và vì tính thực tế, tôi đã chọn hướng dẫn 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 được tô màu chuyển tiếp.
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 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 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 hiệu ứng này 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));
}
Kết luận
Giờ thì bạn đã biết cách tôi làm, còn bạn thì sao‽ 🙂
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
- Mã nguồn trên GitHub