nicetool.dev logo
#css#accessibility#frontend

px hay rem trong CSS: Khi nào dùng đơn vị nào?

rem tôn trọng cỡ chữ người dùng chọn, px thì không. Tìm hiểu điều thực sự khác nhau giữa hai đơn vị, vì sao zoom chưa phải toàn bộ câu chuyện, và quy tắc thực tế cho font, khoảng cách, viền và media query.

6 phút đọc

Lập trình viên frontend nào cũng từng nghe lời khuyên "dùng rem, đừng dùng px". Nhưng ít người giải thích được dùng pixel thì hỏng ở đâu. Câu phản bác hay gặp là "zoom trình duyệt vẫn chạy tốt với px". Câu đó đúng, và cũng vì vậy mà tranh luận chưa bao giờ kết thúc. Bài viết này chỉ ra chính xác trường hợp hai đơn vị cư xử khác nhau, rồi đưa ra một quy tắc đơn giản áp dụng được cho mọi dự án.

Các đơn vị nghĩa là gì?

  • px là pixel CSS, một đơn vị tuyệt đối trong CSS: 16px luôn là 16 pixel CSS, bất kể người dùng cài đặt gì. (Pixel CSS không phải điểm ảnh vật lý. Điện thoại có device pixel ratio bằng 3 sẽ vẽ mỗi pixel CSS bằng 3×3 điểm ảnh thật.)
  • rem là viết tắt của "root em": bội số của font-size trên phần tử gốc <html>. Nếu bạn không đặt cỡ chữ này, giá trị lấy từ trình duyệt, mặc định là 16px.
  • em là bội số của cỡ chữ của chính phần tử đó (hoặc của phần tử cha, khi dùng ngay trong font-size).

Với cài đặt mặc định thì 1rem = 16px, 0.875rem = 14px và 1.5rem = 24px. Công thức đổi chỉ là một phép chia:

rem = px / cỡ chữ gốc

Zoom và cỡ chữ: trường hợp tạo ra khác biệt

Trình duyệt cho người dùng hai cách khác nhau để phóng to chữ.

1. Zoom trang (Ctrl/⌘ và +). Zoom phóng to mọi thứ: px, rem, hình ảnh, đường viền. Trong trường hợp này px và rem cư xử giống hệt nhau, và đó là lý do nhiều người tin hai đơn vị là tương đương.

2. Cài đặt cỡ chữ mặc định. Trong Chrome là Cài đặt → Giao diện → Cỡ chữ, Firefox, Safari và Edge cũng có tùy chọn tương tự. Trình duyệt di động và hệ điều hành cũng có cài đặt cỡ chữ riêng. Người thị lực kém thường thích cách này hơn zoom, vì chữ to lên mà bố cục không phình to tới mức phải cuộn ngang.

Cài đặt này đổi cỡ chữ mặc định của trình duyệt, ví dụ từ 16px lên 20px. Mọi thứ đặt bằng rem to thêm 25%. Mọi thứ đặt bằng px giữ nguyên.

Nếu CSS của bạn viết:

html { font-size: 16px; }   /* ghi đè lựa chọn của người dùng */
body { font-size: 14px; }   /* cũng bỏ qua lựa chọn đó */

thì người đã chọn chữ to vẫn nhận được đúng cỡ chữ nhỏ như cũ. Bạn đã âm thầm vô hiệu hóa một tính năng hỗ trợ tiếp cận. Còn với:

/* không đặt font-size cho html: giữ mặc định của trình duyệt */
body { font-size: 1rem; }

chữ sẽ thay đổi theo lựa chọn của người dùng.

Tiêu chí WCAG 1.4.4 (Resize Text) yêu cầu chữ phóng to được tới 200% mà không mất nội dung. Riêng zoom trang thường đã đáp ứng được tiêu chí này. Tôn trọng cài đặt cỡ chữ là làm đúng cho những người phụ thuộc vào nó, chứ không chỉ để qua một lần đánh giá.

Những gì nên dùng rem

Cỡ chữ: luôn dùng rem

Đây là phần quan trọng nhất. Dùng rem cho font-size ở mọi nơi, kể cả chữ nhỏ như chú thích và badge. Dùng giá trị không đơn vị cho line-height (line-height: 1.5) để nó co giãn theo cỡ chữ đang áp dụng.

Khoảng cách: thường là rem

Nếu chữ to lên mà padding xung quanh vẫn cố định bằng pixel, nút bấm và thẻ nội dung sẽ trông chật chội. Đặt padding, margin và gap bằng rem giúp giữ nguyên tỷ lệ khi cỡ chữ thay đổi. Tailwind CSS làm đúng như vậy: thang khoảng cách được định nghĩa bằng rem, nên p-4 là 1rem và text-sm là 0.875rem.

Media query: rem hoặc em

