Tailwind: Cuộc bứt phá ngoạn mục trên con đường sử dụng Atomic CSS

Microvn3 tháng 5 • 12 min read • 

Đã rất lâu rồi mình mới quay lại để viết bài trên blog của mình, thành thử ra là có một núi bài nháp viết dở mà chưa có cơ hội xuất bản =)). Nhân tiện dạo gần đây mình có đọc về các bài về cơ học lượng tử thì chợt nhớ tới ở lập trình cũng có 1 trend mới về CSS liên quan tới chủ đề này và đó chính là "Atomic CSS".

Vậy "Atomic CSS" là gì?

Quay trở lại quá khứ một chút. Vào năm 2015, trước khi mình trở thành lập trình viên Full-Stack như bây giờ thì mình là một lập trình viên Front-End cho bộ phận phát triển sản phẩm của Kenh14.vn. Vào thời điểm đó, cứ vài ngày mình lại làm demo một sản phẩm để cho anh sếp đi trình sếp tổng vì thế mà cái demo của mình chứa CSS tùm lum như một mớ hỗn độn vậy. Với mỗi yêu cầu mới hay chỉnh sửa lại thiết kế mình phải custom lại CSS cho phù hợp làm sao nhanh nhất cho anh đi họp. Có những lần mình đã vừa code vừa khóc 😂 khi mà chỉ còn vài phút nữa để đi họp mà CSS vẫn đang nhảy tung tóe, giao diện vỡ tùm lum 😂 (được cái mấy anh quý chẳng mắng bao giờ hê hê).

Sau nhiều lần vừa code vừa khóc đó thì mình cũng nghĩ tới việc làm sao để chỉnh sửa nhanh nhất, ít phải viết lại CSS và đặc biệt quan trọng là tránh bị CSS specificity, specificity battle nhất có thể. Và thế là các class CSS độc lập ra đời chỉ để mình giải quyết mấy cái box lặp đi lặp lại khác nhau mỗi cái viền hoặc là sự điều chỉnh khoảng cách giữa các box khác nhau mà sự tinh mắt của anh sếp nhìn ra được mà mình thì chịu chết (từ sau giai đoạn này trở đi, phong cách code của mình đã trở nên tỉ mỉ hơn bao giờ hết). Dưới đây là một trong những gì mình đã từng viết vào những ngày tháng hào hùng chiến đấu tới sáng mới đi ngủ ấy 🥲. Đời không như mơ, công việc vả cho sấp mặt nên mình đã từ bỏ dự án này.

.fl {
    float: left;
}

.fr {
    float: right;
}

.mr-0 {
    margin-right: 0;
}

.mr-5 {
    margin-right: 5px;
}

.mr-10 {
    margin-right: 10px;
}

Trở lại vấn đề chính, Atomic CSS là một cách viết CSS mà ở đó chúng ta không quan tâm quá nhiều tới cách viết CSS giống kiểu truyền thông. Mà thay vào đó bạn sẽ sử dụng các class có sẵn được các framework Atomic CSS quy định (bạn có thể tự quy định nó nếu muốn). Các thuộc tính CSS sẽ được tạo thành các class riêng biệt và duy nhất, mình sẽ mô tả ở dưới.

Viết CSS theo cách truyền thống.

.btn {
    padding:10px 0;
    background:#fff;
}

.btn .child-of-btn{
    padding:10px 0;
}
CSS viết kiểu truyền thống
<button class="btn"><span class="child-of-btn">Click Here</span></button>
HTML truyền thống

Viết CSS theo kiểu Atomic CSS

.pt-10 {
    padding-top:10px;
}

.pb-10 {
    padding-bottom:10px;
}

.py-10{
    @apply pt-10 pb-10;
}

.bg-white{
    background:#fff;
}
Atomic CSS
<button class="pt-10 pb-10 bg-white"><span class="pt-10 pb-10">Click</span></button>

<button class="py-10 bg-white"><span class="pt-10 pb-10">Click Here</span></button>
HTML sau khi áp dụng Atomic CSS. Cả 2 cách viết này đều đúng

Nếu viết kiểu truyền thống thì điều gì sẽ xảy ra?

Như mình đã nói ở trên, khi sử dụng cách viết truyền thống CSS specificity là một thách thức lớn khi mà CSS cho phép kế thừa các thuộc tính dẫn đến việc chồng chéo, ghi đè các thuộc tính giống như một mớ bòng bong cho lập trình viên.

Cứ thử tưởng tượng, một dự án đã vài năm tuổi, trải qua vài đời dev khác nhau rồi mở CSS ra xem thì "Ối Dồi Ôi" luôn. Inline + Important! cứ phải gọi là hoảng loạn. =)))

Dĩ nhiên là cái gì khó là sẽ có người tìm giải pháp thôi. Ngoài cách viết truyền thống thì có thể sử dụng các quy tắc khác để giải quyết các vấn đề này như OOCSS, BEM, SMACSS (Modular CSS từ khóa cho các quy tắc này).

Ưu, nhược điểm của Atomic CSS

