- CSS Grid đã được các trình duyệt chính hỗ trợ rộng rãi từ tháng 3/2017, nhưng
grid-template-areas vẫn còn ít được dùng hơn, dù nó cho phép biểu diễn bố cục trực quan ngay trong CSS mà không cần tính số dòng
grid-template-areas hoạt động bằng cách sắp tên vùng dưới dạng chuỗi và liên kết các phần tử con bằng grid-area; các tên trùng nhau bắt buộc phải tạo thành một vùng hình chữ nhật
- Dòng lưới có tên là cách gắn tên cho các dòng như
content-start / content-end thay vì dùng số, và mẫu *-start với *-end có thể được trình duyệt tận dụng như vùng ngầm định
- Với lật thẻ, header responsive, bố cục editorial, bài đăng kiểu Threads, thẻ chồng lấp, và bố cục điều kiện bằng
:has(), có thể quản lý biến thể sắp xếp chỉ bằng cách đổi chuỗi vùng ở phần tử cha
- Chrome, Safari và Firefox đều cung cấp Grid area DevTools; riêng Firefox đặc biệt hữu ích nhờ hiển thị tên vùng rõ ràng và tự động gợi ý tên dòng khả dụng khi chỉnh
grid-area
Làm bố cục dễ đọc hơn với grid-template-areas
- Cách bố trí CSS Grid truyền thống thường dùng kiểu chỉ định trực tiếp số dòng như
grid-column: 1 / 3
- Số dòng không hiển thị cho tới khi kiểm tra trong DevTools, và với bố cục có cả cột lẫn hàng thì rất khó tính đúng số trong đầu
grid-template-areas gán tên cho từng ô để bố trí, nên bản thân mã CSS trông giống như bản đồ của bố cục
.page {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-areas: "item1 item2";
gap: 1rem;
}
.item-1 {
grid-area: item1;
}
.item-2 {
grid-area: item2;
}
- Các phần tử con được ánh xạ vào vùng có tên bằng
grid-area
- Trong ví dụ thẻ, có thể định nghĩa như
"featured featured article" để vùng featured chiếm 2 cột và vùng article chiếm 1 cột
Cú pháp và các quy tắc bắt buộc phải tuân thủ
grid-template-areas nhận một hoặc nhiều chuỗi vùng
- Một chuỗi tạo ra bố cục 1 chiều chỉ có cột
- Nhiều chuỗi tạo thành bố cục nhiều chiều có cả hàng và cột
- Khi dùng nhiều chuỗi, xuống dòng sẽ giúp bố cục dễ đọc hơn
.element {
display: grid;
grid-template-columns: 200px 1fr;
grid-template-areas:
"aside main"
"footer footer";
}
- Mỗi chuỗi đại diện cho một hàng của lưới, có thể hình dung như một bảng
- Các vùng được định nghĩa bằng cùng một tên phải là hình chữ nhật trong mã
/* invalid */
.layout {
grid-template-areas:
"header header"
"sidebar main"
"header footer";
}
/* valid */
.layout {
grid-template-areas:
"header header"
"sidebar main"
"sidebar footer";
}
- Mọi hàng phải có cùng số lượng token vùng; dù không dùng một vùng nào đó thì bản thân ô vẫn phải được định nghĩa
- Ô trống có thể biểu diễn bằng một dấu chấm hoặc nhiều dấu chấm
.element {
grid-template-areas:
"aside main"
"... footer";
}
- Dấu chấm là null cell token trong đặc tả CSS Grid, biểu thị một ô lưới trống
Quan hệ giữa dòng lưới có tên và vùng
- Trong CSS Grid, số lượng dòng luôn nhiều hơn số cột một đơn vị
- 3 cột thì sẽ có 4 dòng lưới
- Dòng lưới có tên là cách gắn tên riêng cho dòng thay vì dùng số mặc định
.layout {
grid-template-columns:
[full-start] 1fr
[content-start] 2fr
[content-end] 1fr [full-end];
}
.item {
grid-column: content-start / content-end;
}
- Dù đã đặt tên dòng, vẫn có thể tiếp tục dùng dòng số
- Kích thước track (
track size) là kích thước của cột hoặc hàng; các giá trị như 1fr 1fr chính là kích thước track
- Với dòng có tên, ta đặt tên cho các dòng bao quanh track
.layout {
display: grid;
grid-template-columns:
[aside-start] 200px [aside-end main-start] 1fr [main-end];
}
- Một dòng có thể mang nhiều tên; trong ví dụ trên,
aside-end và main-start là cùng một dòng
- Nếu định nghĩa tên như
aside-start và aside-end theo mẫu *-start / *-end, trình duyệt có thể cho phép dùng kiểu như grid-column: aside thông qua tên dòng ngầm định
- Không cần đặt tên cho mọi dòng; có thể chỉ đặt tên chọn lọc cho các dòng quan trọng
- Khi bố trí, có thể trộn số và tên như
grid-column: 1 / content-end
Quản lý biến thể bố cục bằng chuỗi vùng của phần tử cha
- Khi dùng
grid-template-areas, có thể giữ nguyên tên grid-area của các phần tử con và chỉ thay đổi chuỗi vùng của phần tử cha để đổi toàn bộ bố cục
.aside {
grid-area: aside;
}
.main {
grid-area: main;
}
.footer {
grid-area: footer;
}
- Giữ nguyên các tên
aside, main, footer và chỉ thay chuỗi grid-template-areas là toàn bộ cách sắp xếp sẽ tự cập nhật
- Việc điều khiển bố cục được tập trung hóa vào phần định nghĩa vùng của phần tử cha nên dễ điều chỉnh hơn
Lật hướng thẻ
- Với Flexbox, có thể dễ dàng lật hướng thẻ bằng
flex-direction: row-reverse
- Trong CSS Grid, nếu dùng số dòng thì khi đổi hướng phải sửa cả định nghĩa cột lẫn giá trị
grid-column của từng phần tử con
- Nếu dùng vùng có tên, chỉ cần gán ảnh và nội dung của thẻ vào các vùng rồi đổi thứ tự vùng ở phần tử cha
.card {
display: grid;
grid-template-columns: 150px 1fr;
grid-template-areas: "thumb content";
}
.card__thumb {
grid-area: thumb;
}
.card__content {
grid-area: content;
}
.card--flip {
grid-template-columns: 1fr 150px;
grid-template-areas: "content thumb";
}
- Cũng có thể biểu diễn cùng bố cục đó bằng thuộc tính rút gọn
grid-template
.card--flip {
grid-template: "content image" / 1fr 150px;
}
- Thuộc tính rút gọn là tùy chọn; cũng có quan điểm cho rằng dạng đầy đủ dễ đọc hơn
Bố cục header responsive
- Header có logo, navigation và vùng hành động ban đầu có thể được tạo bằng 3 cột bằng nhau
- Nếu dùng số dòng, phải chỉ định
grid-column cho từng .logo, .nav, .actions
- Nếu dùng tên vùng, chỉ cần ánh xạ mỗi phần tử con vào
logo, nav, actions, rồi thay grid-template-areas của phần tử cha theo từng viewport
.header {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-areas: "logo nav actions";
}
.logo {
grid-area: logo;
}
.nav {
grid-area: nav;
}
.actions {
grid-area: actions;
}
- Ở kích thước nhỏ, có thể đặt logo và actions trên dòng đầu, còn navigation chiếm toàn bộ chiều rộng
.header {
grid-template-areas:
"logo logo actions"
"nav nav nav";
}
- Từ
380px trở lên, có thể để logo bên trái và đặt navigation cùng actions ở bên phải
@media (min-width: 380px) {
.header {
grid-template-areas:
"logo nav nav"
"logo actions actions";
}
.nav {
justify-self: end;
}
}
- Từ
900px trở lên, có thể quay lại bố cục một hàng
@media (min-width: 900px) {
.header {
grid-template-areas: "logo nav actions";
}
}
- Chuỗi vùng hoạt động như một ánh xạ hiển thị trực tiếp cách UI được sắp xếp ngay trong CSS
Ví dụ bố cục editorial
- Với section gồm tiêu đề, văn bản và hình ảnh,
grid-template-areas giúp đổi bố cục theo kích thước viewport một cách dễ dàng
.section {
display: grid;
grid-template-areas:
". title title"
"thumb content content";
gap: 1rem;
}
- Dấu chấm bên trái tiêu đề là null cell token biểu thị khoảng trống
- Trong ví dụ section kiểu time.com, các vùng
feature, secondary, most-read được dùng cùng với các cột trống ở hai bên
.featured-section {
display: grid;
grid-template-areas:
". feature most-read ."
". secondary most-read .";
grid-template-columns: 1fr minmax(auto, 57.313rem) minmax(12rem, 18.75rem) 1fr;
grid-template-rows: minmax(auto, 25rem) 1fr;
}
- Các cột trống hai bên chủ yếu đóng vai trò tạo khoảng cách trái phải
- Trong bản responsive, trên màn hình nhỏ sẽ xếp
feature, secondary, most-read theo chiều dọc; từ 500px và 900px trở lên thì thay đổi định nghĩa cột và vùng
grid-template-rows có thể được thay thế bằng định nghĩa vùng nên không bắt buộc phải có
Component bài đăng kiểu Threads
- Khi xem lại CSS của Threads app by Meta, có thể thấy một trường hợp dùng CSS Grid cho component bài đăng
- Ví dụ gốc bố trí item bằng
grid-template-columns, grid-template-rows và số dòng
.postBody {
grid-column-start: 2;
grid-row-start: 2;
grid-row-end: span 3;
}
- Nhiều biến thể bài đăng có thể được biểu diễn dễ hơn bằng
grid-template-areas
.post {
grid-template-areas:
"avatar header"
"avatar body"
". body"
". footer";
}
.post--reply {
grid-template-rows: 40px max-content max-content;
grid-template-areas:
"avatar header"
"body body"
"body body"
"footer footer";
}
.post--nested {
grid-template-areas:
"avatar header"
"avatar body"
"line body"
"footer footer";
}
- Có thể phân biệt biến thể mặc định, trả lời và lồng nhau chỉ bằng cách nhìn vào chuỗi vùng
Item chồng lấp và tên vùng
- Trong
grid-template-areas, cùng một tên phải tạo thành hình chữ nhật, nên nếu lặp lại cùng tên theo hình không phải chữ nhật thì sẽ không hợp lệ
.layout {
display: grid;
grid-template-columns: 1fr 1fr fr;
grid-template-areas:
"card tag tag"
"title title card";
}
- Ví dụ trên là invalid vì
card không tạo thành hình chữ nhật
- Với thẻ chồng lấp, có thể định nghĩa tên vùng mới như
thumb-1, thumb-2 rồi dùng các tên đó trong grid-column và grid-row
.card {
display: grid;
grid-template-columns: auto auto 1fr;
grid-template-rows: auto auto;
grid-template-areas:
"thumb-1 tag"
"title thumb-2";
}
.thumb {
grid-column: thumb-1 / thumb-2;
grid-row: thumb-1 / thumb-2;
}
- Tag và title có thể lần lượt đặt vào
grid-area và căn chỉnh bằng align-self, justify-self
- Nếu cần, phải quản lý thứ tự chồng lớp của phần tử con, nhưng trong ví dụ này chỉ cần thứ tự nguồn là đủ
- Ví dụ chồng lấp này lấy cảm hứng từ trang Oddbird’s Cascading Layouts
:has() và bố cục điều kiện
- Khi kết hợp selector CSS
:has() với Grid area, có thể thay đổi bố cục tùy theo sự tồn tại của một phần tử cụ thể
- Có thể áp dụng định nghĩa vùng khác nhau cho trường hợp trong
<figure> có hoặc không có figcaption
figure {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-areas: "img img img";
gap: 0.5rem;
}
img {
grid-area: img;
}
figcaption {
grid-area: caption;
}
.card:has(figcaption) {
grid-template-areas:
"img img caption"
"img img .";
}
Hỗ trợ hướng đa ngôn ngữ LTR/RTL
- CSS Grid tự điều chỉnh bố cục theo hướng trang là LTR hay RTL
- Các vùng lưới có tên cũng đi theo hướng của trang, nên trong thiết lập RTL bố cục sẽ bị đảo lại
- Có thể xem tài liệu RTL Styling 101 để viết bố cục RTL
Khác biệt về hỗ trợ DevTools
- Chrome, Safari và Firefox đều cung cấp DevTools cho Grid area
- Chrome
- Tên vùng được đặt ở góc trên bên trái nên các vùng có thể trông bị chồng lên nhau
- Lớp phủ màu có thể làm thay đổi cách thiết kế được nhìn thấy
- Safari
- Viền hồng giúp phân biệt item rõ ràng hơn
- Có thể xem tên dòng
- Cách hiển thị tên dòng vẫn còn chỗ để cải thiện, chẳng hạn kiểu xếp chồng các dòng
- Firefox
- Tên vùng hiển thị rõ ràng hơn Chrome và Safari
- Cung cấp danh sách tự động hoàn thành khi nhập tên tùy chỉnh như
*-start, *-end
- Khi chỉnh
grid-area của item con, nó cung cấp danh sách tất cả tên dòng khả dụng
Tài liệu tham khảo
1 bình luận
Ý kiến trên Hacker News
Bản demo animation khá thú vị, nhưng hóa ra animation đó không được làm bằng CSS, nên hơi tiếc
Sau khi học Flexbox, tôi đã nghĩ “như vậy là đủ rồi”, và không học sâu về Grid. Có vẻ vẫn có lý do để ưu tiên Grid [0], nhưng nhìn chung tôi chưa từng cần làm việc gì chỉ Grid mới làm được, còn bảng dữ liệu thì tôi dùng
<table>thật sự[0] https://css-tricks.com/css-grid-replace-flexbox/
Tôi chỉ từng dùng Grid cho gallery ảnh responsive như https://labs.jensimmons.com/2016/examples/image-gallery-grid... hoặc các thẻ responsive như https://every-layout.dev/demos/grid-cards/.
grid-template-columnsđáng để học, còngrid-gapthì tiện. Ngoài ra thì cảm giác Flexbox xử lý được gần như mọi thứTuy nhiên
grid-template-areasthì tôi vẫn thấy hơi lửng lơ. Bài viết đưa ra lý do như dễ điều chỉnh layout theo từng breakpoint hoặc “khi thành viên trong nhóm cần thay đổi layout”, nhưng cũng có thể đơn giản là tôi không phải độc giả mục tiêuCứ dùng cả hai tùy nhu cầu; kết hợp lại thì chúng trở thành bộ công cụ layout rất mạnh
https://www.youtube.com/watch?v=MWWcZzossc8
Khoan đã,
1/3không phải là phân số mà là phạm vi từ 1 đến 3 à? Không hiểu sao lại chọn cú pháp như vậy. Trong lập trình có rất nhiều cú pháp hợp lý và quen thuộc để biểu diễn phạm vi, ví dụ như1..3Có vẻ khá nhiều vấn đề lập trình viên gặp với CSS bắt nguồn từ hiểu lầm này
!importantkhông có nghĩa là “không quan trọng”, mà là “quan trọng!”Tôi vẫn chưa nắm chắc cả Grid lẫn Flexbox. Chiến lược layout cuối cùng mà tôi còn xử lý tương đối tốt là float. Đọc bài này khiến tôi tự tin sẽ thử dùng CSS Grid khi thiết kế layout tiếp theo, và bài viết cũng được làm rất tốt
Có nhiều bài hay, nhưng chỉ đọc hai bài này của CSS-Tricks là tôi thấy đủ
https://css-tricks.com/snippets/css/a-guide-to-flexbox/
https://css-tricks.com/snippets/css/complete-guide-grid/
Ngoài các bài CSS-Tricks đã nêu, tôi cũng khuyên đọc hướng dẫn Flexbox của MDN[1] và tài liệu tham khảo “CSS flexible box layout”[2]. Chúng giúp hiểu sâu hơn các khái niệm, thuộc tính và shorthand property phía sau Flexbox thực sự hoạt động ra sao
[1]: https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layou...
[2]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_flexibl...
Tuy vậy Flexbox cũng gần với layout một chiều có thể xuống dòng, nên mẹo là lồng và xếp chồng các phần tử. Grid thì có template, các phần tử con trực tiếp chiếm những vùng đã định sẵn, nên tĩnh hơn Flexbox; nó cũng có hạn chế là không ảnh hưởng đến các phần tử lồng bên trong. Dù vậy có vẻ subgrid sẽ thay đổi phần đó
Tài liệu liên quan:
Grid Garden https://cssgridgarden.com
Flexbox Froggy https://flexboxfroggy.com
Ước gì có một phiên bản “trước nhập môn” còn cơ bản hơn tài liệu này. Mình đã bị mắc ngay từ phần giới thiệu nên không đi tiếp được. Đặc biệt là đoạn “để bố trí các item con bên trong Grid, cần chỉ định số đường của từng item bằng
grid-column”, mình không hiểu số đường ở đây là gìMình cứ nghĩ đọc theo ngữ cảnh rồi sẽ hiểu, nhưng ngay sau đó lại bị kẹt ở các biểu thức như
1 / 3,1 / 4|a||b|, hai đường ở giữa thực ra được tính là một, nên tổng cộng là 3 đườngVì vậy
1/3không phải là phân số mà là phạm vi cột, nghĩa là “bắt đầu từ đường số 1 và kết thúc ở đường số 3”Tên gọi “số đường” không hay lắm, nhưng thực chất đó là mép của các cột. Vì vậy
1/3đi từ mép số 1, tức mép ngoài cùng bên trái, đến mép số 3, và chiếm toàn bộ 2 cộtVới CSS Grid, mình cực kỳ khuyên xem Jen Simmons Mondrian with auto flow. Chỉ cần vào https://labs.jensimmons.com/2017/01-011.html, nhấn
CvàDrồi thay đổi kích thước cửa sổ. Cũng có video YouTube để xem cùng: https://www.youtube.com/watch?v=qNtJ5p3h2A4Thực sự rất tuyệt
Điều mình vẫn muốn hiểu ở auto flow là cách phát hiện và lấp các ô trống. Khi có các đối tượng kích thước khác nhau được đặt ngẫu nhiên, làm thế nào để auto flow không để lại ô trống? Hoặc có thể phát hiện ô trống rồi thêm gì đó vào không?
Hy vọng đây không phải câu hỏi ngốc, nhưng tại sao cú pháp lại được định nghĩa bằng dấu gạch chéo như
1/2,3/4? Viết kiểu từ cột 1 đến cột 2 như1-2, hoặc1->2chẳng phải tự nhiên hơn sao?1--1,1->-1,1/-1thì cái nào tốt hơn?Trong CSS, các giá trị vốn đã được phân tách bằng khoảng trắng, và nếu xét thêm trực giác thông thường khi thấy dấu gạch ngang, thì cách dưới đây tốt hơn nhiều
grid-column: 4 somegridarea / 6;grid-column: span 3 / 6;grid-column: span somegridarea / span someothergridarea;Rõ ràng hơn rất nhiều so với viết như dưới đây
grid-column: 4 somegridarea-6;grid-column: span 3-6;grid-column: span 3->6;grid-column: span somegridarea-span someothergridarea;grid-column: span somegridarea->span someothergridarea;Mình rất thích kiểu bài blog tương tác như thế này. Với Grid, mình rất khuyến khích trực tiếp thử grid area bằng các công cụ như https://grid.layoutit.com/
Mình đã dùng trang đó để lên kế hoạch layout Grid suốt vài năm nay, và việc chỉnh khoảng cách trở nên dễ hơn nhiều
Có lẽ đây là lần đầu tiên mình thực sự hiểu CSS Grid. Hiển thị số giúp ích rất nhiều, và đây sẽ là bài mình thường xuyên tham khảo sau này
Mình thích CSS Grid và khái niệm area, nhưng vẫn nghĩ phần khó nhất là quản lý responsive, đặc biệt giữa kích thước mobile và desktop. Tùy UI element nào cần mở, có cần toàn màn hình hay không, nhiều khi phải cập nhật
grid-template-areavà kích thước hàng/cột bằng JavaScriptMình vẫn chưa tìm được thế giới nơi
minmaxhaycalcgiải quyết được chuyện này, và hy vọng có ai đó biết câu trả lờivar(--foo)thay vì giá trị cố địnhKhi đó JavaScript chỉ cần cập nhật giá trị của biến tương ứng, và Grid sẽ phản ánh giá trị đó
display: noneở kích thước đóVí dụ rất đơn giản: https://codepen.io/trescenzi/pen/vYqpjvL
:has()hoặc subgrid, nhưng mình chưa có nhiều trải nghiệm trực tiếp