Mở đầu — vì sao bài này quan trọng
Nếu bạn đã từng ngồi hàng giờ tự hỏi "tại sao cái box này lại to hơn mình tính?", "tại sao hai phần tử cứ dính sát vào nhau dù mình đã set margin?", hay "tại sao cái nút bấm bị tràn ra khỏi khung?" — thì 90% khả năng gốc rễ vấn đề nằm ở CSS Box Model. Đây không phải là một khái niệm trang trí. Nó là bộ luật vật lý của toàn bộ trang web. Mỗi phần tử HTML — từ một dòng chữ, một bức ảnh, đến cả một section khổng lồ — đều được trình duyệt vẽ ra dưới dạng một chiếc hộp hình chữ nhật. Hiểu chiếc hộp đó cấu tạo thế nào tức là bạn nắm được cách trình duyệt tính toán kích thước và khoảng cách của mọi thứ.
Tôi nói thẳng: bạn có thể học thuộc Flexbox, Grid, animation, nhưng nếu Box Model còn mơ hồ thì layout của bạn sẽ luôn có những lỗi "ma" mà bạn không giải thích nổi. Ngược lại, khi đã thật sự thấm Box Model, phần lớn bug layout trở nên hiển nhiên — bạn nhìn vào là biết ngay đang thừa padding hay margin ở đâu. Bài này chúng ta sẽ mổ xẻ bốn lớp của chiếc hộp, hiểu sự khác nhau sống còn giữa padding và margin, và nắm vững thuộc tính box-sizing — thứ mà mọi dev chuyên nghiệp đều cấu hình ngay từ dòng CSS đầu tiên.
Khái niệm cốt lõi
Bốn lớp của chiếc hộp
Mỗi phần tử là một chiếc hộp gồm bốn lớp lồng vào nhau, tính từ trong ra ngoài:
- Content (Nội dung) — phần lõi chứa text, hình ảnh, hoặc các phần tử con. Kích thước của nó được điều khiển bởi
widthvàheight. - Padding (Đệm trong) — khoảng trống bên trong hộp, nằm giữa content và border. Padding đẩy nội dung ra xa khỏi viền. Quan trọng: padding mang theo màu nền (
background) của phần tử. - Border (Viền) — đường viền bao quanh padding và content. Có độ dày (
border-width), kiểu (border-style) và màu (border-color). - Margin (Lề ngoài) — khoảng trống bên ngoài hộp, đẩy phần tử này ra xa các phần tử khác. Margin luôn trong suốt, không bao giờ có màu nền.
Phân biệt sống còn: padding vs margin
Đây là điểm khiến người mới nhầm nhiều nhất. Hai thuộc tính trông giống nhau (đều tạo khoảng cách) nhưng bản chất khác hẳn:
- Padding tạo không gian bên trong — nó làm hộp to ra và đẩy nội dung vào trong. Dùng padding khi bạn muốn "thở" giữa chữ và viền. Ví dụ: một nút bấm cần padding để chữ không dính sát mép nút.
- Margin tạo không gian bên ngoài — nó tách phần tử khỏi hàng xóm. Dùng margin khi bạn muốn khoảng cách giữa các phần tử với nhau. Ví dụ: khoảng cách giữa hai thẻ card trong một danh sách.
box-sizing: thủ phạm của hầu hết lỗi kích thước
Mặc định, trình duyệt dùng box-sizing: content-box. Nghĩa là width bạn khai báo chỉ tính cho content, còn padding và border được cộng thêm vào. Hãy xem phép tính:
.box {
width: 300px;
padding: 20px;
border: 5px solid #333;
}
Với content-box, chiều rộng thật mà box chiếm trên màn hình là:
300 (content) + 20 + 20 (padding trái/phải) + 5 + 5 (border trái/phải) = 350px.
Bạn khai 300px nhưng thực tế nó ngốn 350px. Đây chính là lý do layout hay bị "vỡ" khi bạn thêm padding.
Giải pháp là box-sizing: border-box. Với chế độ này, width bao gồm luôn cả padding và border. Khai 300px thì box rộng đúng 300px, và content tự co lại còn 300 - 40 - 10 = 250px. Dễ đoán hơn rất nhiều. Đây là lý do gần như mọi dự án hiện đại đều bắt đầu bằng:
, ::before, *::after {
box-sizing: border-box;
}
Cú pháp shorthand (viết tắt)
Padding và margin đều hỗ trợ viết tắt theo chiều kim đồng hồ, bắt đầu từ trên:
margin: 10px; / cả 4 cạnh = 10px /
margin: 10px 20px; / trên-dưới = 10px, trái-phải = 20px /
margin: 10px 20px 30px; / trên=10, trái-phải=20, dưới=30 /
margin: 10px 20px 30px 40px; / trên, phải, dưới, trái (TRPL — clockwise) /
Nhớ thứ tự TRPL = Top, Right, Bottom, Left (đọc xuôi kim đồng hồ). Padding dùng y hệt.
Margin collapse — hiện tượng dễ gây hoang mang
Có một quy tắc đặc biệt: khi hai phần tử nằm chồng theo chiều dọc, margin-bottom của phần trên và margin-top của phần dưới không cộng lại mà chập vào nhau, lấy giá trị lớn hơn. Nếu thẻ trên có margin-bottom: 30px và thẻ dưới có margin-top: 20px, khoảng cách thật giữa chúng là 30px (không phải 50px). Hiện tượng này gọi là margin collapsing, chỉ xảy ra với margin theo chiều dọc, và là nguyên nhân khiến nhiều người tưởng CSS bị "lỗi".
Tình huống thực tế
Tình huống 1 — Nút "Đặt hàng" bị tràn ở Tiki
Giả sử một bạn fresher trong team frontend của một sàn TMĐT kiểu Tiki được giao làm nút "Thêm vào giỏ". Bạn ấy viết:
.btn-add {
width: 100%;
padding: 14px 24px;
border: 1px solid #1a94ff;
}
Trên màn hình desktop thì ổn, nhưng khi xem trên iPhone, nút bị tràn ra ngoài khung card sản phẩm khoảng 50px, đẩy cả layout lệch sang phải, xuất hiện thanh cuộn ngang xấu xí. Lý do? Card cha rộng đúng bằng màn hình, nút đặt width: 100% (bằng card), cộng thêm 24px + 24px padding và 1px + 1px border vì project chưa set border-box. Tổng cộng nút rộng hơn card đúng 50px.
Bài học: chỉ cần thêm box-sizing: border-box ở reset đầu trang, width: 100% sẽ bao gồm luôn padding và border, nút vừa khít trong card. Đây là lỗi kinh điển và là lý do dòng reset box-sizing được coi là "bắt buộc" ở mọi codebase nghiêm túc.
Tình huống 2 — Khoảng cách "đôi" giữa các section ở landing page Shopee
Một designer chuyển sang code làm landing page khuyến mãi cho một chiến dịch kiểu "Shopee 6.6". Mỗi section (banner, danh mục, flash sale) bạn ấy đặt margin-top: 60px và margin-bottom: 60px cho chắc. Bạn ấy kỳ vọng khoảng cách giữa hai section là 120px, nhưng đo trên trình duyệt chỉ thấy 60px. Bạn ấy hoảng, tưởng CSS không ăn, thử thêm !important, vẫn 60px.
Thủ phạm là margin collapse: margin-bottom: 60px của section trên chập với margin-top: 60px của section dưới, lấy giá trị lớn hơn — vẫn là 60px.
Bài học: đừng "double margin" theo bản năng. Quy ước chuyên nghiệp là chỉ đặt margin theo một chiều (thường là margin-bottom) cho các phần tử xếp dọc, hoặc dùng gap của Flexbox/Grid để tránh hoàn toàn chuyện collapse. Hiểu collapse giúp bạn không hoảng loạn và không thêm những dòng CSS thừa.
Tình huống 3 — Vùng bấm quá nhỏ trên app đặt đồ ăn
Một startup giao đồ ăn ở TP.HCM nhận phản hồi rằng người dùng lớn tuổi hay bấm trượt nút "Gọi lại tài xế". Icon điện thoại chỉ 24x24px, dev đặt thêm margin: 8px xung quanh để "tạo khoảng cách". Vấn đề: margin tạo khoảng trống bên ngoài, không mở rộng vùng có thể bấm — vùng chạm vẫn chỉ 24x24px, quá nhỏ so với khuyến nghị tối thiểu 44x44px của Apple.
Khi sửa lại, dev thay margin: 8px bằng padding: 12px (và bỏ width/height cố định, để icon nằm giữa). Giờ tổng vùng bấm là 24 + 12 + 12 = 48px — vượt ngưỡng 44px, người dùng bấm chính xác hơn hẳn. Tỉ lệ bấm trượt giảm rõ rệt sau lần cập nhật đó.
Bài học: padding mở rộng vùng tương tác của phần tử (vì nó là một phần của hộp), còn margin thì không. Khi cần "vùng bấm lớn hơn", luôn nghĩ tới padding, không phải margin.
Hướng dẫn từng bước
Hãy cùng dựng một thẻ card sản phẩm chuẩn để thấy Box Model vận hành.
Bước 1 — Đặt nền tảng box-sizing. Luôn bắt đầu mọi file CSS bằng:
, ::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
Dòng này xóa margin/padding mặc định của trình duyệt và bật border-box toàn cục — kích thước từ giờ sẽ dễ đoán.
Bước 2 — Tạo content và border. Dựng khung card:
.card {
width: 280px;
border: 1px solid #e0e0e0;
border-radius: 8px;
}
Bước 3 — Thêm padding để nội dung "thở". Đẩy nội dung ra khỏi viền:
.card {
padding: 16px;
}
Nhờ border-box, card vẫn rộng đúng 280px; content tự co còn 280 - 32 = 248px.
Bước 4 — Dùng margin để tách các card. Tạo khoảng cách giữa các card trong danh sách:
.card {
margin-bottom: 24px;
}
Lưu ý: chỉ dùng margin-bottom, không double cả top lẫn bottom, để tránh collapse gây bối rối.
Bước 5 — Kiểm chứng bằng DevTools. Mở DevTools (F12), chọn tab Elements, click vào card, nhìn xuống panel Computed. Bạn sẽ thấy sơ đồ Box Model trực quan: hình chữ nhật nhiều lớp màu — xanh dương (content), xanh lá (padding), vàng (border), cam (margin) — kèm số đo từng cạnh. Đây là công cụ debug Box Model mạnh nhất; hãy tập thói quen mở nó mỗi khi khoảng cách "không đúng ý".
Lỗi thường gặp & mẹo
- Quên set
box-sizing: border-box. Đây là lỗi số một gây vỡ layout khi thêm padding. Luôn đặt reset ở đầu file. Nếu layout đột nhiên rộng quá, kiểm tra dòng này trước tiên. - Dùng margin khi cần padding (và ngược lại). Hỏi: "Mình muốn không gian bên trong hộp hay giữa các hộp?" Bên trong → padding. Giữa các hộp → margin.
- Hoảng vì margin collapse. Nhớ rằng margin dọc chập chứ không cộng. Tránh bằng cách dùng margin một chiều hoặc dùng
gap. - Dùng margin để mở rộng vùng bấm. Sai. Vùng bấm chỉ tính tới hết border. Muốn vùng chạm lớn hơn, dùng padding.
- Đặt
width: 100%rồi thêm padding mà không có border-box. Box sẽ tràn ra ngoài cha.border-boxgiải quyết triệt để. - Mẹo gỡ lỗi nhanh: thêm tạm
outline: 1px solid red;cho phần tử nghi vấn. Khác vớiborder,outlinekhông chiếm chỗ trong Box Model nên không làm xê dịch layout, giúp bạn "soi" ranh giới phần tử mà không gây tác dụng phụ.
Bài tập thực hành
- Tính tay. Cho
width: 200px; padding: 15px; border: 3px solid;. Tính chiều rộng thực tế của box trong hai trường hợpcontent-boxvàborder-box. (Đáp án content-box:200 + 30 + 6 = 236px; border-box:200px.)
- Dựng card hồ sơ. Tự code một thẻ "thông tin nhân viên" gồm avatar, tên, chức danh. Yêu cầu: card rộng
320pxluôn đúng320pxdù bạn thêm bao nhiêu padding; các card cách nhau20pxtheo chiều dọc; chữ cách viền card ít nhất16px.
- Tạo nút chuẩn vùng bấm. Làm một nút có vùng chạm tối thiểu
44pxchiều cao bằng padding (không dùngheightcố định). Kiểm tra bằng DevTools xem tổng chiều cao có đạt không.
- Thí nghiệm margin collapse. Xếp ba thẻ
<div>dọc, mỗi thẻmargin: 40px 0. Đo khoảng cách giữa chúng bằng DevTools và giải thích vì sao không phải80px.
- Soi trang thật. Mở một trang bạn hay dùng (Tiki, Shopee, Zalo web), bật DevTools, click vào nút hoặc card bất kỳ và đọc sơ đồ Box Model. Thử đoán trước số padding rồi đối chiếu với giá trị thật.
Tóm tắt
CSS Box Model là nền tảng của mọi layout web. Mỗi phần tử là một chiếc hộp bốn lớp: content (nội dung lõi), padding (không gian bên trong, đẩy nội dung khỏi viền, mang màu nền), border (đường viền), và margin (không gian bên ngoài, tách phần tử khỏi hàng xóm, luôn trong suốt). Phân biệt cốt lõi: padding là không gian bên trong hộp, margin là không gian giữa các hộp. Thuộc tính box-sizing: border-box khiến width bao gồm luôn padding và border, giúp kích thước dễ đoán — đây là dòng reset bắt buộc ở mọi dự án. Đừng quên margin dọc chập chứ không cộng (margin collapse), và padding mới là thứ mở rộng vùng bấm chứ không phải margin. Nắm vững bốn lớp này, phần lớn bug layout sẽ trở nên hiển nhiên — và DevTools chính là người bạn đồng hành giúp bạn nhìn thấy chiếc hộp bằng mắt thường.