Đập đi làm lại blog với Tailwind CSS

Đập đi làm lại blog với Tailwind CSS
Photo by Pankaj Patel from Unsplash

Sang công ty mới được tiếp xúc với nhiều công nghệ quá. Cái gì với mình cũng mới, cái gì cũng lạ 🤣 Trong đó, lần đầu tiên mình biết đến một framework CSS là Tailwind CSS. Framework này khác hẳn với các framework trước đây mình đã từng dùng. Vì quá mới lạ, nên mình quyết tâm phải học cho bằng được, và cách học tốt nhất mình nghĩ ra được là đập đi làm lại toàn bộ (tất nhiên là giao diện thôi) của blog này bằng Tailwind CSS.

Bối cảnh

Sang công ty mới, thì trong tháng đầu tiên người ta giao cho mình học ngôn ngữ PHP và framework rất nổi tiếng của nó là Laravel. Nguyên nhân là do đây là một ngôn ngữ quan trọng (các ngôn ngữ quan trọng của backend bao gồm Java, Node.js, PHP, Python), thường được đưa ra để khách hàng lựa chọn do hiệu suất tốt và cộng đồng cực kỳ phát triển. Trong khi đó, ngôn ngữ Ruby và framework Ruby on Rails mình đã làm rất nhiều thì hoàn toàn không được chú ý tới do hiệu suất kém, điều khá lạ với một công ty thuần Nhật.

Vừa học Laravel thì được biết đến Tailwind CSS. Và thế là dù mới học Laravel ở mức cho biết, còn chưa viết được trang web nào ra hồn, thì mình đã chuyển sang học Tailwind CSS rồi. Tất cả là do Tailwind CSS quá mới lại, khác hẳn với các framework CSS trước đây mình đã từng dùng (như Bootstrap hay MaterializeCSS) trong khi Laravel thì lại quen quen 😊

Tailwind CSS là gì?

Khái quát

Ngay trang chủ của Tailwind CSS đã viết:

Rapidly build modern websites without ever leaving your HTML.

A utility-first CSS framework packed with classes like flex, pt-4, text-center and rotate-90 that can be composed to build any design, directly in your markup.

Đọc thế này mà hiểu được thì đúng là thánh 😄. Tất nhiên đó là lời giới thiệu thôi, tuy nhiên càng tìm hiểu và sử dụng Tailwind CSS thì mình càng thấy lời giới thiệu này hoàn toàn đúng. Ngoài ra thì ngay trong trang chủ cũng có thêm một đoạn như sau:

“Best practices” don’t actually work.

I’ve written a few thousand words on why traditional “semantic class names” are the reason CSS is hard to maintain, but the truth is you’re never going to believe me until you actually try it. If you can suppress the urge to retch long enough to give it a chance, I really think you’ll wonder how you ever worked with CSS any other way.

