Khai phá tiềm năng của UI tương tác với hướng dẫn toàn diện về các biến thể Tailwind CSS. Tìm hiểu về tạo kiểu cho lớp giả, trạng thái, group và peer.
Làm Chủ các Biến thể (Variants) của Tailwind CSS: Tìm Hiểu Sâu về Tạo Kiểu cho Lớp giả (Pseudo-Class) và Trạng thái
Trong phát triển web hiện đại, việc tạo ra giao diện người dùng không chỉ hấp dẫn về mặt hình ảnh mà còn phải linh hoạt và đáp ứng tương tác của người dùng là điều tối quan trọng. Đây là lúc sức mạnh thực sự của một framework utility-first như Tailwind CSS tỏa sáng. Trong khi các lớp utility của nó cung cấp "cái gì"—quy tắc kiểu cụ thể để áp dụng—thì các biến thể của nó cung cấp yếu tố "khi nào" cực kỳ quan trọng.
Các biến thể (variants) là công thức bí mật biến các thiết kế tĩnh thành trải nghiệm tương tác. Chúng là các tiền tố đặc biệt cho phép bạn áp dụng các lớp utility một cách có điều kiện, dựa trên trạng thái của phần tử, tương tác của người dùng, hoặc thậm chí là trạng thái của một phần tử khác. Dù là thay đổi màu của một nút khi di chuột qua, tạo kiểu cho một ô nhập liệu khi nó được focus, hay hiển thị một thông báo khi một hộp kiểm được chọn, các biến thể chính là công cụ cho công việc đó.
Hướng dẫn toàn diện này được thiết kế cho các nhà phát triển trên toàn thế giới. Chúng ta sẽ khám phá toàn bộ phổ của các biến thể Tailwind CSS, từ các lớp giả cơ bản như hover
và focus
đến các kỹ thuật nâng cao sử dụng group
và peer
cho các tương tác component phức tạp. Khi kết thúc, bạn sẽ có kiến thức để xây dựng các giao diện tinh vi, nhận biết trạng thái hoàn toàn ngay trong HTML của mình.
Hiểu Khái niệm Cốt lõi: Variants là gì?
Về cốt lõi, một biến thể trong Tailwind CSS là một tiền tố bạn thêm vào một lớp utility, được phân tách bằng dấu hai chấm (:
). Tiền tố này hoạt động như một điều kiện. Lớp utility mà nó đứng trước sẽ chỉ được áp dụng khi điều kiện đó được đáp ứng.
Cú pháp cơ bản rất đơn giản và trực quan:
variant:utility-class
Ví dụ, hãy xem xét một nút đơn giản. Bạn có thể muốn nền của nó mặc định là màu xanh lam, nhưng sẽ chuyển sang màu xanh lam đậm hơn khi người dùng di chuột qua. Trong CSS truyền thống, bạn sẽ viết:
.my-button {
background-color: #3b82f6; /* bg-blue-500 */
}
.my-button:hover {
background-color: #2563eb; /* bg-blue-700 */
}
Với các biến thể của Tailwind, bạn đạt được kết quả tương tự trực tiếp trong HTML, giữ cho việc tạo kiểu của bạn được đặt cùng vị trí với markup:
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
Click me
</button>
Ở đây, hover:
là biến thể. Nó báo cho engine Just-In-Time (JIT) của Tailwind tạo ra một quy tắc CSS chỉ áp dụng bg-blue-700
khi nút ở trạng thái :hover
. Khái niệm đơn giản nhưng mạnh mẽ này là nền tảng cho tất cả việc tạo kiểu tương tác trong Tailwind CSS.
Các Biến thể Phổ biến nhất: Lớp giả Tương tác
Lớp giả (Pseudo-classes) là các bộ chọn CSS định nghĩa một trạng thái đặc biệt của một phần tử. Tailwind cung cấp các biến thể cho tất cả các lớp giả phổ biến mà bạn sử dụng hàng ngày để phản hồi các hành động của người dùng.
Biến thể hover
: Phản hồi với Con trỏ Chuột
Biến thể hover
có lẽ là biến thể được sử dụng thường xuyên nhất. Nó áp dụng các kiểu khi con trỏ của người dùng đang chỉ vào một phần tử. Nó rất cần thiết để cung cấp phản hồi trực quan trên các liên kết, nút, thẻ và bất kỳ phần tử nào có thể nhấp vào.
Ví dụ: Một component thẻ tương tác
Hãy tạo một thẻ được nâng lên và có bóng đổ nổi bật hơn khi di chuột qua, một mẫu phổ biến trong thiết kế UI hiện đại.
<div class="p-6 max-w-sm mx-auto bg-white rounded-xl shadow-md
transition-all duration-300
hover:shadow-xl hover:-translate-y-1">
<h3 class="text-xl font-medium text-black">Global Insights</h3>
<p class="text-slate-500">Discover trends from around the world.</p>
</div>
Trong ví dụ này:
hover:shadow-xl
thay đổi bóng hộp (box-shadow) thành một bóng lớn hơn khi di chuột qua.hover:-translate-y-1
di chuyển thẻ lên một chút, tạo hiệu ứng "nâng lên".- Chúng ta đã thêm
transition-all
vàduration-300
để làm cho sự thay đổi trạng thái trở nên mượt mà và có hiệu ứng động.
Biến thể focus
: Tạo kiểu cho Khả năng truy cập và Ô nhập liệu
Biến thể focus
rất quan trọng đối với khả năng truy cập (accessibility). Nó áp dụng các kiểu khi một phần tử được chọn, bằng cách nhấp chuột vào nó hoặc điều hướng đến nó bằng bàn phím (ví dụ: bằng phím 'Tab'). Nó được sử dụng phổ biến nhất trên các phần tử biểu mẫu như input, textarea và nút.
Ví dụ: Một ô nhập liệu được tạo kiểu tốt
Một trạng thái focus rõ ràng cho người dùng biết chính xác họ đang ở đâu trên trang, điều này rất quan trọng cho việc điều hướng chỉ bằng bàn phím.
<label for="email" class="block text-sm font-medium text-gray-700">Email Address</label>
<input type="email" id="email"
class="mt-1 block w-full px-3 py-2 bg-white border border-slate-300 rounded-md
text-sm shadow-sm placeholder-slate-400
focus:outline-none focus:border-sky-500 focus:ring-1 focus:ring-sky-500">
Đây là những gì các biến thể focus:
thực hiện:
focus:outline-none
: Loại bỏ đường viền focus mặc định của trình duyệt. Chúng ta làm điều này để thay thế nó bằng kiểu của riêng mình, hấp dẫn hơn về mặt hình ảnh.focus:border-sky-500
: Thay đổi màu đường viền thành màu xanh da trời sáng.focus:ring-1 focus:ring-sky-500
: Thêm một vầng sáng bên ngoài tinh tế (một vòng bóng hộp) cùng màu, làm cho trạng thái focus càng nổi bật hơn.
Biến thể active
: Ghi nhận các Cú nhấp và Chạm
Biến thể active
được áp dụng khi một phần tử đang được người dùng kích hoạt—ví dụ, trong khi một nút đang được nhấn xuống. Nó cung cấp phản hồi ngay lập tức rằng cú nhấp hoặc chạm đã được ghi nhận.
Ví dụ: Một nút có hiệu ứng "được nhấn"
<button class="bg-indigo-500 text-white font-semibold py-2 px-4 rounded-lg
shadow-md hover:bg-indigo-600 focus:outline-none
focus:ring-2 focus:ring-indigo-400 focus:ring-opacity-75
active:bg-indigo-700 active:translate-y-0.5">
Submit
</button>
Trong nút được cải tiến này:
active:bg-indigo-700
làm cho nút càng đậm hơn trong khi nó đang được nhấn.active:translate-y-0.5
đẩy nút xuống một chút, tạo ra hiệu ứng nhấn xuống vật lý.
Các Biến thể Tương tác khác: focus-within
và focus-visible
focus-within
: Biến thể hữu ích này áp dụng các kiểu cho một phần tử *cha* bất cứ khi nào một trong các phần tử *con* của nó nhận được focus. Nó hoàn hảo để tạo kiểu cho toàn bộ một nhóm biểu mẫu khi người dùng đang tương tác với ô nhập liệu của nó.
<div class="flex items-center space-x-2 p-4 border rounded-lg focus-within:border-blue-500 focus-within:ring-1 focus-within:ring-blue-500">
<!-- SVG Icon -->
<svg class="h-6 w-6 text-gray-400">...</svg>
<input type="text" placeholder="Search..." class="outline-none">
</div>
Bây giờ, khi người dùng focus vào <input>
, toàn bộ thẻ <div>
cha sẽ có một đường viền và vòng màu xanh lam.
focus-visible
: Các trình duyệt có các phương pháp phỏng đoán khác nhau về thời điểm hiển thị vòng focus. Ví dụ, chúng có thể không hiển thị nó trên một nút sau khi nhấp chuột, nhưng sẽ hiển thị sau khi điều hướng bằng bàn phím. Biến thể focus-visible
cho phép bạn tận dụng hành vi thông minh hơn này. Thường được khuyến nghị sử dụng focus-visible
thay vì focus
để tạo kiểu đường viền/vòng nhằm cung cấp trải nghiệm người dùng tốt hơn cho cả người dùng chuột và bàn phím.
Tạo kiểu Dựa trên Trạng thái: Các Biến thể cho Biểu mẫu và Phần tử UI
Ngoài tương tác trực tiếp của người dùng, các phần tử thường có các trạng thái dựa trên các thuộc tính của chúng. Tailwind cung cấp các biến thể để tạo kiểu cho các trạng thái này một cách tường minh.
Biến thể disabled
: Thông báo Tình trạng không khả dụng
Khi một nút hoặc ô nhập liệu biểu mẫu có thuộc tính disabled
, nó không thể tương tác được. Biến thể disabled
cho phép bạn tạo kiểu cho trạng thái này để làm cho nó rõ ràng về mặt hình ảnh cho người dùng.
<button disabled class="bg-slate-300 text-slate-500 font-bold py-2 px-4 rounded cursor-not-allowed
disabled:opacity-50">
Processing...
</button>
Ở đây, disabled:opacity-50
giảm độ mờ của nút khi thuộc tính disabled
có mặt, một quy ước phổ biến để chỉ ra trạng thái không hoạt động. Lớp utility cursor-not-allowed
càng củng cố thêm điều này.
Biến thể checked
: Dành cho Hộp kiểm và Nút Radio
Biến thể checked
rất cần thiết để tạo các hộp kiểm và nút radio tùy chỉnh. Nó áp dụng các kiểu khi thuộc tính checked
của ô nhập liệu là true.
Ví dụ: Một hộp kiểm được tạo kiểu tùy chỉnh
<label class="flex items-center space-x-3">
<input type="checkbox" class="appearance-none h-5 w-5 border border-gray-300 rounded-md
checked:bg-blue-600 checked:border-transparent">
<span class="text-gray-900 font-medium">Accept terms and conditions</span>
</label>
Chúng ta sử dụng appearance-none
để loại bỏ kiểu mặc định của trình duyệt, và sau đó sử dụng biến thể checked:
để thay đổi màu nền khi hộp được chọn. Bạn thậm chí có thể thêm một biểu tượng dấu kiểm bằng cách sử dụng các pseudo-element ::before
hoặc ::after
kết hợp với biến thể này.
Các Biến thể Xác thực Biểu mẫu: required
, optional
, valid
, invalid
Các biểu mẫu hiện đại cung cấp phản hồi xác thực theo thời gian thực. Các biến thể xác thực của Tailwind tận dụng API xác thực ràng buộc của trình duyệt. Các biến thể này áp dụng dựa trên các thuộc tính như required
và trạng thái hợp lệ hiện tại của giá trị đầu vào (ví dụ: cho type="email"
).
<input type="email" required
class="border rounded-md px-3 py-2
invalid:border-pink-500 invalid:text-pink-600
focus:invalid:border-pink-500 focus:invalid:ring-pink-500
valid:border-green-500">
Trường nhập liệu này sẽ có:
- Một đường viền và văn bản màu hồng nếu nội dung không phải là một địa chỉ email hợp lệ (
invalid:
). - Một đường viền màu xanh lá cây khi một địa chỉ email hợp lệ được nhập (
valid:
). - Vòng focus cũng sẽ chuyển sang màu hồng nếu trường được focus trong khi không hợp lệ (
focus:invalid:
).
Tương tác Nâng cao: Các biến thể `group` và `peer`
Đôi khi, bạn cần tạo kiểu cho một phần tử dựa trên trạng thái của một phần tử *khác*. Đây là lúc các khái niệm mạnh mẽ group
và peer
phát huy tác dụng. Chúng giải quyết cả một lớp các thách thức về UI mà trước đây khó có thể xử lý chỉ bằng các lớp utility.
Sức mạnh của `group`: Tạo kiểu cho Phần tử con dựa trên Trạng thái của Phần tử cha
Biến thể group
cho phép bạn tạo kiểu cho các phần tử con dựa trên trạng thái của một phần tử cha. Để sử dụng nó, bạn thêm lớp group
vào phần tử cha mà bạn muốn theo dõi. Sau đó, trên bất kỳ phần tử con nào, bạn có thể sử dụng các biến thể như group-hover
, group-focus
, v.v.
Ví dụ: Một thẻ có tiêu đề và biểu tượng cùng thay đổi màu khi di chuột qua
<a href="#" class="group block max-w-xs mx-auto rounded-lg p-6 bg-white ring-1 ring-slate-900/5 shadow-lg space-y-3
hover:bg-sky-500 hover:ring-sky-500">
<div class="flex items-center space-x-3">
<!-- SVG Icon -->
<svg class="h-6 w-6 stroke-sky-500 group-hover:stroke-white">...</svg>
<h3 class="text-slate-900 group-hover:text-white text-sm font-semibold">New Project</h3>
</div>
<p class="text-slate-500 group-hover:text-white text-sm">Create a new project from a variety of templates.</p>
</a>
Cách hoạt động:
- Chúng ta thêm lớp
group
vào thẻ<a>
cha. - Khi người dùng di chuột qua toàn bộ liên kết, màu nền của nó thay đổi nhờ
hover:bg-sky-500
. - Đồng thời, lớp
group-hover:stroke-white
trên SVG vàgroup-hover:text-white
trên các phần tử văn bản được kích hoạt, thay đổi màu của chúng thành màu trắng.
Điều này tạo ra một hiệu ứng di chuột gắn kết, toàn diện mà nếu không sẽ yêu cầu CSS hoặc JavaScript tùy chỉnh.
Tạo kiểu cho Phần tử anh em với `peer`: Một Thay đổi Cuộc chơi cho Biểu mẫu
Biến thể peer
tương tự như group
, nhưng nó hoạt động để tạo kiểu cho các phần tử anh em (sibling). Bạn thêm lớp peer
vào một phần tử, và sau đó bạn có thể sử dụng các biến thể như peer-checked
hoặc peer-invalid
trên các phần tử anh em *tiếp theo* để tạo kiểu cho chúng dựa trên trạng thái của "peer". Điều này cực kỳ hữu ích cho các điều khiển biểu mẫu tùy chỉnh.
Ví dụ: Một nhãn thay đổi khi hộp kiểm liên quan của nó được chọn
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" class="sr-only peer">
<div class="w-11 h-6 bg-gray-200 rounded-full
peer-focus:ring-4 peer-focus:ring-blue-300
peer-checked:after:translate-x-full peer-checked:after:border-white
after:content-[''] after:absolute after:top-0.5 after:left-[2px]
after:bg-white after:border-gray-300 after:border after:rounded-full
after:h-5 after:w-5 after:transition-all
peer-checked:bg-blue-600"></div>
<span class="ml-3 text-sm font-medium text-gray-900 peer-checked:text-blue-600">
Enable Notifications
</span>
</label>
Đây là một công tắc bật/tắt (toggle switch) hoàn chỉnh, có thể truy cập, được xây dựng mà không cần dùng đến JavaScript!
- Hộp kiểm
<input>
thực tế được ẩn về mặt hình ảnh bằng lớpsr-only
(nó vẫn có thể truy cập được bởi trình đọc màn hình) và được đánh dấu là mộtpeer
. - Công tắc bật/tắt trực quan là một thẻ
<div>
được tạo kiểu để trông giống như một đường ray với một tay cầm (sử dụng pseudo-element::after
). peer-checked:bg-blue-600
thay đổi màu nền của đường ray khi hộp kiểm bị ẩn được chọn.peer-checked:after:translate-x-full
trượt tay cầm sang phải khi hộp kiểm được chọn.peer-checked:text-blue-600
thay đổi màu của văn bản nhãn<span>
kế cận.
Kết hợp các Biến thể để Kiểm soát Chi tiết
Một trong những tính năng mạnh mẽ nhất của Tailwind là khả năng xâu chuỗi các biến thể lại với nhau. Điều này cho phép tạo ra các kiểu điều kiện có độ đặc hiệu cao.
Biến thể Đáp ứng và Trạng thái: Bộ đôi Năng động
Bạn có thể kết hợp các tiền tố đáp ứng (như md:
, lg:
) với các biến thể trạng thái để áp dụng các kiểu chỉ trên một số kích thước màn hình nhất định *và* trong một số trạng thái nhất định. Biến thể đáp ứng luôn đứng trước.
Cú pháp: breakpoint:state:utility-class
<button class="bg-blue-500 text-white p-2 rounded
hover:bg-blue-600
md:bg-green-500 md:hover:bg-green-600">
Responsive Button
</button>
Nút này sẽ:
- Có màu xanh lam trên màn hình nhỏ, chuyển sang màu xanh lam đậm hơn khi di chuột qua.
- Có màu xanh lá cây trên màn hình trung bình trở lên (
md:bg-green-500
), chuyển sang màu xanh lá cây đậm hơn khi di chuột qua (md:hover:bg-green-600
).
Chồng chất nhiều Biến thể Trạng thái
Bạn cũng có thể chồng chất nhiều biến thể trạng thái để áp dụng các kiểu chỉ khi tất cả các điều kiện được đáp ứng. Điều này hữu ích cho việc tinh chỉnh các tương tác.
Ví dụ: Một nút chế độ tối phản ứng khác nhau với hover và focus
<button class="p-2 rounded-full text-gray-400
dark:text-gray-500
hover:text-gray-600 dark:hover:text-gray-300
focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500
dark:focus:ring-offset-gray-900 dark:focus:ring-gray-400
dark:hover:focus:ring-gray-200">
<!-- Icon here -->
</button>
Ở đây, dark:hover:focus:ring-gray-200
áp dụng một màu vòng cụ thể chỉ khi chế độ tối đang hoạt động, nút đang được di chuột qua, *và* nó có focus. Thứ tự của các biến thể trạng thái thường không quan trọng, vì Tailwind sẽ tạo ra bộ chọn CSS chính xác cho sự kết hợp đó.
Tùy chỉnh và Các trường hợp đặc biệt
Mặc dù Tailwind cung cấp một bộ biến thể toàn diện ngay từ đầu, đôi khi bạn cần kiểm soát nhiều hơn.
Sử dụng Biến thể Tùy ý (Arbitrary Variants)
Đối với các tình huống đơn lẻ mà bạn cần một bộ chọn CSS không được bao gồm trong một biến thể tích hợp sẵn, bạn có thể sử dụng các biến thể tùy ý. Đây là một lối thoát cực kỳ mạnh mẽ cho phép bạn viết các bộ chọn tùy chỉnh trực tiếp trong thuộc tính class của mình, được đặt trong dấu ngoặc vuông.
Ví dụ: Tạo kiểu cho các mục danh sách khác nhau
<ul>
<li class="[&:nth-child(odd)]:bg-gray-100 p-2">First item</li>
<li class="[&:nth-child(odd)]:bg-gray-100 p-2">Second item</li>
<li class="[&:nth-child(odd)]:bg-gray-100 p-2">Third item</li>
</ul>
Lớp [&:nth-child(odd)]:bg-gray-100
tạo ra CSS cho li:nth-child(odd)
, tạo ra một danh sách có các hàng xen kẽ màu mà không cần thêm các lớp bổ sung vào mỗi mục.
Một cách sử dụng phổ biến khác là để tạo kiểu cho các phần tử con trực tiếp:
<div class="[&_>_p]:mt-4">
<p>First paragraph.</p>
<p>Second paragraph. This will have a top margin.</p>
<div><p>Nested paragraph. This will NOT have a top margin.</p></div>
</div>
Lớp [&_>_p]:mt-4
chỉ tạo kiểu cho các phần tử con p
trực tiếp của div.
Cấu hình Biến thể trong `tailwind.config.js`
Theo mặc định, engine JIT của Tailwind bật tất cả các biến thể cho tất cả các plugin lõi. Tuy nhiên, nếu bạn cần bật các biến thể cho các plugin của bên thứ ba hoặc muốn đăng ký một biến thể tùy chỉnh, bạn sẽ cần sử dụng tệp `tailwind.config.js` của mình.
// tailwind.config.js
module.exports = {
// ...
plugins: [
function({ addVariant }) {
addVariant('child', '& > *');
addVariant('child-hover', '& > *:hover');
}
],
}
Plugin tùy chỉnh này thêm các biến thể mới là `child` và `child-hover`, mà sau đó bạn có thể sử dụng như child:text-red-500
để tạo kiểu cho tất cả các phần tử con trực tiếp của một phần tử.
Kết luận: Sức mạnh của Giao diện người dùng Điều khiển bởi Trạng thái
Các biến thể của Tailwind CSS không chỉ là một sự tiện lợi; chúng là một phần cơ bản của triết lý utility-first. Bằng cách cho phép bạn mô tả giao diện của một phần tử trên tất cả các trạng thái tiềm năng của nó trực tiếp trong HTML, các biến thể giúp bạn xây dựng các giao diện người dùng phức tạp, mạnh mẽ và có khả năng bảo trì cao.
Từ các hiệu ứng hover
đơn giản đến các điều khiển biểu mẫu phức tạp được xây dựng với peer-checked
và các kết hợp đa trạng thái, đáp ứng, giờ đây bạn đã có một bộ công cụ toàn diện để biến các thiết kế của mình thành hiện thực. Chúng khuyến khích một tư duy dựa trên component, nơi tất cả logic—cấu trúc, kiểu dáng và trạng thái—được gói gọn ở một nơi.
Chúng ta đã đề cập đến những điều cần thiết và khám phá các kỹ thuật nâng cao, nhưng hành trình không kết thúc ở đây. Cách tốt nhất để làm chủ các biến thể là sử dụng chúng. Hãy thử nghiệm kết hợp chúng, khám phá danh sách đầy đủ trong tài liệu chính thức của Tailwind CSS, và thử thách bản thân xây dựng các component tương tác mà không cần đến CSS hoặc JavaScript tùy chỉnh. Bằng cách nắm bắt sức mạnh của việc tạo kiểu điều khiển bởi trạng thái, bạn sẽ có thể xây dựng các trải nghiệm người dùng nhanh hơn, nhất quán hơn và thú vị hơn cho khán giả toàn cầu.