CSS Grid — The Complete Guide to Every Property (with an Interactive Demo)
A complete, practical CSS Grid reference: tracks, the fr unit, minmax & auto-fit, template-areas, alignment, item placement, and every container/item property — with a live interactive demo.
Why One More Grid Article {Vì sao lại thêm một bài về Grid}
Most Grid tutorials teach you four properties and stop {Phần lớn tutorial Grid dạy bạn bốn thuộc tính rồi dừng}. Then you hit a real layout — a dashboard, a holy-grail shell, a responsive gallery — and reach for fragile hacks {Rồi bạn gặp layout thật — dashboard, holy-grail shell, gallery responsive — và phải dùng hack mong manh}. This post is the opposite {Bài này thì ngược lại}: a complete map of every Grid property, grouped by where it lives (container vs item), each with the smallest example that makes it click {một bản đồ đầy đủ mọi thuộc tính Grid, nhóm theo nơi nó sống (container vs item), mỗi cái kèm ví dụ nhỏ nhất để hiểu ngay}.
Pair it with the live demo below — change a control, read the generated CSS {Hãy dùng kèm demo bên dưới — chỉnh control, đọc CSS sinh ra}.
The Live Demo {Demo trực tiếp}
Seven tabs, one per concept: track builder, the fr unit, auto-fit + minmax, grid-template-areas, alignment, item placement, and a full property reference {Bảy tab, mỗi tab một khái niệm: track builder, đơn vị fr, auto-fit + minmax, grid-template-areas, alignment, đặt vị trí item, và bảng tra thuộc tính đầy đủ}.
Open the full demo {Mở demo đầy đủ}: /tools/css-grid-complete-demo/.
The Mental Model {Mô hình tư duy}
Grid is two-dimensional {Grid là hai chiều}: you control rows and columns at the same time {bạn kiểm soát hàng và cột cùng lúc}. Three nouns are enough to reason about any grid {Ba danh từ là đủ để suy luận mọi grid}:
- Tracks — the columns and rows themselves {các cột và hàng}.
- Lines — the numbered dividers between tracks;
ntracks meansn + 1lines {các đường phân cách có đánh số;ntrack thì cón + 1line}. - Cells / Areas — a single track intersection, or a rectangle of cells you name {một giao điểm track, hoặc một hình chữ nhật gồm nhiều cell mà bạn đặt tên}.
Everything below is just sizing tracks, or placing items against lines/areas {Mọi thứ bên dưới chỉ là chỉnh kích thước track, hoặc đặt item theo line/area}.
.container {
display: grid; /* or inline-grid */
}
display: grid makes direct children grid items {display: grid biến các con trực tiếp thành grid item}. Text nodes and ::before/::after become items too {Text node và ::before/::after cũng thành item}.
Part 1 — Defining Tracks {Phần 1 — Định nghĩa track}
grid-template-columns and grid-template-rows declare the explicit grid {grid-template-columns và grid-template-rows khai báo grid explicit}.
.container {
display: grid;
grid-template-columns: 200px 1fr 200px; /* fixed · flexible · fixed */
grid-template-rows: 64px 1fr; /* header · body */
gap: 16px; /* row & column gutter */
}
A few sizing options you will actually use {Vài cách định kích thước bạn sẽ thật sự dùng}:
| Value {Giá trị} | Meaning {Ý nghĩa} |
|---|---|
200px, 10rem | Fixed track {Track cố định} |
auto | Sized to content (or leftover space) {Theo content (hoặc không gian còn lại)} |
1fr | A fraction of leftover free space {Một phần không gian còn lại} |
min-content | Smallest the content can be {Nhỏ nhất content có thể} |
max-content | Largest the content wants {Lớn nhất content muốn} |
minmax(a, b) | Clamp between a and b {Kẹp giữa a và b} |
fit-content(x) | max-content, capped at x {max-content nhưng giới hạn ở x} |
gap, row-gap, column-gap {khoảng cách}
.container {
gap: 16px; /* both axes */
gap: 12px 24px; /* row-gap column-gap */
column-gap: 24px; /* individually */
}
gap only adds space between tracks, never on the outer edges {gap chỉ thêm khoảng cách giữa các track, không bao giờ ở mép ngoài}.
Try it {Thử ngay}: open the demo’s Tracks tab and change columns, rows, gap, and item count {mở tab Tracks của demo và đổi columns, rows, gap, số item}.
Part 2 — The fr Unit {Phần 2 — Đơn vị fr}
fr distributes the leftover space after fixed tracks and gaps are subtracted {fr phân chia không gian còn lại sau khi trừ track cố định và gap}. This is the single most important thing to internalize {Đây là điều quan trọng nhất cần thấm}:
/* 120px is removed first; the remaining width splits 1 : 2 */
grid-template-columns: 120px 1fr 2fr;
fr is greedy but content-aware {fr tham lam nhưng vẫn để ý content}: a 1fr track will not shrink below its content’s min-content size unless you add minmax(0, 1fr) {một track 1fr sẽ không co nhỏ hơn min-content của content trừ khi bạn dùng minmax(0, 1fr)}.
/* The classic "why won't my grid item shrink?" fix */
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
This minmax(0, 1fr) trick is the cure for overflowing text or charts inside grid items {Mẹo minmax(0, 1fr) là cách chữa khi text hay chart tràn ra ngoài grid item}.
Try it {Thử ngay}: the fr unit tab lets you mix a fixed column with several
frtracks {tab fr unit cho bạn trộn một cột cố định với nhiều trackfr}.
Part 3 — Responsive Without Media Queries {Phần 3 — Responsive không cần media query}
The single most useful Grid pattern {Pattern Grid hữu ích nhất}:
.gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 1rem;
}
Read it right to left {Đọc từ phải sang trái}: each column is at least 220px, at most 1fr; the browser fits as many columns as the width allows {mỗi cột tối thiểu 220px, tối đa 1fr; trình duyệt nhồi nhiều cột nhất mà chiều rộng cho phép}. No breakpoints {Không breakpoint}.
auto-fit vs auto-fill {auto-fit và auto-fill}
The difference shows only when there are fewer items than columns {Khác biệt chỉ lộ ra khi item ít hơn số cột}:
auto-fit | auto-fill | |
|---|---|---|
| Empty tracks {Track rỗng} | Collapsed to 0 {Co về 0} | Kept at min size {Giữ ở kích thước min} |
| Visible effect {Hiệu ứng} | Items stretch to fill the row {Item giãn lấp đầy hàng} | Items keep their size; gaps remain {Item giữ size; còn khoảng trống} |
Try it {Thử ngay}: the auto-fit / minmax tab has a container-width slider so you can watch columns wrap {tab auto-fit / minmax có slider chiều rộng container để bạn xem cột wrap}.
Part 4 — grid-template-areas {Phần 4 — Vùng đặt tên}
Name regions in ASCII, then drop items into them {Đặt tên vùng bằng ASCII, rồi thả item vào}. This is the most readable way to build page shells {Đây là cách dễ đọc nhất để dựng page shell}:
.app {
display: grid;
grid-template-columns: 200px 1fr 200px;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header header"
"nav main aside"
"footer footer footer";
min-height: 100dvh;
}
.app > header { grid-area: header; }
.app > nav { grid-area: nav; }
.app > main { grid-area: main; }
.app > aside { grid-area: aside; }
.app > footer { grid-area: footer; }
Rules that trip people up {Vài luật hay khiến người ta vấp}:
- Every row string must have the same number of columns {Mỗi chuỗi hàng phải có cùng số cột}.
- A repeated name spanning cells must form a rectangle {Tên lặp lại để span phải tạo thành hình chữ nhật}.
- Use
.for an empty cell {Dùng.cho ô trống}. - Re-defining areas at a media query is how you reflow to mobile {Định nghĩa lại areas trong media query là cách reflow sang mobile}.
@media (max-width: 640px) {
.app {
grid-template-columns: 1fr;
grid-template-areas:
"header"
"main"
"nav"
"footer";
}
.app > aside { display: none; }
}
Try it {Thử ngay}: the template-areas tab switches between holy-grail, dashboard, and stacked layouts {tab template-areas chuyển giữa holy-grail, dashboard, và stacked}.
Part 5 — Alignment {Phần 5 — Canh chỉnh}
Grid has two alignment scopes, and mixing them up is the #1 source of confusion {Grid có hai phạm vi alignment, nhầm chúng là nguyên nhân bối rối số 1}:
*-items/*-self— align content inside each cell {canh content trong từng cell}.*-content— align the whole grid inside the container when the grid is smaller than it {canh cả lưới trong container khi lưới nhỏ hơn container}.
And two axes {Và hai trục}:
justify-*— the inline (row / horizontal in LTR) axis {trục inline (hàng / ngang trong LTR)}.align-*— the block (column / vertical) axis {trục block (cột / dọc)}.
.container {
/* content inside every cell */
justify-items: center; /* inline axis */
align-items: center; /* block axis */
place-items: center; /* shorthand: align-items / justify-items */
/* the whole grid within the container */
justify-content: space-between;
align-content: center;
place-content: center / space-between;
}
/* override one item */
.featured {
justify-self: end;
align-self: start;
place-self: start end; /* align-self / justify-self */
}
| Property {Thuộc tính} | Scope {Phạm vi} | Axis {Trục} |
|---|---|---|
justify-items / justify-self | Cell content / one item | Inline {Inline} |
align-items / align-self | Cell content / one item | Block {Block} |
justify-content | Whole grid | Inline {Inline} |
align-content | Whole grid | Block {Block} |
Try it {Thử ngay}: the Alignment tab exposes all four container properties at once {tab Alignment mở cả bốn thuộc tính container cùng lúc}.
Part 6 — Placing Items {Phần 6 — Đặt item}
By default items auto-flow into the next available cell {Mặc định item tự chảy vào cell trống kế tiếp}. Override placement with line numbers or span {Ghi đè vị trí bằng số line hoặc span}:
.hero {
grid-column: 1 / 3; /* from line 1 to line 3 → spans 2 columns */
grid-row: 1 / 2;
}
.full-bleed {
grid-column: 1 / -1; /* first line to LAST line — full width */
}
.wide {
grid-column: span 2; /* span 2 tracks from wherever it lands */
}
Line counting starts at 1 on the left/top; -1 is the last line {Đếm line bắt đầu từ 1 ở trái/trên; -1 là line cuối}. grid-area is the four-value shorthand {grid-area là shorthand bốn giá trị}:
/* grid-area: row-start / col-start / row-end / col-end */
.card { grid-area: 1 / 1 / 3 / 3; }
grid-auto-flow and implicit tracks {grid-auto-flow và track ngầm}
When items land outside the explicit grid, Grid creates implicit tracks {Khi item rơi ra ngoài grid explicit, Grid tạo track ngầm}, sized by grid-auto-rows / grid-auto-columns {kích thước theo grid-auto-rows / grid-auto-columns}:
.masonry-ish {
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: 120px; /* every implicit row is 120px */
grid-auto-flow: row dense; /* backfill holes left by spanning items */
}
dense backfills gaps left by larger items {dense lấp lại các lỗ hổng do item lớn để lại} — great for galleries, but it can reorder items visually away from DOM order, which hurts keyboard/tab flow {tuyệt cho gallery, nhưng có thể đảo thứ tự hình ảnh so với DOM, ảnh hưởng luồng keyboard/tab}.
Try it {Thử ngay}: the Item placement tab applies
grid-column/grid-row/grid-auto-flowto one highlighted item {tab Item placement ápgrid-column/grid-row/grid-auto-flowlên một item được tô sáng}.
Part 7 — Named Lines & subgrid {Phần 7 — Line đặt tên & subgrid}
You can name lines and reference them instead of numbers {Bạn có thể đặt tên line và tham chiếu thay vì số}:
.container {
grid-template-columns: [full-start] 1fr [content-start] minmax(0, 64rem) [content-end] 1fr [full-end];
}
.content { grid-column: content-start / content-end; }
.bleed { grid-column: full-start / full-end; }
repeat() with names yields auto-numbered lines like col 1, col 2 {repeat() kèm tên tạo line tự đánh số như col 1, col 2}:
grid-template-columns: repeat(3, [col] 1fr);
.item { grid-column: col 2 / span 1; }
subgrid lets a nested grid inherit its parent’s track lines so children align across components {subgrid cho grid lồng nhau kế thừa line track của cha để con căn thẳng hàng xuyên component}:
.card {
display: grid;
grid-template-rows: subgrid; /* adopt the parent's row tracks */
grid-row: span 3;
}
For deeper subgrid/masonry patterns, see {Để xem subgrid/masonry sâu hơn, đọc}: Advanced Grid — subgrid & masonry.
Every Property at a Glance {Toàn bộ thuộc tính trong một bảng}
On the container {Trên container}:
| Property {Thuộc tính} | Job {Nhiệm vụ} |
|---|---|
display: grid | inline-grid | Create the grid {Tạo grid} |
grid-template-columns / -rows | Size explicit tracks {Kích thước track explicit} |
grid-template-areas | Name regions {Đặt tên vùng} |
grid-template | Shorthand: rows / columns (+ areas) {Shorthand} |
gap / row-gap / column-gap | Gutters {Khoảng cách} |
grid-auto-columns / -rows | Size implicit tracks {Kích thước track ngầm} |
grid-auto-flow | Auto-placement direction + dense {Hướng tự đặt + dense} |
justify-items / align-items / place-items | Align cell content {Canh content trong cell} |
justify-content / align-content / place-content | Align the whole grid {Canh cả lưới} |
grid | Master shorthand {Shorthand tổng} |
On an item {Trên item}:
| Property {Thuộc tính} | Job {Nhiệm vụ} |
|---|---|
grid-column / grid-row | Place via lines / span {Đặt theo line / span} |
grid-area | Area name, or 4-line shorthand {Tên area, hoặc shorthand 4 line} |
justify-self / align-self / place-self | Align this one item {Canh riêng item này} |
order | Reorder within auto-flow {Đổi thứ tự trong auto-flow} |
The demo’s All properties tab is the same reference, kept handy next to live examples {Tab All properties trong demo là bảng tra y hệt, để cạnh ví dụ trực tiếp}.
Production Pitfalls {Cạm bẫy thực chiến}
- Overflow from
1fr{Tràn vì1fr}: a1frtrack respects content’smin-content. Useminmax(0, 1fr)to allow shrinking {Track1frtôn trọngmin-content. Dùngminmax(0, 1fr)để cho co}. gapis not margin {gapkhông phải margin}: no edge spacing; pad the container instead {không có khoảng cách mép; hãy pad container}.densebreaks tab order {densephá thứ tự tab}: visual order diverges from DOM order — avoid for interactive content {thứ tự hình ảnh lệch DOM — tránh với content tương tác}.orderis visual only {orderchỉ thị giác}: likedense, it does not move DOM focus order {giốngdense, không đổi thứ tự focus của DOM}.100%vs100dvhshells {shell100%vs100dvh}: usemin-height: 100dvh(notheight) so content can grow {dùngmin-height: 100dvh(không phảiheight) để content giãn được}.- Percent inside
fr{Percent trongfr}: avoid%tracks mixed withfr; they measure different things and fight {tránh trộn track%vớifr; chúng đo khác nhau và xung đột}.
Exercises {Bài tập}
Use the demo to check your answers {Dùng demo để kiểm tra đáp án}.
- Build a responsive card gallery where cards are at least
240pxand stretch to fill the row, with1.25remgaps — without any media query {Dựng gallery card responsive: card tối thiểu240px, giãn lấp đầy hàng, gap1.25rem— không media query}. - Create a holy-grail layout with
grid-template-areas, then make it stack to a single column under640pxand hide theaside{Tạo holy-grail bằnggrid-template-areas, rồi cho stack thành một cột dưới640pxvà ẩnaside}. - In a 4-column grid, make the first item a full-width hero using
grid-column: 1 / -1, then turn ongrid-auto-flow: denseand observe the reflow {Trong grid 4 cột, biến item đầu thành hero full-width bằnggrid-column: 1 / -1, rồi bậtgrid-auto-flow: densevà quan sát reflow}. - Stretch {Nâng cao}: center a single element both axes using only Grid, in two different ways (
place-itemsvsplace-contentvsmargin: auto) and explain when each differs {Căn giữa một phần tử cả hai trục chỉ bằng Grid, theo nhiều cách (place-itemsvsplace-contentvsmargin: auto) và giải thích khi nào chúng khác nhau}.
Solution sketch {Gợi ý lời giải}
/* 1 */
.gallery { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 1.25rem; }
/* 2 — see Part 4; swap grid-template-areas in the media query and display:none the aside */
/* 3 */
.grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); grid-auto-flow: dense; }
.grid > :first-child { grid-column: 1 / -1; }
/* 4 — any of these centers a single child */
.a { display: grid; place-items: center; } /* aligns the item in its cell */
.b { display: grid; place-content: center; } /* aligns the whole (1-cell) grid */
.c { display: grid; } .c > * { margin: auto; } /* auto margins eat free space */place-items centers the item inside its track; place-content centers the track block inside the container — identical for a single full-size cell, different once tracks are smaller than the container {place-items căn item trong track; place-content căn khối track trong container — giống nhau với một cell full-size, khác khi track nhỏ hơn container}.
Wrap-up {Tổng kết}
Grid is small {Grid nhỏ gọn}: a handful of container properties to size tracks, a handful of item properties to place things, and two alignment scopes {vài thuộc tính container để chỉnh track, vài thuộc tính item để đặt vị trí, và hai phạm vi alignment}. Once fr, minmax, auto-fit, and grid-template-areas are muscle memory, most layouts become a few honest lines of CSS {Khi fr, minmax, auto-fit, và grid-template-areas thành phản xạ, phần lớn layout chỉ còn vài dòng CSS trung thực}.
Keep the demo open while you build {Cứ mở demo trong lúc code}: /tools/css-grid-complete-demo/.