Với những gì đã trải qua thì mình hoàn toàn đồng ý với quan điểm này. Mình đã từng làm nhiều dự án sử dụng các framework dựng sẵn (chủ yếu là Bootstrap), mọi việc thường diễn ra theo hướng thế này:

  • Lúc đầu mọi thứ rất ổn, vì Bootstrap đã dựng sẵn hầu hết các thành phần cần sử dụng cho một trang web từ header, footer, form, button, v.v…
  • Dần dần, mọi thứ phức tạp hơn, chúng mình bắt đầu tự đặt các class, tự custom lại CSS nhưng vẫn dựa vào class dựng sẵn.
  • Dần dần, code CSS chúng mình custom cũng lớn dần lên và ngày càng phức tạp hơn. Và vì nó được xây dựng bởi một team có nhiều trình độ (và chủ yếu là trình độ không cao về CSS do các developer mà mình làm việc cùng đều làm backend là chủ yếu) nên việc maintain trở nên rất khó khăn.
  • Lúc này tình hình làm việc với UI trở nên rất vất vả. Mỗi thay đổi nhỏ về UI cũng thường rất mất thời gian để tìm hiểu và sửa. Ngoài ra, thì việc đảm bảo sửa UI ở trang này không làm hỏng UI của trang khác cũng là một vấn đề rất đau đầu.
  • Lúc này giải pháp là với mỗi trang sẽ đặt 1 class theo tên của trang đó, vào bao toàn bộ các code CSS custom cho từng trang vào trong class này. Có nhiều dự án chúng mình đã rút kinh nghiệm nên triển khai kiểu này ngay từ đầu. Tuy nhiên, cách này chỉ đảm bảo UI hoạt động được thôi chứ không đảm CSS được maintain tốt do các thành phần được lặp lại khá nhiều. Những lúc như vậy, mình có cảm giác rằng thà giao hết UI cho một người đảm nhận còn tốt hơn (nhưng giải pháp này chỉ tốt nếu có 1 frontend developer mà thôi).
  • Chúng mình có một giải pháp cho vấn đề lặp code này, đó là đặt các class dạng tổng quát và sử dụng ở mọi nơi. Do trình độ có hạn nên các class này của chúng mình chủ yếu là các class về margin, padding, background, text, v.v… Nói chung là các class rất đơn giản. Thế nhưng giờ đây khi gặp Tailwind CSS thì mình thấy framework này đã triển khai các class kiểu như thế này từ rất lâu rồi.

Trong các phần tiếp theo, mình sẽ trình bày sơ qua về Tailwind CSS và cách sử dụng

Lưu ý rằng, việc sử dụng Tailwind CSS nghe rất lý tưởng, rất hợp với bài toán của chúng mình như trên, nhưng có thể nói rằng, dùng được Tailwind CSS cũng phải có trình độ về CSS nhất định. Ví dụ những developer đã từng làm việc với mình vốn là backend developer đã quen kiểu làm với Bootstrap thì nếu phải làm việc với Tailwind CSS thì khả năng không làm được gì là rất cao. Họ cần thời gian catch up và cần phải có một người dựng được layout với một số thành phần quan trọng của trang web sẵn.

Cơ bản về Tailwind CSS

Utility First

Với Tailwind CSS thì những class dựng sẵn kiểu như .btn của Bootstrap là hoàn toàn không tồn tại. Ngược lại thì Tailwind CSS đã dựng sẵn các class, gọi là các class utility (tiện ích). Tailwind CSS đã dựng sẵn rất nhiều tiện ích liên quan đến những thứ như:

  • Flexbox và grid
  • kích thước (width, height), margin, padding
  • Màu sắc, background, border
  • font chữ, cỡ chữ

Dưới đây là một số so sánh nho nhỏ giữa việc sử dụng Tailwind CSS là việc không sử dụng (tự viết CSS):

Tự viết CSS

.sample {
    display: flex;
    flex-direction: column;
}

@media (min-width: 640px) {
    .sample {
        display: flex;
        flex-direction: row;
    }
}
<div class="sample">
  <div>a</div>
  <div>b</div>
</div>

Sử dụng Tailwind CSS

<div class="flex flex-col sm:flex-row">
  <div>a</div>
  <div>b</div>
</div>

Như vậy, Tailwind CSS đã dựng sẵn các class CSS (ví dụ .flex) và cả các class responsive và nhiều tiện ích khác (ví dụ .sm:font-lg, .hover:flex-row) mà chúng ta có thể sử dụng dễ dàng vào HTML và không cần viết một dòng CSS nào. Ngược lại, cách làm này lại không có chúng ta những class dựng sẵn theo kiểu của Bootstrap (đã dựng sẵn các thành phần), mà mọi thứ chúng ta bắt buộc phải tự dựng hoàn toàn bằng các class tiện ích này. Với những người lần đầu tiếp xúc thì cũng hơi khó khăn.

Ví dụ, với Bootstrap, thì một button chỉ cần viết thế này:

<button type="button" class="btn btn-primary">
    ...