Bên trong media query, rem và em luôn tính theo cỡ chữ ban đầu của trình duyệt, không theo font-size bạn đặt cho html. Điều này rất có ích: @media (min-width: 48rem) mặc định là 768px, nhưng với người dùng đặt cỡ chữ 20px thì thành 960px. Người dùng chữ to vì vậy sẽ được dùng bố cục mobile rộng rãi sớm hơn, thay vì bố cục desktop với chữ bị ép trong các cột hẹp.

Typography co giãn: kết hợp rem với vw

font-size: 4vw không to lên khi zoom, vì zoom làm thay đổi chiều rộng viewport tính bằng pixel CSS. Luôn thêm một thành phần rem:

h1 { font-size: clamp(1.75rem, 1.2rem + 2vw, 3rem); }

Những gì nên giữ px

Có những thứ không nên co giãn theo cỡ chữ:

  • Đường viền và đường kẻ mảnh. Viền 1px nên luôn mảnh. Viền 0.0625rem sẽ thành đường 1.25px bị nhòe khi cỡ chữ mặc định là 20px.
  • Box shadow và outline chỉ có tác dụng trang trí.
  • Ảnh có kích thước cố định, ví dụ avatar có file đúng 64×64.
  • Những dịch chuyển nhỏ để căn chỉnh thị giác, như đẩy icon xuống 1px.

Một phép thử dễ nhớ: nếu người dùng tăng gấp đôi cỡ chữ, thứ này có nên to lên không? Nếu có, dùng rem. Nếu chỉ là trang trí, px là đủ.

Mẹo 62.5%: tiện nhưng có rủi ro

Một kiểu viết phổ biến giúp tính nhẩm dễ hơn:

html { font-size: 62.5%; }  /* 10px khi mặc định là 16px */
body { font-size: 1.6rem; } /* 16px */

Giờ 1.4rem = 14px, nhẩm rất nhanh. Vì dùng phần trăm nên cách này vẫn tôn trọng cài đặt của người dùng. Nhược điểm nằm ở thực tế:

  • Mọi component bên thứ ba giả định 1rem = 16px (date picker, widget nhúng, thư viện UI) bỗng hiển thị chỉ bằng 62.5% kích thước dự kiến.
  • Bạn phải nhớ đặt lại cỡ chữ cho body, nếu không chữ chưa được style sẽ chỉ còn 10px.
  • Các framework utility như Tailwind mặc định cỡ chữ gốc là 16px.

Ngày nay mẹo này hiếm khi đáng dùng. Editor và công cụ có thể đổi đơn vị giúp bạn. Hãy giữ cỡ chữ gốc mặc định, dùng công cụ chuyển đổi hoặc nhớ vài giá trị hay gặp.

Các giá trị nên nhớ

px rem (gốc 16)
12 0.75
14 0.875
16 1
18 1.125
20 1.25
24 1.5
32 2
48 3

Với các giá trị khác, công cụ Chuyển đổi px sang rem đổi được cả hai chiều, cho phép đặt cỡ chữ gốc tùy ý và đổi cả một đoạn CSS cùng lúc. Nếu muốn, công cụ giữ nguyên các viền 1px, rất tiện khi chuyển đổi stylesheet cũ.

Chuyển đổi một codebase có sẵn

Bạn không cần viết lại toàn bộ trong một lần:

  1. Bỏ mọi font-size bằng px trên html hoặc body. Chỉ thay đổi này đã giúp các component dùng rem tôn trọng cài đặt của người dùng ngay lập tức.
  2. Đổi cỡ chữ trước, từng file một. Đây là phần ảnh hưởng nhiều nhất tới khả năng tiếp cận.
  3. Đổi khoảng cách sau, bắt đầu từ các component dùng chung như nút, ô nhập và thẻ nội dung.
  4. Giữ viền và shadow bằng px.
  5. Kiểm tra với cỡ chữ mặc định 20px hoặc 24px trong cài đặt trình duyệt, không chỉ thử zoom. Hãy để ý chữ tràn ra khỏi các khung có chiều cao cố định, đây là lỗi hay gặp nhất sau khi chuyển đổi.

Tóm tắt

  • px và rem trông giống hệt nhau với cài đặt mặc định và khi zoom trang.
  • Chúng khác nhau khi người dùng đổi cỡ chữ mặc định: rem thay đổi theo, px thì bỏ qua.
  • Dùng rem cho cỡ chữ, khoảng cách và media query, px cho viền và chi tiết trang trí, và line-height không đơn vị.
  • Không bao giờ đặt cỡ chữ gốc bằng px.

Công sức bỏ thêm rất ít, nhưng với những người cần chữ to, nó quyết định trang web của bạn có dùng được hay không.

Tác giả

Nguyễn Thành Nam

Full-Stack Engineer · Backend & Platform

Kỹ sư full-stack với hơn 9 năm kinh nghiệm xây dựng API và nền tảng lưu lượng lớn bằng Go, NestJS, PHP (Laravel, Phalcon) và Node.js. Chuyên về thiết kế API GraphQL, hệ thống thời gian thực và tối ưu hiệu năng; xây dựng NICETOOL.dev cho cộng đồng lập trình viên.

Tự thử ngay

Bài viết liên quan