Ưu điểm

  1. Dựa vào framework, library sẽ không cần phải nghĩ tên cho các class, chỉ cần nhớ các quy ước và sử dụng nó.
  2. Cải thiện hiệu suất bằng việc giảm thiểu các việc trùng lặp thuộc tính.
  3. 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.
  4. Đồng nhất về mặt quy ước (bg-whitebackground:white; pt-10padding-top:10px). Tái sử dụng linh hoạt trong dự án có nhiều thuộc tính giống nhau.
  5. Có thể kết hợp nhiều giải pháp khác OOCSS, BEM, SMACSS để đạt kết quả mong đợi.
  6. Đủ thành thạo sẽ là lợi thế cho việc xây dựng các POC, MVP. Bằng không thì nó lại trở thành nhược điểm lớn 😆.
  7. Giống phong cách inline-style nhưng lại mạnh mẽ hơn về sự kế thừa. Không những vẫn sử dụng được media query, pseudo, keyframe animation mà còn tận dụng được cả bộ nhớ đệm của trình duyệt.
  8. 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.
  9. Cộng đồng sử dụng Tailwind đang phát triển nhanh chóng đồng nghĩa các lỗi mà bạn gặp phải sẽ nhanh chóng được gỡ lỗi và giải quyết. Nếu bạn giàu có bạn có thể mua gói trên TailwindUI để có các components xịn xò.

Nhược điểm

  1. Sử dụng số lượng lớn class nếu như đối tượng đó có nhiều CSS. Đối với nhưng class quan trọng hoặc có sử dụng selector thì vẫn cần đặt tên cụ thể.
  2. Đối với người mới cần một thời gian để nhớ về các quy tắc và cách thức viết cho đúng chuẩn mà được mọi người trong team chấp thuận.
  3. Một số thiết kế quá phức tạp thì vẫn cần sử dụng CSS thuần để viết thêm, mặc dù không quá nhiều.
  4. Mặc dù đang là trend nhưng phần lớn vẫn chưa sử dụng nó. Điều đó khiến cho việc sử dụng trong teamwork cần phải cân nhắc.

Framework/Library nào hỗ trợ Atomic CSS

Atomic CSS không định nghĩa các component giống như các Framework CSS khác như Bootstrap, UI-Kit, etc... Vì vậy ngay cả chính bạn cũng có thể tạo ra 1 thư viện Atomic CSS cho đội ngũ của bạn, miễn sao là nó được mọi người trong nhóm chấp thuận.

Vì thế có rất nhiều các Atomic CSS nhưng theo mình đánh giá có vài dự án triển vọng sau đây.

  • Acss: Có mặt từ 2015 bởi Yahoo. Theo đánh giá của mình thì phức tạp, rối mắt trong việc lập trình.
  • Tachyons: Được gợi ý bởi nhiều lập trình viên Front-End. Theo mình đánh giá khá nặng ngay cả bản tối thiểu, ít nhất là gấp đôi Tailwind mà mình đang dùng cho startup của mình ở đây.
  • Tailwind: Mình dùng được hơn 1 năm thì đánh giá cá nhân sau v2.0 là bước tiến lớn. Gia tăng rất nhiều tiện ích nhưng tên class vẫn hơi dài, chưa được tối ưu cho việc ghi nhớ, bù lại có 1 số cái khá hay như như @apply nhiều class làm một cho gọn.
  • WindiCSS: Có kiến trúc giống Tailwind nhưng cú pháp đơn giản và hỗ trợ nhiều hơn.
  • Basscss: Đánh giá cá nhân là em nay siêu nhẹ =)). Có thể cắm thêm Add-Ons vào ở đây. Ngoài ra, theo tìm hiểu thì được Google AMP khuyên dùng vì kích thước nó nhẹ. Nếu mà k cần sử dụng G-AMP thì mình sẽ không sử dụng do phải config loằng ngoằng.
  • quantum-css: Nghe tên thôi đã thấy căng não rồi nhưng mà rất tiếc là chủ dự án có vẻ lâu rồi không cập nhật hay có tối ưu nào mới.

Tailwind: Cuộc bứt phá ngoạn mục

Đầu tiên, phải kể tới Tailwind là gì?

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

Có mặt từ tháng 9/2017, dựa vào Atomic CSS/utility-first CSS thì Tailwind đã liên tục cải thiện và nhanh chóng đưa mình vào một trong những Framework mạnh mẽ trong những năm gần đây. Đặc biệt phải kể 1 số thành tích trên bảng xếp hạng của Tailwind tại đây.

Tailwind CSS: From Side-Project Byproduct to Multi-Million Dollar Business
The story of how a couple of failed SaaS ideas and a bunch of ugly Less stylesheets turned into my life’s work.
Câu chuyện về sự khởi đầu của Tailwind được CEO chia sẻ lại

10 phút dành cho trải nghiệm :))

Trước khi bắt đầu, mình xin chia sẻ là có nhiều cách để bạn có thể trải nghiệm Tailwind. Với cách đơn giản nhất là nhúng file CSS đã được compile tại đây như nhúng CSS bình thường và sử dụng nó. Trong bài viết này, mình muốn hướng dẫn các bạn chi tiết hơn về việc build lại nó từ đầu như thế nào.