</button>

thì với Tailwind CSS sẽ phải viết thế này:

<button type="button" class="outline-none bg-blue-500 hover:bg-blue-700 px-8 py-2 text-white">
    ...
</button>

Các class utility của Tailwind CSS rất nhiều và gần như đầy đủ cho mọi thuộc tính (thực ra thỉnh thoảng thì vẫn hơi thiếu). Các class được đặt tên rất khoa học nên việc sử dụng tương đối dễ dàng. Tuy nhiên, cá nhân mình thì không thể nhớ hết được tất cả các class, mà vừa làm vừa tra tài liệu, lâu dần thì cũng nhớ được một số class hay dùng.

Design system

Tailwind CSS còn dựng sẵn cho chúng ta khá nhiều tiện ích, trong đó phải kể đến như:

Các tiện ích về màu sắc, ví dụ:

  • text-red-100 (nhạt nhất)
  • text-red-200
  • text-red-300
  • text-red-400
  • text-red-500
  • text-red-600
  • text-red-700
  • text-red-800
  • text-red-900 (đậm nhất)

Tương tự như vậy, font size cũng được định nghĩa sẵn các class:

  • text-xs (nhỏ nhất)
  • text-sm
  • text-base (default)
  • text-lg
  • text-xl
  • text-2xl
  • v.v… (max hình như là 6xl)

Ngoài ra còn rất nhiều tiện ích về chiều cao, độ rộng, margin, padding, border, v.v… Việc của lập trình viên lúc này là chọn class nào phù hợp để sử dụng mà thôi, cái duy nhất cần phải maintain ở đây là HTML chứ không phải CSS.

Bằng việc thống nhất các class được sử dụng trong team, một team có thể xây dựng một bản thiết kế thống nhất và linh hoạt. Đó là điểm mạnh của Tailwind CSS. Tuy nhiên, không phải lúc nào các class utility của Tailwind CSS cũng đã đủ dùng (điển hình nhất là bảng màu của Tailwind CSS thường không đáp ứng được yêu cầu) và khi đó chúng ta cần customize lại công cụ này một chút.

Customize

Config

Trước hết, để customize Tailwind CSS chúng ta cần thay đổi config của công cụ này. Tài liệu của Tailwind CSS đã rất chi tiết, mời các bạn đọc để hiểu thêm.

Thay đổi config của Tailwind CSS có 2 kiểu: ghi đè và mở rộng. Ý kiến cá nhân của mình là không nên ghi đè, mà chỉ cần mở rộng là đủ (ví dụ bổ sung màu sắc, break point, v.v…). Không cần phải lo về việc sẽ nhiều class dư thừa không dùng tới, vì quá trình tối ưu cho production cũng sẽ loại bỏ những class này.

Ngoài ra, một công cụ khá hay là plugins cũng có thể được dùng nếu cần thiết. Với plugin, chúng ta có thể tự định nghĩa các class mà chúng ta muốn. Ví dụ điển hình nhất chắc là before, after vì hai pseudo này không được Tailwind CSS định nghĩa sẵn mà muốn dùng phải tự thêm. Lưu ý là chỉ khi chúng ta cần dựa trên Tailwind CSS để build các class mới cần dùng cách này, còn nếu chỉ đơn giản là các class riêng lẻ thì nên sử dụng directive (sẽ nói ở phần sau), hoặc đơn giản hơn là cứ viết trực tiếp vào file CSS nó cũng chạy bình thường.

Có vẻ là before, after sẽ được hỗ trợ sớm thôi (xem thêm ở đây). Nếu vậy thì Tailwind CSS đã gần như rất đầy đủ rồi, có thể yên tâm sử dụng mà không cần phải customize nhiều.

Functions & Directives

Tailwind CSS cho phép chúng ta sử dụng Functions & Directives để customize CSS. Tài liệu của Tailwind CSS đã viết rất chi tiết và đầy đủ, mời các đọc để hiểu thêm về phần này.

