CSS Grid bị tràn trên màn hình nhỏ: tìm nguyên nhân từ min-width và kích thước cột

6 giờ tới · 8 phút đọc
Tại sao 1fr 1fr vẫn tạo thanh cuộn ngang?
Bạn đặt grid-template-columns: 1fr 1fr và nghĩ lưới sẽ tự chia đôi chiều rộng màn hình. Trên desktop hoạt động tốt. Nhưng trên điện thoại, thanh cuộn ngang xuất hiện.
Vấn đề không nằm ở 1fr. Đơn vị fr (fraction) tính toán không gian còn lại sau khi trình duyệt đã phân bổ kích thước tối thiểu cho nội dung. Nếu một ô lưới chứa văn bản dài hoặc ảnh không có max-width, kích thước tối thiểu của ô đó có thể vượt quá 1fr tính được - và trình duyệt sẽ mở rộng ô đó thay vì cắt nội dung.
1fr chia phần dư, không phải chia toàn bộ chiều rộng
Cụ thể hơn: mỗi cột trong CSS Grid có thuộc tính min-width ngầm định là auto. Giá trị auto nghĩa là cột tối thiểu phải đủ rộng để chứa nội dung bên trong mà không bị vỡ dòng. Khi nội dung quá rộng (ảnh 400px, tiêu đề dài không có word-break...), min-width: auto khiến cột phình ra, đẩy tổng chiều rộng grid vượt viewport.
Đây là điểm khác biệt quan trọng: 1fr không phải là 1/2 của chiều rộng container. Nó là 1/2 của không gian khả dụng sau khi các ràng buộc kích thước tối thiểu đã được thỏa mãn.
Chẩn đoán lỗi: lần theo min-width từng bước
Trước khi sửa, cần xác định phần tử nào đang đẩy chiều rộng ra. Có ba công cụ nhanh:
1. DevTools - Elements panel
Mở Chrome DevTools, chuyển sang chế độ responsive (Ctrl+Shift+M), chọn kích thước 375px (iPhone SE). Dùng Inspector click vào từng ô grid để xem kích thước thực tế trong box model. Nếu ô rộng hơn 375px / 2 = 187.5px trong khi bạn kỳ vọng đó là giới hạn, thì có vấn đề.
2. CSS tạm thời để highlight
Thêm đoạn này vào stylesheet trong khi debug (bài tập minh họa, không dùng production):
Khi mọi phần tử đều có viền đỏ, bạn dễ thấy phần tử nào đang tràn ra ngoài container.
Viền đỏ tạm thời giúp thấy ngay phần tử nào tràn khỏi grid
3. overflow-x: hidden trên body - bẫy phổ biến
Nhiều người thêm overflow-x: hidden vào body hoặc html để ẩn thanh cuộn. Cách này che triệu chứng, không sửa nguyên nhân. Nội dung vẫn tràn, chỉ là người dùng không cuộn được tới nó. Tệ hơn: trên iOS Safari, overflow-x: hidden đôi khi không hoạt động đúng như mong đợi.
Sửa lỗi với minmax() và min-width: 0
minmax(0, 1fr) thay cho 1fr
minmax(min, max) cho phép đặt rõ giới hạn dưới cho kích thước cột. Khi bạn viết:
Trình duyệt hiểu: cột tối thiểu là 0px, tối đa là 1fr. Điều này ghi đè min-width: auto ngầm định - cột không còn bị ràng buộc phải chứa hết nội dung mà không co lại. Nội dung bên trong có thể bị cắt hoặc xuống dòng, nhưng layout không bị vỡ.
minmax(0, 1fr) cho phép cột co lại thay vì đẩy layout vỡ
min-width: 0 trên phần tử con
Cách thứ hai: giữ nguyên 1fr trên grid container, nhưng đặt min-width: 0 lên trực tiếp phần tử con của grid:
Kết quả tương đương. Lý do cách này hoạt động: min-width: 0 ghi đè min-width: auto của phần tử con, cho phép nó nhỏ hơn kích thước nội dung của mình (và nội dung sẽ bị overflow hoặc wrap thay vì đẩy phần tử ra ngoài).
Nên dùng cách nào? Cả hai đều hợp lệ. Nếu bạn kiểm soát CSS của grid container, dùng minmax(0, 1fr) vì nó tường minh hơn. Nếu grid container đến từ thư viện bên ngoài, dùng min-width: 0 trên con là lựa chọn ít xâm phạm hơn.
Bài tập: sửa card grid có tiêu đề dài và ảnh
Dưới đây là bài tập minh họa - đây là ví dụ giả định để luyện tập chẩn đoán và sửa lỗi.
Markup ban đầu (có lỗi)
Lỗi ở đây: chuỗi AAABBBCCCDDDEEE không có khoảng trắng nên min-width: auto của .card phải đủ rộng để hiển thị toàn bộ chuỗi đó. Ảnh không có max-width ràng buộc bổ sung (dù width: 100% hoạt động, ảnh vẫn có intrinsic size có thể ảnh hưởng trong một số trường hợp).
Chuỗi không có khoảng trắng buộc cột phải mở rộng
CSS đã sửa
Kiểm thử ở ba độ rộng
Sau khi sửa, mở DevTools và kiểm tra theo thứ tự:
| Độ rộng | Kỳ vọng | Cách kiểm tra |
|---|---|---|
| 1280px (desktop) | Hai cột đều nhau, nội dung hiển thị đầy đủ | Mắt thường |
| 768px (tablet) | Hai cột vẫn đều nhau, tiêu đề dài xuống dòng | DevTools responsive mode |
| 375px (mobile) | Không có thanh cuộn ngang, tiêu đề wrap | DevTools + kéo thanh cuộn để xác nhận không còn overflow |
Nếu ở 375px vẫn còn thanh cuộn: dùng bước chẩn đoán từ section trước - thêm outline: 1px solid red tạm thời để tìm phần tử còn tràn.
Những lỗi liên quan thường gặp
gap cộng vào kích thước cột không như kỳ vọng
Nếu bạn dùng grid-template-columns: 1fr 1fr và gap: 24px, tổng chiều rộng grid là (chiều rộng container) - 24px chia đều cho hai cột. gap không gây tràn vì nó được trừ ra trước khi tính fr. Tuy nhiên nếu bạn dùng padding hoặc margin bên trong cột thêm vào mà box-sizing chưa đặt, kích thước có thể bị cộng ra ngoài.
Đảm bảo bạn có:
Đây là reset phổ biến và thường có sẵn trong các framework CSS, nhưng nếu bạn đang viết CSS thuần từ đầu, dễ quên.
Ảnh không có height: auto
Thêm height: auto để ảnh giữ tỉ lệ khi co lại.
grid-template-columns cứng với px
Nếu viewport nhỏ hơn 400px + gap, lưới sẽ tràn. Dùng fr hoặc minmax() thay cho px cứng, hoặc kết hợp với media query để chuyển sang một cột:
Media query chuyển sang một cột khi viewport nhỏ hơn 480px
Muốn tìm hiểu sâu hơn về Grid và layout responsive, khóa HTML CSS từ Zero đến Hero của F8 có phần thực hành xây dựng layout thực tế, miễn phí.
Tóm tắt: trước và sau khi sửa
Bảng dưới so sánh trực tiếp hành vi của hai cách viết phổ biến (dữ liệu từ đặc tả CSS Grid và hành vi trình duyệt chuẩn):
| Thuộc tính | 1fr |
minmax(0, 1fr) |
|---|---|---|
min-width mặc định |
auto (theo nội dung) |
0 (có thể co về 0) |
| Cột bị tràn khi nội dung rộng? | Có | Không |
| Nội dung dài không có khoảng trắng | Đẩy cột ra ngoài | Overflow trong ô, có thể dùng overflow-wrap |
Ảnh không có max-width |
Có thể gây tràn | Ít bị ảnh hưởng hơn |
| Tương thích trình duyệt | Tốt | Tốt (CSS Grid Level 1) |
Các bước sửa lỗi theo thứ tự ưu tiên:
- Đổi
1frsangminmax(0, 1fr)trêngrid-template-columns - Thêm
overflow-wrap: break-wordcho text dài - Đảm bảo ảnh có
width: 100%; height: auto; max-width: 100% - Xác nhận
box-sizing: border-boxđã được đặt toàn cục - Kiểm thử ở 375px, 768px, 1280px
Năm bước, làm theo thứ tự từ trên xuống
Nguồn tham khảo:
- MDN: minmax() và fr unit - đặc tả
minmax()vàfr unit - web.dev - Learn CSS Grid - hướng dẫn Grid toàn diện từ web.dev
Kiểm tra tài liệu chính thức MDN và web.dev để xác nhận hành vi với phiên bản trình duyệt hiện tại của bạn.