Bỏ qua, sang nội dung chính
← Về danh sách

· Working ·

Lưới 4px và 8px: vì sao đúng lưới mà layout vẫn lệch

Lưới 4px hay 8px giúp mọi khoảng cách là bội số của một đơn vị, nhưng không tự tạo ra nhịp. Bài này giải thích vì sao lưới 8px/4px phổ biến, rồi sửa một layout đúng lưới mà vẫn rối bằng ba bậc khoảng trắng, có code CSS trước và sau.

Lưới 4px nghĩa là mọi khoảng cách đều là bội số của 4: 4, 8, 12, 16, 24. Lưới 8px dùng bước lớn hơn. Cả hai loại bỏ giá trị lẻ như 13px nhưng không tự tạo ra nhịp. Layout dễ đọc khi khoảng trắng có ít nhất ba bậc tách bạch: trong nhóm, giữa các nhóm, giữa các section.

Tôi là Hai Le, designer làm design system và tự code site này. Bài dưới dùng thang khoảng cách thật của site làm ví dụ.

Vì sao lưới 8px và 4px hay được dùng?

Một nguồn hay được nhắc tới là Material Design của Google. Trang Metrics & keylines trong bộ hướng dẫn Material bản đầu (vẫn còn ở m1.material.io) ghi: "All components align to an 8dp square baseline grid for mobile, tablet, and desktop." Cùng trang đó, icon trên thanh công cụ và chữ căn theo lưới 4dp. Nói cách khác, 8 là bước cho bố cục và component, 4 là bước cho chi tiết.

Lý do thực tế nằm ở phép nhân. Màn hình có mật độ điểm ảnh khác nhau, và hệ điều hành nhân kích thước thiết kế lên 1.5, 2 hoặc 3 lần. Một khoảng 8 nhân 1.5 ra 12 điểm ảnh tròn. Một khoảng 5 nhân 1.5 ra 7.5, và trình duyệt phải làm tròn, nên hai cạnh vốn bằng nhau có thể lệch nhau một điểm ảnh. Bội số của 4 nhân với các hệ số này đều ra số nguyên.

Lý do thứ hai là tốc độ ra quyết định. Khi chỉ có khoảng mười giá trị được phép, designer và dev ngừng tranh luận 14 hay 15. Tailwind cũng xây trên đơn vị này: trong Tailwind v4, biến --spacing mặc định là 0.25rem, tức 4px, nên gap-2 là 8px và p-6 là 24px.

Nên dùng lưới 4px hay 8px?

Tôi dùng 4px làm đơn vị gốc và 8px làm bước chính. Đây là thang khoảng cách của site này, lấy từ DESIGN.md:

TokenGiá trịVí dụ dùng
xxs2pxKhe rất nhỏ, hiếm khi dùng
xs4pxIcon cạnh chữ
sm8pxTiêu đề với đoạn dẫn ngay dưới
md12pxPadding ngang của nút nhỏ
lg16pxGap giữa thẻ trong hàng ba cột
xl20pxPadding bên trong thẻ
2xl24pxLề trang trên mobile
3xl32pxGap giữa các cột, lề trang desktop
4xl48pxPadding section trên mobile
5xl64pxKhoảng hàng trong lưới tính năng
6xl80pxPadding section từ md trở lên

Thang có 12 và 20, hai giá trị không chia hết cho 8. Chúng tồn tại vì có những chỗ 8 quá chật và 16 quá thưa, như padding của thẻ. Khi thêm bậc 20px, tôi phải đổi tên mọi token từ xl trở lên, và quy tắc đổi tên được ghi lại ngay trong file để không ai dùng nhầm tên cũ.

Vì sao layout đúng lưới mà vẫn trông lệch?

Vì lưới chỉ đảm bảo các con số chia hết cho 4. Nó không biết khoảng nào là "cùng nhóm" và khoảng nào là "ranh giới".

Lỗi tôi gặp nhiều nhất: khoảng cách giữa tiêu đề và đoạn dẫn bằng đúng khoảng cách giữa hai khối nội dung. Cả hai đều là 24px, cả hai đều đúng lưới. Nhưng mắt người nhóm các thứ nằm gần nhau, nên khi mọi khoảng đều bằng nhau, người đọc không còn thấy đâu là một nhóm. Tiêu đề trông như lơ lửng giữa hai khối.

Sửa một layout đúng lưới nhưng rối thế nào?

Đây là một section thường gặp: tiêu đề, đoạn dẫn, rồi một hàng thẻ. Mọi giá trị đều chia hết cho 4.

/* Trước: đúng lưới, không có thứ bậc */
.section       { padding-block: 24px; }
.section h2    { margin-bottom: 24px; }
.section .lead { margin-bottom: 24px; }
.cards         { display: grid; gap: 24px; }
.card          { padding: 24px; }
.card h3       { margin-bottom: 24px; }