Lần này để làm lại blog này, ngoài @tailwind (để gọi các thành phần của Tailwind CSS) thì chỉ dùng đến @layer để thêm các class dùng cho pygments để highlight code mà thôi. Thực ra không dùng @layer cũng được, cứ viết thằng class như bình thường cũng chạy, nhưng đã dùng Tailwind CSS thì mình viết thêm tí trông cho nó pro 😛

Đập đi làm lại blog bằng Tailwind CSS

Sau khi hiểu được phần nào Tailwind CSS thì mình quyết tâm thực hành bằng cách viết lại UI của blog này bằng Tailwind CSS. Về công việc thì cũng đơn giản, do trước đây mình đã từng viết lại CSS một lần rồi nên vẫn còn chút kinh nghiệm. Lần này đơn giản là thay các thuộc tính đó bằng class utility tương ứng của Tailwind CSS mà thôi.

Việc cần chỉnh sửa là sửa lại các file template. Tuy nhiên mình cũng tiện thể refactor lại luôn. Trước đây mình đã chia file quá nhỏ (ví dụ footer thì chia thành 4-5 file thì nay gộp lại thành 1 file cho gọn). Đồng thời do nhiều thuộc tính không có sẵn trong Tailwind CSS trong khi không tìm được phương án customize mà tự viết CSS thì lìu tìu quá, nên mình đành phải dùng các phương pháp khác thay thế. Ví dụ như :before, :after thì chuyển sang dùng HTML tag cho nhanh.

Mình tiếp tục sử dụng SCSSPostCSS để làm việc với Tailwind CSS. Về cơ bản thì không gặp vấn đề gì trong việc sử dụng các công cụ này. Lúc đầu mình lo khi sass gặp mấy câu kiểu như @tailwind không hiểu sẽ báo lỗi thì hỏng việc, nhưng hoá ra gặp trường hợp này sass sẽ để nguyên, sau đó thì Tailwind CSS khi được gọi bởi PostCSS sẽ dịch nốt. Có điểm đang lưu ý là file CSS production lần này lớn hơn lần trước kha khá (lần trước chỉ hơn 10KB mà lần này hơn 16KB) nhưng đã bỏ đi được rất nhiều thư viện bên ngoài nên tổng thể là tốt hơn trước nhiều.