Let's go, khởi tạo 1 dự án với

npm init -y
Khởi tạo dự án
npm install tailwindcss --save
Cài đặt Tailwind cho dự án
mkdir css
mkdir css/style.css
Tạo thư mục và file chứa CSS
@tailwind base;

@tailwind components;

@tailwind utilities;
Nội dung tệp style.css

Bạn đừng lo lắng, khi compile nó sẽ tự động tìm nạp và chèn các thành phần Tailwind vào đây.

npx tailwindcss init
# hoặc bản đầy đủ
npx tailwindcss init --full
Tạo tệp config Tailwind

Vì cấu hình đầy đủ rất dài, bạn có thể tham khảo ở đây

Đến đây vẫn chưa chạy được đâu 😂 Tailwind yêu cầu thêm một bộ tiền xử lý nữa. Ở đây mình sử dụng PostCSS.

npm install --save postcss-cli autoprefixer @fullhuman/postcss-purgecss cssnano
Cài đặt PostCSS và Plugin của nó Autoprefixer, CssNano, PureCSS

Nhiều bạn sẽ thắc mắc tại sao mình lại cài postcss-purgecss trong khi V2.0 Tailwind đã hỗ trợ pure. Đúng là hỗ trợ, nhưng nó chưa thực sự triệt để. Trong thời gian optimize cho startup của mình OkLabel.vn mình đã phát hiện ra Pure trong config của Tailwind thực sự chưa đạt như mình mong muốn, vì thế mình đã cài thêm postcss-purgecss.

vim postcss.config.js
Thêm cấu hình cho PostCSS
module.exports = {
    plugins: [
        require('tailwindcss'),
        require('autoprefixer'),
        require('cssnano'),
        require('@fullhuman/postcss-purgecss')({
            content: ['./build/*.html'] // List every file contains class here
        })
    ]
}
Dán vào và lưu lại file postcss.config.js
"build": "postcss css/styles.css -o build/styles.css"
Thêm vào scripts trong package.json để tiện sử dụng command
npm run build
Thử chạy command để xem đã hoạt động chưa nào.

Xem kết quả nào.

Tạo 1 file index.html để chạy thử xem sao.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body class="max-w-md mx-auto">
<figure class="md:flex bg-gray-100 rounded-xl p-8 md:p-0">
    <img class="w-32 h-32 md:w-48 md:h-auto md:rounded-none rounded-full mx-auto"
         src="https://tailwindcss.com/_next/static/media/sarah-dayan.a8ff3f1095a58085a82e3bb6aab12eb2.jpg" alt=""
         width="384" height="512">
    <div class="pt-6 md:p-8 text-center md:text-left space-y-4">
        <blockquote>
            <p class="text-lg font-semibold">
                “Tailwind CSS is the only framework that I've seen scale
                on large teams. It’s easy to customize, adapts to any design,
                and the build size is tiny.”
            </p>
        </blockquote>
        <figcaption class="font-medium">
            <div class="text-cyan-600">
                Sarah Dayan
            </div>
            <div class="text-gray-500">
                Staff Engineer, Algolia
            </div>
        </figcaption>
    </div>
</figure>
</body>
</html>
Mình copy luôn đoạn HTML trên trang chủ chạy thử
Tèn ten, xịn luôn.
File style.css sau khi build

Kinh nghiệm của bản thân

  1. Nên viết cho màn hình nhỏ nhất những thuộc tính mà tất cả đều có, hay nói cách khác là ưu tiên mobile-first. Sau đó tới các màn hình khác lớn hơn thì viết class để thêm hoặc sửa lại thuộc tính. Ví dụ: class="sm:inline md:flex" thì khi ở màn hình small nó sẽ hiển thị xuyên suốt cho tới khi gặp màn hình medium nó mới chuyển thành flex
  2. Hãy tận dụng tối đa sức mạnh của Plugin.
  3. Loại bỏ các cấu hình không cần thiết trong tailwind.config.js.
  4. Đối với các class có mức độ sử dụng lại lớn và độ dài khai báo class nhiều hãy sử dụng @apply.
    Điều này có thể khiến cho bundle size sẽ tăng tên đáng kể - đóng góp bởi @Thông Minh Lư.
  5. Luôn kiểm tra lại file build cuối cùng để đảm bảo nó không bị thừa so với những lần trước mình đã tối ưu.
  6. Tham khảo ở bên dưới 2 link cheat-sheetawesome-tailwindcss để có thêm kiến thức về Tailwind.

Tham khảo
https://itnext.io/yes-heres-the-best-css-framework-in-2021-2c9eb2ced678
https://viblo.asia/p/dong-dem-mot-so-cach-to-chuc-css-bWrZn7jblxw
https://2020.stateofcss.com/en-US/technologies/css-frameworks
https://adamwathan.me/tailwindcss-from-side-project-byproduct-to-multi-mullion-dollar-business
https://github.com/aniftyco/awesome-tailwindcss
https://nerdcave.com/tailwind-cheat-sheet