rem và em qua một ví dụ lồng nhau
rem tính theo cỡ chữ gốc (phần tử <html>, mặc định 16px). em tính theo cỡ chữ hiện tại, mà với font-size thì nghĩa là cỡ chữ của phần tử cha. Lồng cùng một class ba lần là thấy ngay sự khác biệt:
Các kích thước được in ra là giá trị trình duyệt đã tính. Mỗi 1.25em nhân với phần tử cha: 20px, rồi 25px, rồi 31.25px. Mỗi 1.25rem chỉ nhìn vào phần tử gốc: 20px, 20px, 20px. Sự nhân dồn đó là lý do cỡ chữ bằng em dễ vượt tầm kiểm soát trong danh sách và menu lồng nhau, và cũng là lý do phần lớn trang web đặt cỡ chữ bằng rem.
Mỗi đơn vị tính theo gì
| Đơn vị | Tính theo | Giá trị mặc định của 1 đơn vị |
|---|---|---|
px | không gì cả (một CSS pixel) | 1px |
rem | cỡ chữ của phần tử gốc (html) | 16px |
em | cỡ chữ của phần tử; riêng với font-size là của phần tử cha | 16px ở cấp ngoài cùng |
% | tùy thuộc vào thuộc tính (xem bên dưới) | |
vw / vh | 1% chiều rộng / chiều cao của viewport | |
dvh, svh, lvh | 1% chiều cao viewport động, nhỏ hoặc lớn | |
vmin / vmax | 1% cạnh nhỏ hơn / lớn hơn của viewport | |
ch | chiều rộng của ký tự "0" trong font hiện tại | khoảng nửa em |
ex | chiều cao chữ x của font hiện tại | khoảng nửa em |
CSS pixel không phải pixel thiết bị: trên điện thoại có màn hình 3x, 1px rộng bằng 3 pixel thiết bị. Nó giữ kích thước nhìn thấy gần như nhau trên mọi màn hình, nên px vẫn ổn cho viền và đổ bóng.
em cho khoảng cách co giãn theo chữ
em không chỉ dành cho cỡ chữ. Trên padding, margin, width hay border-radius, nó nghĩa là "nhân với cỡ chữ của phần tử này". Nhờ vậy một class nút dùng được ở mọi kích thước:
Padding của mỗi nút theo chiều dọc bằng nửa cỡ chữ và theo chiều ngang bằng một cỡ chữ, nên ba nút trông như cùng một nút ở ba kích thước. Với padding bằng px, bạn sẽ cần một quy tắc padding riêng cho mỗi kích thước.
Lưu ý rằng nút mặc định không kế thừa cỡ chữ của trang (trình duyệt cho các thành phần biểu mẫu cỡ chữ riêng), nên nút ở giữa ở đây là 13.33px trên Chrome, không phải 16px. Thêm font: inherit cho nút và ô nhập liệu khi bạn muốn chúng khớp với trang.
Phần trăm: tùy vào thuộc tính
% không có một nghĩa duy nhất. Mỗi thuộc tính tự quy định nó là phần trăm của cái gì:
| Thuộc tính | % tính theo |
|---|---|
width, max-width, left, right | chiều rộng của phần tử cha (khối chứa) |
height, top, bottom | chiều cao của phần tử cha, và chỉ khi phần tử cha có chiều cao được đặt sẵn |
padding, margin (cả bốn phía) | chiều rộng của phần tử cha |
font-size | cỡ chữ của phần tử cha (150% = 1.5em) |
line-height | cỡ chữ của chính phần tử |
transform: translate() | kích thước của chính phần tử |
width: 50% là 200px, một nửa phần tử cha. padding-top: 5% là 20px: 5% chiều rộng của phần tử cha, không phải chiều cao. Còn height: 50% không có tác dụng gì, vì phần tử cha không có chiều cao được đặt sẵn, nên phần tử con chỉ cao bằng phần chữ cộng với padding đó. Trang width và height giải thích chi tiết quy tắc này.
vw, vh và thanh địa chỉ trên di động
1vw là 1% chiều rộng viewport và 1vh là 1% chiều cao. Chúng hữu ích cho các khu vực toàn màn hình và cho kích thước linh hoạt (thường đặt trong clamp()). Trên điện thoại, vh có một điểm cần lưu ý: thanh địa chỉ của trình duyệt trượt ra trượt vào khi bạn cuộn, và 100vh là chiều cao không tính thanh đó, nên một khối hero height: 100vh sẽ bị cắt ở phía dưới khi thanh địa chỉ hiện ra. Các đơn vị mới khắc phục điều này:
| Đơn vị | Chiều cao được dùng |
|---|---|
100vh | viewport lớn (các thanh đã ẩn) trên phần lớn trình duyệt di động |
100svh | viewport nhỏ (các thanh đang hiện): luôn vừa màn hình |
100lvh | viewport lớn, một cách tường minh |
100dvh | viewport động: thay đổi khi các thanh di chuyển |
.hero {
min-height: 100vh; /* fallback for older browsers */
min-height: 100svh; /* fits the screen even with the address bar visible */
}
vw cũng có điểm cần lưu ý riêng: 100vw bao gồm cả chiều rộng của thanh cuộn dọc khi thanh cuộn chiếm chỗ (như trên Windows), nên width: 100vw có thể rộng hơn trang và sinh ra thanh cuộn ngang. Hãy dùng width: 100% khi muốn "toàn bộ chiều rộng của phần tử cha".
ch cho độ dài dòng dễ đọc
ch là chiều rộng của ký tự "0", nên max-width: 65ch giới hạn một đoạn văn ở khoảng 65 ký tự mỗi dòng, độ dài đọc thoải mái, ở mọi cỡ chữ:
Nên dùng đơn vị nào
| Dùng cho | Đơn vị | Lý do |
|---|---|---|
| Cỡ chữ | rem | Nhất quán ở mọi nơi, tôn trọng cài đặt cỡ chữ của người đọc |
| Padding và khoảng cách trong component | rem hoặc em | em khi cần lớn lên theo chữ của chính component |
| Viền, đường kẻ mảnh, đổ bóng | px | Cần giữ mảnh ở mọi cỡ chữ |
| Chiều rộng bố cục | %, fr, max-width bằng rem hoặc ch | Theo khung chứa, giới hạn độ dài dòng |
| Khu vực toàn màn hình | svh / dvh (kèm vh dự phòng) | Xử lý thanh địa chỉ trên di động |
| Chữ co giãn | clamp() kết hợp rem và vw | Lớn lên theo màn hình trong một giới hạn |
Vì sao rem tôn trọng người đọc còn px thì không
Trình duyệt có cài đặt cỡ chữ mặc định, dành cho những người cần chữ to hơn. Cài đặt này thay đổi cỡ chữ gốc, nên mọi thứ dùng rem và em lớn lên theo. Còn font-size: 14px vẫn là 14px bất kể người đọc chọn gì. Thu phóng trình duyệt cũng phóng to px, nhưng cài đặt cỡ chữ mới là thứ nhiều người đọc dựa vào. Đây là lý do chính để đặt cỡ chữ bằng rem.
Một số trang đặt html { font-size: 62.5%; } để 1rem bằng 10px cho dễ tính. Cách này vẫn tôn trọng cài đặt của người đọc (62,5% của cỡ chữ họ chọn), nhưng nhớ đặt font-size dễ đọc cho body, nếu không mọi phần tử không có cỡ chữ riêng sẽ hiện chữ 10px.
Lỗi thường gặp
- Cỡ chữ bằng
emtrong các phần tử lồng nhau. Chúng nhân dồn ở mỗi cấp. Dùngremcho cỡ chữ. height: 50%không có tác dụng. Phần tử cha cần có chiều cao được đặt sẵn thì chiều cao phần trăm mới hoạt động.- Tưởng
padding-top: 10%tính theo chiều cao phần tử cha. Phần trăm của padding và margin theo chiều dọc dùng chiều rộng của phần tử cha. width: 100vwsinh ra thanh cuộn ngang. Nó bao gồm cả thanh cuộn. Dùng100%.height: 100vhbị cắt trên điện thoại. Dùng100svhhoặc100dvh.- Đặt
html { font-size: 12px }. Giá trị px trên phần tử gốc ghi đè cài đặt cỡ chữ của người đọc cho cả trang. Đừng động vào phần tử gốc, hoặc dùng phần trăm.
Câu hỏi thường gặp
rem và em khác nhau thế nào?
rem tính theo cỡ chữ của phần tử gốc (<html>), nên 1.5rem có cùng kích thước ở mọi nơi trên trang. em tính theo cỡ chữ của chính phần tử đó (với font-size thì là của phần tử cha), nên nó bị nhân dồn khi các phần tử lồng nhau.
1rem bằng bao nhiêu pixel?
Mặc định là 16px, vì trình duyệt đặt cỡ chữ gốc là 16px. Nếu người dùng tăng cỡ chữ mặc định trong cài đặt trình duyệt, hoặc bạn đặt html { font-size: ... }, 1rem sẽ thay đổi theo.
Nên dùng rem hay px cho cỡ chữ?
Dùng rem cho cỡ chữ. Nó tôn trọng cỡ chữ mặc định mà người đọc đặt trong trình duyệt, điều mà px bỏ qua. Thu phóng trình duyệt thì có tác dụng với cả hai, nhưng không phải người đọc nào cũng dùng thu phóng.
Khi nào nên dùng em thay vì rem?
Dùng em cho khoảng cách cần co giãn theo chữ của chính phần tử, như padding của nút hay kích thước icon cạnh chữ. Khi đó cùng một class trông vẫn đúng ở mọi cỡ chữ. Dùng rem cho cỡ chữ và khoảng cách ở cấp trang, để không có gì bị nhân dồn.
100vh và 100dvh khác nhau thế nào?
Trên trình duyệt di động, 100vh là chiều cao khi thanh địa chỉ đã ẩn, nên một khu vực 100vh sẽ cao hơn màn hình nhìn thấy khi thanh đó đang hiện. 100dvh (dynamic viewport height) thay đổi theo thanh địa chỉ khi nó hiện ra và ẩn đi, còn 100svh luôn là chiều cao nhỏ hơn.