Sau khi đập đi làm lại (phần HTML) thì mạnh dạn thực hiện một số thay đổi như sau:

  • Bỏ việc sử dụng Isotope, do đó không cần sử dụng Imagesloaded nữa (mình thấy dùng event window.onload cũng ổn), và mình bỏ luôn việc sử dụng jQuery chuyển sang code JavaScript thuần 😎. Code JavaScript của blog trở nên rất đơn giản, file nguồn chưa đến 100 dòng, không cần dùng thư viện bên ngoài nào.
  • Layout chính của trang chuyển sang sử dụng flexbox và grid là chính, hầu như không sử dụng đến float. Một điểm khá hay là flexbox có thể kết hợp với order để sắp xếp thứ tự hiển thị của các thành phần vào 2 cột. Mặt trái của việc này là các trình duyệt cũ sẽ không còn tương thích nữa. Nhưng mà thôi, đọc blog này chắc chủ yếu là developer (hoặc có khi chả có ai 😪) nên chắc không phải lo chuyện update trình duyệt.
  • Về cơ bản thì Tailwind CSS không cần customize nhiều, chỉ cần customize màu đỏ (do màu mặc định quá xấu), và thêm các class để highlight code mà thôi (cái này thì Tailwind CSS không thể support được).
  • Font chữ mặc định của Tailwind CSS bị lỗi trên Windows 10 bản tiếng Nhật. Do đó mình customize font stack giống như Github. Mình không thích Google Fonts do không thích bị theo dõi cũng như tiếp tay cho việc theo dõi người khác.
  • Tích cực sử dụng flexbox và grid, đơn giản và hiệu quả hơn nhiều so với trước đây. Đặc biệt flexbox giúp cho việc căn chỉnh theo chiều ngang và chiều dọc dễ dàng hơn trước rất nhiều. Nhờ flexbox và grid và mình đã quyết định làm lại navbar theo hướng khác (giữ nguyên giao diện như cũ), đồng thời sửa lại phần phân trang cho đẹp hơn.
  • Bỏ việc sử dụng font-awesome, chuyển sang dùng icon dạng SVG. Nhờ đó mà không cần phụ thuộc vào tài nguyên bên ngoài, đồng thời giúp trang tải nhanh hơn khá khá, đồng thời không lo bị content blocker chặn 😁
  • Việc sử dụng Markdown khiến cho việc dùng Tailwind CSS với phần nội dung các bài viết tương đối khó khăn. Lúc đầu thì mình đặt class bao nội dung của mỗi bài viết rồi viết css cho từng thành phần như heading, các thẻ p, pre, code, ul, v.v… dùng @apply. Thế nhưng sau đó mình đã tìm hiểu và biết cách viết một plugin đơn giản cho Pelican để có thể customize quá trình tạo HTML và nhờ đó mà đưa các class của Tailwind CSS vào luôn, như vậy thì mình không cần customize Tailwind CSS quá nhiều.
  • Cũng nhờ quá trình làm lại blog này mà mình biết khá nhiều về cách tối ưu hiệu suất tải trang. Lần đầu tiên mình biết đến thuộc tính loading="lazy" dành cho ảnh. Tuy thuộc tính này chưa được hỗ trợ bởi tất cả các trình duyệt, nhưng thế cũng tạm được rồi vì các trình duyệt nhân Chromium hiện đang rất phổ biến.
  • Quyết định bỏ việc sử dụng Disqus để comment, bỏ luôn Twitter timeline vì cảm thấy mình đang tiếp tay cho việc theo dõi người dùng. Đặc biệt là Disqus giờ đã thu thập thông tin nhiều hơn trước đây rất nhiều.
  • Vietsub toàn bộ trang web, người Việt dùng tiếng Việt. Nửa Anh nửa Việt trông hơi dị.
  • Cuối cùng là đưa ảnh con Ki xuất hiện ở khắp nơi 🐱 để cho nó nổi tiếng.

Nhận xét

Tailwind CSS khác hoàn toàn với các framework CSS khác. Tuy nhiên, mình không cho rằng chúng là đối thủ cạnh tranh và sẽ thay thế lẫn nhau. Việc so sánh các framework với nhau là để hiểu hơn về các framework là chính, vì với bài toán này thì framework này phù hợp, bài toán khác thì framework khác lại tốt hơn. Điều quan trọng là phải tận dụng tối đa các đặc điểm của mỗi framework.

Tuy nhiên, cần phải nhấn mạnh rằng bản thân Tailwind CSS không liên quan gì đến việc “làm nên một thiết kế đẹp”. Nó là một công cụ để xây dựng UI cho một trang web, chứ nó không tạo ra UI của trang web đó.

Ưu điểm

Ưu điểm nổi trội của Tailwind CSS không thể không nhắc đến đó là: Bạn không cần viết 1 dòng css nào mà chỉ cần thêm class để tạo giao diện, bạn không cần phải đau đầu suy nghĩ tên class để đặt cho các div. Đúng như slogan ngay ở trang chủ của Tailwind CSS:

Rapidly build modern websites without ever leaving your HTML.

Ngoài ra, các tên class của tailwind rất khoa học, dễ hiểu và dễ sử dụng, mỗi class là một thuộc tính. Bạn cần thuộc tính gì thì dùng class tương ứng như thế.

TailwindCSS giúp cải thiện hiệu suất vì giảm thiểu các việc trùng lặp thuộc tính. Có nhiều plugin hỗ trợ loại bỏ các class thừa không sử dụng, giảm thiểu việc có mặt trong việc khai báo ở class HTML. Đồng thời giúp giảm chi phí maintain ứng dụng do không cần maintain CSS nhiều (chủ yếu maintain HTML là đủ).