Sáu chỗ dùng cùng một giá trị. Tiêu đề thẻ cách mô tả của nó xa bằng khoảng giữa hai thẻ, và section này dính vào section kế tiếp chỉ với 24px.

Bản sửa chia khoảng trắng thành ba bậc, đặt tên theo vai trò:

/* Sau: ba bậc, mỗi bậc cách bậc dưới ít nhất gấp đôi */
:root {
  --space-group: 8px;    /* trong một nhóm */
  --space-block: 32px;   /* giữa các nhóm */
  --space-section: 80px; /* giữa các section */
}

.section       { padding-block: var(--space-section); }
.section h2    { margin-bottom: var(--space-group); }
.section .lead { margin-bottom: var(--space-block); }
.cards         { display: grid; gap: 16px; }
.card          { padding: 20px; }
.card h3       { margin-bottom: var(--space-group); }

@media (max-width: 767px) {
  :root { --space-section: 48px; }
}

Ba thay đổi tạo ra khác biệt. Tiêu đề dính vào đoạn dẫn (8px) nên đọc như một khối. Đoạn dẫn cách hàng thẻ 32px, gấp bốn, nên rõ là sang nhóm mới. Section cách nhau 80px trên desktop và 48px trên mobile, đúng hai giá trị padding section mà site này dùng cho mọi section.

Quy tắc tôi tự đặt: mỗi bậc cách bậc ngay dưới ít nhất gấp đôi. Chênh lệch nhỏ hơn thế, như 16 với 24, thường không đủ để mắt phân biệt nhóm. Padding thẻ (20px) và gap giữa thẻ (16px) nằm ngoài ba bậc này. Chúng thuộc về component và giữ nguyên ở mọi chỗ thẻ xuất hiện.

Viết bằng Tailwind, bản "sau" gói trong vài class:

<section class="py-12 md:py-20">
  <h2 class="mb-2">…</h2>
  <p class="mb-8">…</p>
  <div class="grid gap-4 md:grid-cols-3">…</div>
</section>

Đưa thang khoảng cách vào design system thế nào?

Thang khoảng cách là một nhóm token, giống màu và chữ. Nếu bạn chưa quen khái niệm này, đọc bài Design token là gì trước, rồi xem các câu hỏi về đặt tên ở bài Câu hỏi thường gặp về token.

Trong Figma, tạo một collection Number cho spacing và gắn vào gap, padding của auto layout. Khi thang đã là variable, không ai gõ tay 13 được nữa. Phần khó hơn là ghi lại vai trò của từng bậc. "16px là gap giữa thẻ trong hàng ba cột" giúp người khác (và AI) chọn đúng giá trị khi gặp màn hình mới. Tôi ghi những quy tắc đó trong DESIGN.md, cách làm có trong bài DESIGN.md là gì.

Khoảng trắng và chữ đi cùng nhau. Line-height của tiêu đề tiếng Việt cần chỗ cho dấu chồng, chuyện tôi viết trong bài Font tiếng Việt cho UI.

Ở Joyme, tôi dựng design system cho phiên bản đầu của sản phẩm trong ba tháng, bạn có thể xem trong case study Joyme. Nếu team bạn cần người dựng thang token và giữ nó đồng bộ giữa Figma với code, xem thêm về tôi ở trang giới thiệu.

Nguồn

  • Material Design (bản 1): Metrics & keylines: lưới baseline 8dp cho component, 4dp cho icon trên thanh công cụ và chữ. Truy cập 2026-10-04.
  • Tailwind CSS v4.3, file theme.css trong package tailwindcss: --spacing: 0.25rem.
  • DESIGN.md (mục Layout) trong repo của site này.

Câu hỏi thường gặp

Nên dùng lưới 4px hay 8px?
Dùng 4px làm đơn vị gốc và 8px làm bước chính. Phần lớn khoảng cách nên rơi vào bội số của 8. Các bước 4px, 12px, 20px dành cho chi tiết nhỏ bên trong component như khoảng giữa icon và chữ, hoặc padding của nút.
Tailwind có dùng lưới 4px không?
Có. Trong Tailwind v4, biến --spacing mặc định là 0.25rem, tức 4px khi cỡ chữ gốc là 16px. Vì vậy p-1 là 4px, p-2 là 8px, p-5 là 20px.
Line-height có cần chia hết cho 4 không?
Material Design ghi rằng chữ căn theo lưới baseline 4dp. Với giao diện web, tôi ưu tiên chiều cao dòng thực tế gần bội số của 4 cho các style lớn, nhưng không bẻ line-height của chữ thân bài chỉ để chia hết. Ở site này, chữ thân 16px có line-height 22.4px.
Có cần lưới khi làm Figma bằng auto layout không?
Auto layout thay cho việc kéo thả căn lưới, nhưng bạn vẫn phải chọn giá trị gap và padding. Lưới 4px cho bạn danh sách giá trị được phép. Lưu chúng thành variable trong Figma để không ai gõ tay 13 hay 22.