4 lý do bạn nên sử dụng Tailwind CSS
bởi Vương PhạmTailwind CSS là một giải pháp giúp cách tiếp cận Utility first CSS trở nên thông dụng. Nó có sức ảnh hưởng lớn tương tự như ReactJS đối với lập trình UI hoặc Ruby on Rails đối với thiết kế web framework. Đây không chỉ là một ý tưởng nhất thời của Adam Wathan. Ông cũng không sao chép một công nghệ cũ (như nhiều web framework xây dựng trên NodeJS hiện nay) mà Tailwind CSS thực sự giải quyết được những vấn đề khó khăn của CSS hiện đại.
Tailwind CSS giải quyết các vấn đề khó khăn của lập trình CSS
Tôi đã thấy nhiều phong cách lập trình CSS. Phần lớn các phong cách này trở nên không phù hợp khi codebase lớn dần, trang web có giao diện phức tạp hơn hoặc team lập trình có nhiều thành viên hơn.
Sau đây là một vài phong cách phổ biến. Mỗi phong cách này sẽ cho chúng ta thấy một hoặc vài khó khăn khi lập trình CSS.
Viết CSS có ngữ nghĩa? (Semantic)
.homepage {
background-color: #f0f0f0;
padding: 1rem;
border-radius: 5px;
.title {
font-size: 1.5rem;
color: #333;
margin-bottom: 0.5rem;
}
.call-to-action {
background-color: #007bff;
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 3px;
cursor: pointer;
&:hover {
background-color: #0056b3;
}
&.primary {
background-color: #28a745;
&:hover {
background-color: #1e7e34;
}
}
}
}
Đây là một trong những cách viết CSS đầu tiên mà có lẽ bạn sẽ được học. Ngữ nghĩa ở đây có nghĩa là homepage
(trang chủ), title
(tiêu đề) hay call-to-action
(kêu gọi hành động) là tên của từng phần tử của trang.
Những người ủng hộ cách viết này chỉ ra ưu điểm của nó là CSS được viết ra sẽ không ảnh hưởng tới cấu trúc HTML (HTML markup). Các lớp CSS trong cấu trúc HTML đơn giản là phân tách và đặt tên cho các phần tử của trang, với mỗi file CSS khác nhau, giao diện trang sẽ khác nhau. Điều này đúng. Tuy nhiên, ở quy mô lớn, ưu điểm này quá nhỏ so với những nhược điểm của nó.
Trong trường hợp bạn cần một giao diện tương tự trang chủ (.homepage
), nhưng nó lại ở một trang khác. Để tuân thủ theo ngữ nghĩa, bạn có hai lựa chọn.
Lựa chọn thứ nhất là sao chép toàn bộ các quy tắc CSS của .homepage
sang .pricing-page
. Nếu sử dụng cách này ở quy mô lớn, dung lượng của file CSS của bạn sẽ rất lớn, tốc độ trang web của bạn sẽ bị ảnh hưởng nặng nề.
Lựa chọn thứ hai (được phần lớn lập trình viên ưa thích) là sử dụng .homepage, .pricing-page {
. Với cách này, nếu trang pricing có sự khác biệt về giao diện so với trang chủ, bạn sẽ phải định nghĩa thêm nhiều lớp CSS khác. Các lớp CSS này sẽ không tránh khỏi việc phải ghi đè các lớp CSS con của trang chủ.
Trong các dự án mà tôi đã tham gia, việc sử dụng lại các CSS class có sẵn bằng cách ghi đè chúng thường dẫn tới việc lạm dụng độ ưu tiên CSS (CSS Specificity). Các lớp CSS chồng chéo nhau quá nhiều dẫn tới code quá khó hiểu hoặc không thể chỉnh sửa hay ghi đè được nữa.
Một nhược điểm khác ở đây là, bằng cách sử dụng các lớp CSS lồng nhau như trên, ở những trang có bố cục (layout) phức tạp, sẽ dẫn tới việc cấu trúc HTML (HTML markup) bắt buộc phải tuân theo một cấu trúc cố định thì code CSS mới hoạt động đúng.
Những nhược điểm nghiêm trọng này dẫn đến sự ra đời của BEM.
Viết CSS tuân theo BEM? (Block – Element – Modifier)
<button class="button button--primary">
<span class="button__text">Click Me</span>
</button>
/* Block: button */
.button {
display: inline-block;
padding: 10px 20px;
border: none;
border-radius: 5px;
font-size: 16px;
cursor: pointer;
}
/* Element: button__text */
.button__text {
color: #333;
}
/* Modifier: button--primary */
.button--primary {
background-color: #007bff;
color: white;
}
Đối với BEM, một CSS block
đại diện cho một kiểu phần tử trên trang, ví dụ như accordion
, button
,… Mỗi block
gồm nhiều element
con với định dạng tên lớp là <tên block>__<tên element>
. Mỗi block
có thể được thay đổi một chút bằng cách sử dụng modifier
với định dạng tên lớp <tên block>--<tên variant của block>
hoặc <tên block>__<tên element>--<tên variant của element>
.
Có thể thấy, quan điểm của BEM hoàn toàn trái ngược với cách viết CSS theo ngữ nghĩa:
BEM không chú trọng vào việc tên lớp CSS phải có ý nghĩa mà ưu tiên việc đặt tên dựa trên UI pattern (các kiểu giao diện thường thấy trên một trang web): button, accordion, card, dropdown, ..v..v.
Việc tạo các lớp CSS dựa trên UI pattern giúp việc tái sử dụng chúng trên nhiều trang khác nhau dễ dàng hơn. Đây cũng là lý do các thư viện như Bootstrap, Semantic UI,… rất nổi tiếng và thông dụng trước khi ReactJS ra đời. Cách làm này giúp lập trình viên tiết kiệm rất nhiều thời gian.
BEM khuyến khích viết CSS theo cấu trúc phẳng, hạn chế việc lồng các lớp CSS. Các lớp modifier được áp dụng trực tiếp vào element của block mà nó muốn thay đổi. BEM không khuyến khích việc thay đổi CSS của element bằng cách thêm các quy tắc CSS vào phần tử cha của nó. Quy tắc này giúp giảm thiểu tối đa việc lạm dụng độ ưu tiên (specificity) của CSS.
BEM cũng có những nhược điểm riêng. Việc viết tên các lớp CSS theo BEM thường rất mất công và tẻ nhạt với các tên lớp rất dài. Một vấn đề khác là modifier của BEM thường hay lặp lại. Ví dụ:
.accordion__text--align-right {
text-align: right;
}
.title__text--align-right {
text-align: right;
}
Mặc dù hai modifier này có nội dung giống nhau nhưng tên của chúng lại khác nhau vì chúng được áp dụng cho hai UI pattern khác nhau. Ở những codebase lớn tuân theo BEM, vấn đề này thường xuyên xảy ra. Các thư viện nổi tiếng như Bootstrap hay Bulma sử dụng các lớp utility như text-right
, text-center
để giải quyết vấn đề này. Tuy nhiên, số lượng utility class thường không nhiều vì nó sẽ ảnh hưởng đến kích thước file CSS.
Ngoài ra, khi một website được phát triển đủ lâu, các UI pattern sẵn có từ Bootstrap hay Bulma sẽ không còn đủ. Điều này đòi hỏi lập trình viên phải sáng tạo thêm UI pattern trong quá trình làm việc, nếu không sẽ lại gặp những vấn đề tương tự khi lập trình CSS theo ngữ nghĩa.
Việc phát triển UI pattern còn được gọi là phát triển Design System cho trang web. Đây là một công việc khó và do ngành lập trình còn non trẻ, số lượng lập trình viên giỏi chưa nhiều, đặc biệt ở các team lớn với trình độ lập trình viên không đồng đều, việc duy trì design system một cách dễ dàng là rất khó khăn.
Code CSS cần phải như thế nào để dễ bảo trì?
Tóm lại, ở quy mô lớn, để duy trì code CSS và đảm bảo hiệu năng, chúng ta cần chú ý:
(1) Tránh để các thuốc tính CSS bị lặp lại, gây tăng kích thước file CSS.
Đối với (1), Tailwind CSS, với cách tiếp cận ưu tiên utility và post processor để xóa tất cả các utility class không được dùng, đảm bảo các thuộc tính CSS sẽ không bị lặp lại và không có đoạn CSS nào dư thừa.
(2) Tránh lạm dụng độ ưu tiên (specificity) của CSS, ưu tiên cấu trúc CSS phẳng, tránh ghi đè lớp CSS này bằng lớp CSS khác.
Đối với (2), các utility class của Tailwind CSS luôn được ưu tiên áp dụng trực tiếp vào thẻ HTML. Đội ngũ của Tailwind CSS còn phát triển các công cụ hỗ trợ code (code intellisense) để thông báo khi bạn đang sử dụng 2 utility class chỉnh sửa cùng một thuộc tính CSS.
(3) Chắt lọc UI pattern, phát triển Design System song song với việc viết code CSS.
Đối với (3), Tailwind CSS đặt nền móng cho việc tạo ra Design System khi định nghĩa các bộ thuộc tính như sizing
(kích thước), spacing
(khoảng cách), typography
(kiểu chữ),… Chúng là những khối xây dựng cơ bản để tạo ra Design System và là yếu tố mang lại sự đồng nhất trong giao diện, thường thấy khi ứng dụng Design System.
Ngoài ra, các UI pattern được xây dựng dựa trên Tailwind CSS rất dễ sao chép từ codebase này sang codebase khác, vì vậy chúng thường được chia sẻ rộng rãi. Bộ UI pattern của Bootstrap trước đây gần như chỉ có thể được xây dựng và mở rộng bởi đội ngũ phát triển cốt lõi của nó, nhưng các thư viện UI dựa trên Tailwind CSS có thể được mở rộng bởi cả cộng đồng. Nếu một UI pattern được xây dựng không tốt, nó cũng có thể bị xóa khỏi codebase của bạn một cách dễ dàng.
Để bạn hiểu rõ hơn về tầm quan trọng của 3 tiêu chí trên đối với code CSS ở quy mô lớn, tôi sẽ đi sâu hơn vào lợi ích của Tailwind CSS trong từng tiêu chí.
Tailwind CSS giảm kích thước và độ phức tạp của file CSS
Với các lớp Utility của Tailwind CSS, các giá trị của thuộc tính CSS không còn là vô hạn. Trước đây, bạn có thể định nghĩa hàng trăm giá trị margin, padding, font-size, color,… Ở quy mô lớn, kích thước file CSS sẽ tăng lên rất nhanh. Tailwind CSS giới hạn số lượng utility class cho mỗi thuộc tính, thường chỉ còn khoảng mười giá trị hữu hạn, dẫn đến kích thước file CSS cũng trở nên hữu hạn.
Khi các thuộc tính CSS không bị lặp lại, kích thước file CSS sẽ nhỏ hơn và trình duyệt sẽ xử lý file CSS nhanh hơn. Khi các CSS class không ghi đè lẫn nhau, file CSS sẽ ít phức tạp hơn, trình duyệt sẽ vẽ giao diện nhanh hơn và không phải vẽ lại nhiều lần. Cả hai lợi ích này đều giúp tăng tốc độ của trang web.
Ngoài ra, khi kích thước file CSS nhỏ, bạn có thể sử dụng kỹ thuật nhúng trực tiếp nội dung file này vào thẻ <style>
và đặt nó trong <head>
của tài liệu HTML. Làm như vậy, trình duyệt có thể nhanh chóng có được style của trang web trong yêu cầu đầu tiên tới máy chủ mà không cần thông qua thẻ link rel="stylesheet"
. Đây cũng là kỹ thuật mà rất nhiều framework thông dụng hiện nay như NuxtJS, NextJS sử dụng để có tốc độ tải trang nhanh.
Với độ phức tạp thấp của file CSS, các chỉ số của Google Pagespeed Insight (PSI) như First Contentful Paint (FCP), Cumulative Layout Shift (CLS) hay Time to Next Paint sẽ loại bỏ được một yếu tố lớn gây cản trở.
Tailwind CSS giúp phát triển Design System cho trang web
Giới hạn các giá trị cho phép của các thuộc tính CSS là bước đầu tiên để xây dựng Design System mà Tailwind CSS đã thực hiện sẵn cho bạn.
Material UI Design System là một Design System khá nổi tiếng trong cộng đồng lập trình viên và thiết kế web. Tuy nhiên, một trong những điểm yếu mà ai cũng phải thừa nhận là rất khó tùy chỉnh và mở rộng. Một trong những lý do là cấu trúc HTML và CSS của Material UI rất phức tạp và có nhiều lớp.
Tuy nhiên, khi tôi quan sát các Design System dựa trên Tailwind CSS như Radix UI, DaisyUI, Flowbite,… một điều dễ nhận thấy là các System này dễ mở rộng và thay đổi hơn. Các component của chúng có cấu trúc HTML markup đơn giản và dễ hiểu, đồng thời vẫn có giao diện đẹp và tính năng không thua kém Material UI. Có vẻ như Tailwind CSS đã đặt nền móng cho một phong cách thiết kế Design System thiết thực hơn. Bạn thậm chí có thể kết hợp các component của nhiều Design System lại với nhau.
Tailwind CSS giúp tăng năng suất làm việc của bạn
Bạn không cần phải chuyển đổi qua lại giữa file HTML markup và file CSS để đọc hiểu và thay đổi giao diện trang web của bạn. Người ta thường gọi đây là chi phí chuyển đổi ngữ cảnh (Context switching cost).
Với những lợi ích mà Tailwind CSS mang lại trong việc cấu trúc mã CSS, công việc bảo trì, tùy chỉnh và chỉnh sửa mã CSS của bạn sẽ trở nên dễ dàng và đỡ tốn thời gian hơn rất nhiều.
Tôi thấy rằng với mức độ đồng nhất cao và sự khác biệt nhỏ giữa các dự án khi thiết lập Tailwind CSS và các thư viện UI của nó (flowbite, daisy), bạn sẽ dễ dàng sử dụng lại được kiến thức về chúng trong các công việc như mở rộng và phát triển thêm.
Kết luận
Nhiều người khá hoài nghi về Tailwind CSS vì một vài lý do như:
(1) Utility class thì không khác gì inline styles.
Họ đã bỏ qua một sự khác biệt rất lớn: inline styles cho phép bạn viết vô số thuộc tính CSS, còn Utility class thì không. Sử dụng inline styles cũng không giúp bạn xử lý vấn đề tương thích trình duyệt như Utility class.
(2) Để tạo kiểu cho một thẻ HTML đơn giản thì cần một đoạn CSS class rất dài. Ví dụ như button:
w-full lg:w-auto inline-flex cursor-pointer select-none items-center justify-center overflow-hidden relative bg-primary-950 text-white font-semibold text-md uppercase hover:bg-white hover:text-primary-950 border-2 border-primary-950 px-7 py-3 duration-200 gap-4 rounded-3xl
Với sự phổ biến của Component library (ReactJS, VueJS), đây không còn là một vấn đề lớn vì bạn có thể gói gọn những đoạn CSS này và ẩn nó bên trong các component.
Việc ghi đè CSS class của component cũng khá đơn giản, các công cụ như Tailwind merge giúp bạn loại bỏ các Utility class đã bị ghi đè, trình duyệt sẽ không phải xử lý những class này.
Tailwind CSS đại diện cho lớp công nghệ mới sử dụng hướng tiếp cận Utility first để lập trình CSS. Với mỗi giải pháp công nghệ mới, chúng ta đều phải xây dựng lại cơ sở hạ tầng cho chúng từ đầu. Cơ sở hạ tầng ở đây ý tôi muốn nói đến là xử lý lại những vấn đề cũ bằng công nghệ mới, kỳ vọng là chúng sẽ được xử lý tốt hơn, và xử lý các vấn đề của công nghệ mới bằng những giải pháp tốt đã có. Ví dụ như sự ra đời của DaisyUI, Flowbite để thay thế Bootstrap hay việc sử dụng Component library để gói gọn nhược điểm phải sử dụng nhiều Utility class.
Tôi nghĩ Tailwind CSS là một giải pháp đáng để chúng ta dành thời gian xây dựng lại toàn bộ cơ sở hạ tầng lập trình web cho nó.