Không phải lo lắng về việc sửa ở một chỗ này có thể chết ở chỗ khác do việc sử dụng cascading tạo ra. Bạn sửa HTML ở đâu thì nó chỉ có tác dụng ở đó. Còn nếu dùng Tailwind CSS mà vẫn lỗi chỗ này chỗ kia thì đành trách mình quá kém thôi.

Tài liệu chi tiết, đầy đủ bao gồm tất cả cách sử dụng chi tiết của class và chỉ dẫn nhiều cách tùy chỉnh khác nhau. Bạn có thể tìm hiểu tại đây.

Nhược điểm

Bên những ưu điểm trên thì nó cũng có những nhược điểm nhất định.

Đầu tiên là bạn phải là một người kha khá về CSS thì mới có thể sử dụng được. Như mình khi lần đầu làm với Tailwind CSS cũng rất vất vả, đó là mình đã có kinh nghiệm tự viết CSS cho blog này rồi đấy. Nếu chưa thì chắc bó tay luôn. Hơn nữa là sẽ mất nhiều thời gian cho những bạn mới bắt đầu vì chưa làm quen được hết các class của Tailwind CSS. Phải cần có thời gian để có thể nhớ về các quy tắc và cách thức viết cho đúng chuẩn.

Khi sử dụng Tailwind CSS thì bạn sẽ thấy là số lượng class cho mỗi tag là cực kì nhiều, số class sẽ tương ứng với số thuộc tính mà bạn muốn cài đặt. Ví dụ như dưới đây là thuộc tính cho tiêu đề của mỗi bài viết. Đó là chưa kể những chỗ phức tạp hơn như thanh navbar chẳng hạn.

<h1 class="mb-2 text-3xl leading-relaxed font-bold text-gray-900 uppercase">...</h1>

Vì nguyên nhân này nên code HTML sẽ dài hơn và khó ngắt dòng hơn. Như mình trước đây đã quyết tâm là ngắt dòng ở 80 ký tự thì nay không thể làm được nữa. Cố ngắt thì cũng được nhưng nó không đúng spec HTML lắm 🤣

Một điểm mình thấy khá khó hiểu là phần lớn các tiện ích (nhất là các tiện ích liên quan đến màu sắc) được xây dựng rất cồng kềnh. Ví dụ class sau:

.bg-gray-100 {
    --tw-bg-opacity: 1;
    background-color: rgba(243,244,246,var(--tw-bg-opacity));
}

Sao không viết thế này cho ngắn gọn:

.bg-gray-100 {
    background-color: #f3f4f6;
}

Một nhược điểm nữa là dù số class tiện ích rất nhiều nhưng Tailwind CSS cũng không thể nào đủ hết 100% thuộc tính CSS nên bạn cần phải config thêm khi muốn sử dụng. Mình đã tốn kha khá thời gian tìm hiểu về các pseudo before, after nhưng không thành công, và cuối cùng chọn giải pháp là dùng thẻ HTML khác để thay thế.

Nhược điểm cuối cùng mình thấy là Tailwind CSS không hỗ trợ tất cả trình duyệt (IE không được hỗ trợ gì luôn). Các class utility chỉ là thuộc tính CSS đơn thuần, không hề có thủ thuật hay polyfill gì để hỗ trợ các trình duyệt cũ. Với blog của mình thì không vấn đề gì nhưng nếu làm việc với khách hàng thì đây cũng là một điểm cần lưu ý.

Tôi xin lỗi nếu bài viết có bất kỳ typo nào. Nếu bạn nhận thấy điều gì bất thường, xin hãy cho tôi biết.

Nếu có bất điều gì muốn nói, bạn có thể liên hệ với tôi qua các mạng xã hội, tạo discussion hoặc report issue trên Github.