grid-template-areas cho dashboard admin | Named Grid layout thực chiến
Shell admin/SaaS dùng grid-template-areas — đặt tên vùng rồi gán grid-area, không nhảy số cột. Desktop: sidebar cố định + header/main/footer. Mobile: viết lại cùng tên areas (một cột), ẩn hoặc drawer sidebar. Lưới widget bên trong main dùng repeat(12, 1fr) + span — đừng nhét widget vào một ASCII map khổng lồ. Prototype cột/gap trên CSS Grid generator (chạy trình duyệt), rồi viết tay named areas.
Team frontend Việt Nam làm admin CRM, portal nội bộ, SaaS dashboard thường gặp cùng một PR: sidebar 240px, header sticky, main scroll, footer mỏng — rồi breakpoint tablet phá hết vì mọi thứ gắn grid-column: 1 / 3. grid-template-areas giải bài đó bằng sơ đồ đọc được: đổi layout = đổi chuỗi tên, không đụng markup.
Bài này đi sâu named areas cho shell trang. Nếu bạn đang chọn Flex vs Grid tổng quát, đọc thêm CSS Grid vs Flexbox.
Ai nên bookmark
- Dev dựng layout admin (sidebar + content) lần đầu hoặc đang refactor Flex lồng
- Freelancer / outsourcing nhận Figma “dashboard 3 vùng” và cần CSS ổn định qua breakpoint
- Reviewer muốn PR layout đọc được trong 30 giây (ASCII areas thay vì số magic)
Named areas là gì?
grid-template-areas khai báo bản đồ chữ nhật bằng chuỗi tên. Mỗi hàng trong template là một chuỗi; mỗi token là một ô. Phần tử con nhận grid-area: tên.
.admin {
display: grid;
grid-template-columns: 240px 1fr;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"side head"
"side main"
"side foot";
min-height: 100dvh;
}
.admin__side { grid-area: side; }
.admin__head { grid-area: head; }
.admin__main { grid-area: main; }
.admin__foot { grid-area: foot; }
Đọc template như wireframe: cột trái toàn side, cột phải head / main / foot. Designer đổi “sidebar rộng hơn” → sửa 240px; đổi “header full width trên sidebar” → viết lại areas — không đổi HTML.
Quy tắc cứng (sai là template vỡ)
- Mỗi hàng cùng số token —
"side head"và"side main foot"không khớp → invalid. - Vùng chữ nhật — tên
mainphải tạo hình chữ nhật liên tục; chữ L / lỗ thủng không hợp lệ. - Tên khớp
grid-area— typosidebarvsside= phần tử rơi khỏi vùng. .= ô trống — không gán vùng; hữu ích khi chừa khoảng có chủ đích.
| Tình huống | Ưu tiên | Lý do |
|---|---|---|
| Shell trang (side/header/main/footer) | Named areas | Breakpoint = viết lại chuỗi; PR dễ review |
| Lưới 12 cột widget / KPI | span cột | Widget thêm/bớt liên tục; areas map sẽ phình |
| Gallery đều cột | auto-fit + minmax | Không cần đặt tên từng card |
| Navbar một hàng | Flexbox | Một trục; xem bài so sánh Flex/Grid |
| Form label thẳng cột | Grid cột số / subgrid | Areas ít mang lại giá trị hơn track cố định |
Pattern shell admin chuẩn
Desktop: sidebar xuyên suốt
Pattern phổ biến nhất cho CRM / back-office:
.admin {
display: grid;
grid-template-columns: 16rem 1fr;
grid-template-rows: 3.5rem 1fr 2.5rem;
grid-template-areas:
"nav top"
"nav body"
"nav foot";
gap: 0; /* border nội bộ thay gap nếu cần hairline */
min-height: 100dvh;
}
.admin__nav { grid-area: nav; overflow: auto; }
.admin__top { grid-area: top; }
.admin__body { grid-area: body; overflow: auto; }
.admin__foot { grid-area: foot; }
Vì sao không Flex column + Flex row? Vì bạn đang căn hai chiều cùng lúc (sidebar cao bằng cả ba hàng). Flex thuần dễ sinh wrapper flex-col > flex-row > flex-col — đúng tạm, review sau 6 tháng khó đọc hơn ASCII areas.
Header full-bleed (sidebar dưới header)
Một số design: logo + search trên toàn rộng, nav chỉ dưới:
.admin--topnav {
grid-template-columns: 16rem 1fr;
grid-template-rows: 3.5rem 1fr;
grid-template-areas:
"top top"
"nav body";
}
Cùng bốn class HTML; chỉ đổi template. Đây là lý do named areas thắng grid-column: 1 / -1 rải rác khắp stylesheet.
Tablet / mobile: viết lại areas
@media (max-width: 900px) {
.admin {
grid-template-columns: 1fr;
grid-template-rows: auto auto 1fr auto;
grid-template-areas:
"top"
"nav"
"body"
"foot";
}
}
@media (max-width: 640px) {
.admin {
grid-template-areas:
"top"
"body"
"foot";
}
.admin__nav {
/* drawer: position fixed + transform — vẫn giữ grid-area khi mở */
position: fixed;
inset: 0 auto 0 0;
width: min(16rem, 85vw);
z-index: 40;
transform: translateX(-100%);
}
.admin.is-nav-open .admin__nav {
transform: translateX(0);
}
}
Nguyên tắc: tên vùng ổn định; chỉ chuỗi areas và track đổi theo breakpoint. HTML grid-area không đụng.
Main bên trong: widget ≠ shell areas
Sau khi shell ổn, vùng body thường là lưới KPI:
.dashboard {
display: grid;
grid-template-columns: repeat(12, minmax(0, 1fr));
gap: 1rem;
padding: 1rem;
}
.w-8 { grid-column: span 8; }
.w-4 { grid-column: span 4; }
.w-6 { grid-column: span 6; }
.w-12 { grid-column: span 12; }
@media (max-width: 900px) {
.w-8, .w-4, .w-6 { grid-column: span 6; }
}
@media (max-width: 640px) {
.w-8, .w-4, .w-6, .w-12 { grid-column: span 12; }
}
Ép toàn bộ widget vào một grid-template-areas kiểu:
"kpi kpi chart chart chart"
"table table table table table"
…sẽ vỡ ngay khi PO thêm widget thứ sáu giữa sprint. Areas cho khung trang; span cho nội dung động.
Ruột từng card (tiêu đề + badge + nút) → Flex. Pattern thắng: Grid shell → Grid 12 cột → Flex chrome.
Lỗi hay gặp (và cách đọc DevTools)
| Triệu chứng | Nguyên nhân | Cách xử |
|---|---|---|
| Layout nhảy / vùng trống lạ | Số token mỗi hàng không đều | Đếm token; format ASCII thẳng cột trong editor |
| Phần tử nằm góc trái | grid-area typo hoặc chưa gán | So khớp tên trong template và class |
| Invalid property value | Vùng không chữ nhật (chữ L) | Vẽ lại; tách thành hai vùng hoặc dùng span |
| Sidebar không cao full | Thiếu min-height / rows 1fr | min-height: 100dvh + row giữa 1fr |
| Content tràn ngang | 1fr + min-content con không co | minmax(0, 1fr) trên cột; overflow trên body |
| Mobile còn khoảng side trống | Đổi columns nhưng quên viết lại areas | Media query phải đổi cả hai |
Trong Chrome/Firefox: bật Grid overlay trên .admin — tên areas hiện trên track. Nếu overlay không khớp wireframe, sửa template trước khi đổ lỗi React/Vue.
minmax(0, 1fr): cột 1fr mặc định minmax(auto, 1fr) — bảng rộng trong main có thể kéo nở track. Admin hay “vỡ ngang” vì thiếu minmax(0, 1fr) hoặc overflow: auto trên vùng body.
Case study
1) Portal nội bộ outsourcing (React + CSS modules)
Yêu cầu: sidebar menu 8 mục, topbar user, main iframe báo cáo, footer version.
Team đầu dùng Flex: aside + div cột phải chứa header/main/footer. Khi design đổi “topbar full width”, phải tách DOM. Chuyển sang named areas — một PR CSS, HTML giữ bốn vùng. Tablet xếp nav thành hàng ngang dưới topbar bằng cách viết lại areas, không đụng component React.
2) SaaS analytics cho client Việt
Shell areas + dashboard 12 cột. Sprint 1: 4 KPI span 3. Sprint 3: thêm chart span 8 + list span 4. Areas shell không đổi một dòng; chỉ class span trên widget. Reviewer mới vào repo đọc grid-template-areas hiểu khung trang trong một phút.
3) Fail nhanh: areas map cả trang gồm 20 widget
Một freelancer copy “holy grail” rồi mở rộng ASCII cho mọi card. Thêm filter bar giữa header và KPI → phải sửa 4 media query. Refactor: tách .shell (areas) và .dashboard (12 cột) — thời gian debug layout giảm rõ ở các PR sau.
Quy trình prototype trên Kawa
Công cụ CSS Grid không sinh named areas — nó xuất repeat, gap, kích thước track. Dùng đúng việc:
- Chốt số cột shell (2) và gap (0 hoặc 1px hairline)
- Thử
1fr/auto/minmax(0,1fr)cho track main - Copy đoạn
display: grid; … gap…làm xương - Viết tay
grid-template-areastheo wireframe - Kiểm tra overlay DevTools trên viewport thật (390 / 768 / 1280)
Không cần đăng ký; chạy trên trình duyệt — phù hợp khi đang xem Figma nội bộ và không muốn upload layout lên service lạ.
🏁 Tạo CSS Grid ngay tại đây
Checklist trước merge:
- Mỗi hàng template cùng số token
- Mọi
grid-areakhớp tên trong areas - Mobile đã viết lại areas (không chỉ đổi
grid-template-columns) - Cột nội dung dùng
minmax(0, 1fr)nếu có bảng/pre rộng - Widget động không nằm trong ASCII areas của shell
- Đã xem Grid overlay ở breakpoint chính
Tóm tắt quyết định
| Câu hỏi | Trả lời ngắn |
|---|---|
| Shell admin 2D? | grid-template-areas |
| Đổi breakpoint? | Viết lại cùng tên areas |
| Widget thêm/bớt? | 12 cột + span trong main |
| Nav một hàng / nhóm nút? | Flex (không areas) |
| Chốt số cột/gap nhanh? | /vi/tools/css-grid/ rồi viết areas tay |
Named areas không “thay” Flex hay 12-cột — nó là ngôn ngữ đặt tên cho khung trang. Dashboard dễ bảo trì khi khung đọc như wireframe và nội dung động không bị nhét vào cùng một bản đồ ASCII.