Bài 9.2: Cách sử dụng CSS Grid cho layout

Bài 9.2: Cách sử dụng CSS Grid cho layout
Chào mừng các bạn quay trở lại với series bài viết về Lập trình Web Front-end! Hôm nay, chúng ta sẽ cùng nhau "mổ xẻ" một trong những kỹ thuật layout mạnh mẽ và hiện đại nhất hiện nay: CSS Grid. Nếu Flexbox tuyệt vời cho layout một chiều (hàng hoặc cột), thì Grid sinh ra để giải quyết các bài toán layout hai chiều (cả hàng và cột) một cách cực kỳ hiệu quả và trực quan.
Hãy quên đi các phương pháp "hack" layout cũ như float
hay inline-block
. CSS Grid mang đến một cách tiếp cận hoàn toàn mới, giúp bạn kiến tạo nên những bố cục trang web phức tạp, linh hoạt và đặc biệt dễ dàng để làm responsive.
CSS Grid hoạt động như thế nào?
Tương tự như Flexbox, CSS Grid hoạt động dựa trên mối quan hệ giữa Container (thẻ cha chứa các phần tử) và Items (các phần tử con trực tiếp bên trong container). Bạn định nghĩa Grid trên container, và các items sẽ tự động sắp xếp hoặc bạn có thể điều khiển vị trí của chúng trong lưới đó.
Chúng ta bắt đầu bằng cách biến một thẻ HTML thành một Grid Container.
Biến Element thành Grid Container
Thuộc tính đầu tiên và quan trọng nhất bạn cần áp dụng cho thẻ cha là display
:
.grid-container {
display: grid;
/* hoặc display: inline-grid; nếu bạn muốn container hiển thị inline */
}
- Giải thích: Khi bạn thiết lập
display: grid;
, thẻ.grid-container
sẽ trở thành một Grid Container, và các thẻ con trực tiếp của nó (chỉ các thẻ con trực tiếp) sẽ trở thành Grid Items. Điều này mở ra cánh cửa cho việc sắp xếp các items theo một cấu trúc lưới hai chiều.
Sau khi có container, chúng ta cần định nghĩa cấu trúc của lưới: số lượng và kích thước của các cột (columns) và hàng (rows).
Định nghĩa Cấu trúc Lưới: Columns và Rows
Đây là lúc sức mạnh của Grid bắt đầu thể hiện. Bạn sử dụng grid-template-columns
và grid-template-rows
để định nghĩa rõ ràng lưới của mình.
.grid-container {
display: grid;
grid-template-columns: 100px 1fr 20%; /* Định nghĩa 3 cột */
grid-template-rows: 50px auto 150px; /* Định nghĩa 3 hàng */
}
- Giải thích:
grid-template-columns
: Định nghĩa các cột của lưới. Mỗi giá trị cách nhau bởi dấu cách mô tả một cột.100px
: Cột đầu tiên có chiều rộng cố định 100 pixels.1fr
: Cột thứ hai chiếm một phần không gian còn lại sau khi các cột có kích thước cố định/tự động đã được phân bổ.fr
(fraction) là đơn vị cực kỳ hữu ích trong Grid, giúp phân chia không gian còn lại một cách linh hoạt. Nếu có1fr 1fr
, mỗi cột sẽ chiếm 50% không gian còn lại. Nếu có1fr 2fr
, cột thứ hai sẽ rộng gấp đôi cột đầu tiên trong không gian còn lại.20%
: Cột thứ ba chiếm 20% chiều rộng của container.
grid-template-rows
: Tương tự như columns, nhưng định nghĩa các hàng.50px
: Hàng đầu tiên cao 50 pixels.auto
: Hàng thứ hai có chiều cao tự động co giãn theo nội dung bên trong nó.150px
: Hàng thứ ba cao 150 pixels.
Sử dụng repeat()
Khi bạn có nhiều cột hoặc hàng có cùng kích thước, repeat()
là người bạn tốt.
.grid-container {
display: grid;
grid-template-columns: repeat(4, 1fr); /* 4 cột, mỗi cột rộng 1fr */
grid-template-rows: repeat(2, 100px); /* 2 hàng, mỗi hàng cao 100px */
}
- Giải thích:
repeat(số_lần, kích thước)
là cách viết tắt tiện lợi, giúp mã CSS của bạn gọn gàng hơn rất nhiều.
Tạo Khoảng Cách Giữa Các Items (gap
)
Lưới thường cần có khoảng cách giữa các ô (cells) hoặc các items.
.grid-container {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px; /* Khoảng cách 20px giữa cả hàng và cột */
/* Hoặc:
row-gap: 15px; /* Khoảng cách giữa các hàng */
column-gap: 25px; /* Khoảng cách giữa các cột */
*/
}
- Giải thích:
gap
: Thuộc tính viết tắt cho cảrow-gap
vàcolumn-gap
. Nếu bạn chỉ truyền một giá trị, nó áp dụng cho cả hai. Nếu truyền hai giá trị (gap: 15px 25px;
), giá trị đầu tiên cho hàng, giá trị thứ hai cho cột.row-gap
: Khoảng cách giữa các hàng.column-gap
: Khoảng cách giữa các cột.- Trước đây thuộc tính này là
grid-gap
,grid-row-gap
,grid-column-gap
. Phiên bản không có tiền tốgrid-
là phiên bản mới hơn và được khuyến khích sử dụng.
Căn Chỉnh Các Items trong Lưới (Alignment)
CSS Grid cung cấp các thuộc tính mạnh mẽ để căn chỉnh các items cả trong ô lưới của chúng (justify-items
, align-items
) và căn chỉnh toàn bộ nội dung lưới trong container nếu có không gian trống (justify-content
, align-content
).
Căn chỉnh Items trong ô lưới (Item Alignment)
Áp dụng trên Grid Container:
.grid-container {
display: grid;
grid-template-columns: repeat(3, 100px);
grid-template-rows: repeat(2, 100px);
place-items: center; /* Căn giữa cả theo trục ngang và dọc cho TẤT CẢ items */
/* Hoặc:
justify-items: center; /* Căn giữa items theo trục ngang (inline axis) */
align-items: center; /* Căn giữa items theo trục dọc (block axis) */
*/
}
- Các giá trị phổ biến:
start
,end
,center
,stretch
(mặc định, item kéo dài hết ô). - Giải thích: Các thuộc tính này điều khiển vị trí của các Grid Items bên trong ô lưới mà chúng chiếm giữ.
place-items
là viết tắt củaalign-items
vàjustify-items
.
Bạn cũng có thể căn chỉnh từng item riêng lẻ bằng cách áp dụng các thuộc tính justify-self
, align-self
, place-self
trực tiếp lên Grid Item đó.
.grid-item-dac-biet {
justify-self: end; /* Căn item này sang phải trong ô của nó */
align-self: start; /* Căn item này lên trên trong ô của nó */
/* Hoặc:
place-self: end start;
*/
}
- Giải thích: Các thuộc tính
*-self
trên item con sẽ ghi đè lên thuộc tính*-items
trên container cha cho item đó.
Căn chỉnh Nội dung Lưới (Content Alignment)
Áp dụng trên Grid Container khi tổng kích thước của lưới nhỏ hơn kích thước của container:
.grid-container {
display: grid;
grid-template-columns: repeat(3, 100px); /* Tổng cộng 300px + gap */
grid-template-rows: repeat(2, 100px); /* Tổng cộng 200px + gap */
width: 500px; /* Container rộng hơn lưới */
height: 400px; /* Container cao hơn lưới */
place-content: center; /* Căn giữa toàn bộ lưới trong container */
/* Hoặc:
justify-content: center; /* Căn giữa toàn bộ lưới theo trục ngang */
align-content: center; /* Căn giữa toàn bộ lưới theo trục dọc */
*/
}
- Các giá trị phổ biến:
start
,end
,center
,stretch
,space-around
,space-between
,space-evenly
. - Giải thích: Các thuộc tính này điều khiển cách toàn bộ lưới (tập hợp các ô và khoảng cách) được đặt trong Grid Container khi container có không gian trống.
place-content
là viết tắt củaalign-content
vàjustify-content
.
Đặt Vị Trí Cho Grid Items
Bây giờ chúng ta đã có lưới, làm thế nào để đặt các items vào những vị trí cụ thể hoặc trải dài qua nhiều ô? Chúng ta sử dụng các thuộc tính trên Grid Items: grid-column
và grid-row
.
Lưới tạo ra các đường kẻ (lines) và các ô (cells). Các đường kẻ được đánh số bắt đầu từ 1.
<div class="grid-container">
<div class="item item-1">Item 1</div>
<div class="item item-2">Item 2</div>
<div class="item item-3">Item 3</div>
<div class="item item-4">Item 4</div>
</div>
.grid-container {
display: grid;
grid-template-columns: repeat(4, 1fr); /* Có 5 đường kẻ cột: 1, 2, 3, 4, 5 */
grid-template-rows: repeat(2, 100px); /* Có 3 đường kẻ hàng: 1, 2, 3 */
gap: 10px;
}
.item-1 {
/* Bắt đầu từ đường kẻ cột 1, kết thúc tại đường kẻ cột 3 */
grid-column: 1 / 3;
/* Bắt đầu từ đường kẻ hàng 1, kết thúc tại đường kẻ hàng 2 */
grid-row: 1 / 2;
background-color: lightblue;
}
.item-2 {
/* Bắt đầu từ đường kẻ cột 3, trải dài qua 2 cột */
grid-column: 3 / span 2;
/* Bắt đầu từ đường kẻ hàng 2, kết thúc tại đường kẻ hàng 3 */
grid-row: 2 / 3;
background-color: lightgreen;
}
.item-3 {
/* Bắt đầu từ đường kẻ cột 1, trải dài qua 4 cột */
grid-column: 1 / span 4;
/* Bắt đầu từ đường kẻ hàng 1, kết thúc tại đường kẻ hàng 3 */
grid-row: 1 / span 2; /* Hoặc 1 / 3 */
background-color: lightcoral;
}
- Giải thích:
grid-column: start / end;
: Item bắt đầu tại đường kẻ cộtstart
và kết thúc ngay trước đường kẻ cộtend
.grid-column: start / span count;
: Item bắt đầu tại đường kẻ cộtstart
và trải dài quacount
cột.grid-row
: Tương tự nhưgrid-column
, nhưng cho hàng.- Bạn có thể sử dụng số âm để đếm từ đường kẻ cuối cùng (ví dụ:
-1
là đường kẻ cuối cùng).
Thuộc tính viết tắt grid-area
Thuộc tính grid-area
là một cách viết tắt rất tiện lợi:
.item-1 {
/* grid-area: start-row / start-column / end-row / end-column; */
grid-area: 1 / 1 / 2 / 3; /* Item 1 trong ví dụ trên */
}
- Giải thích: Thứ tự là: hàng bắt đầu, cột bắt đầu, hàng kết thúc, cột kết thúc.
grid-area
còn có một công dụng mạnh mẽ hơn: sử dụng tên vùng lưới.
Tạo Layout Với Tên Vùng (grid-template-areas
)
Đây là một cách cực kỳ trực quan để tạo các bố cục phức tạp. Đầu tiên, bạn định nghĩa các vùng trên Grid Container bằng grid-template-areas
.
.grid-container {
display: grid;
grid-template-columns: 1fr 3fr 1fr; /* 3 cột */
grid-template-rows: auto 1fr auto; /* 3 hàng */
gap: 10px;
grid-template-areas:
"header header header" /* Hàng 1: 3 ô đều là header */
"nav main aside" /* Hàng 2: nav, main, aside */
"footer footer footer";/* Hàng 3: 3 ô đều là footer */
}
/* Gán tên vùng cho các Grid Items */
header { grid-area: header; background-color: lightblue; }
nav { grid-area: nav; background-color: lightgreen; }
main { grid-area: main; background-color: lightcoral; }
aside { grid-area: aside; background-color: lightgoldenrodyellow; }
footer { grid-area: footer; background-color: lightsalmon; }
<div class="grid-container">
<header>Header</header>
<nav>Navigation</nav>
<main>Main Content</main>
<aside>Sidebar</aside>
<footer>Footer</footer>
</div>
- Giải thích:
grid-template-areas
: Sử dụng các chuỗi string, mỗi chuỗi đại diện cho một hàng trong lưới. Trong mỗi chuỗi, các từ cách nhau bởi dấu cách đại diện cho các ô trong hàng đó. Tên giống nhau trong các ô liền kề sẽ tạo thành một vùng. Dấu.
(dấu chấm) đại diện cho một ô trống không được gán cho item nào.- Sau khi định nghĩa các vùng, bạn chỉ cần gán tên vùng tương ứng cho từng Grid Item bằng thuộc tính
grid-area: ten_vung;
.
- Ưu điểm: Code layout trở nên rất dễ đọc, bạn có thể "nhìn" thấy cấu trúc layout ngay trong CSS! Điều này cũng giúp việc làm responsive cực kỳ đơn giản chỉ bằng cách thay đổi giá trị của
grid-template-areas
trong media query.
Xây dựng Responsive Layout với Grid
Một trong những lý do khiến Grid trở nên phổ biến là khả năng tạo layout responsive một cách tự nhiên và hiệu quả. Bạn có thể kết hợp Grid với Media Queries để thay đổi cấu trúc lưới dựa trên kích thước màn hình.
Ví dụ: Thay đổi layout Header/Nav/Main/Aside/Footer ở trên cho màn hình nhỏ hơn.
.grid-container {
display: grid;
grid-template-columns: 1fr 3fr 1fr; /* Default cho màn hình lớn */
grid-template-rows: auto 1fr auto;
gap: 10px;
grid-template-areas:
"header header header"
"nav main aside"
"footer footer footer";
}
/* ... (CSS cho items như trên) ... */
@media (max-width: 768px) {
.grid-container {
grid-template-columns: 1fr; /* Chỉ 1 cột duy nhất */
grid-template-rows: auto auto 1fr auto auto; /* Các hàng xếp chồng */
grid-template-areas:
"header" /* Header ở trên cùng */
"nav" /* Tiếp theo là Nav */
"main" /* Nội dung chính chiếm phần lớn */
"aside" /* Sidebar xuống dưới Main */
"footer";/* Footer ở cuối cùng */
}
/* Các item vẫn giữ nguyên grid-area: ten_vung; của chúng */
}
- Giải thích: Bên trong
@media
query, chúng ta chỉ cần thay đổigrid-template-columns
,grid-template-rows
, vàgrid-template-areas
. Các Grid Item tự động di chuyển và điều chỉnh kích thước theo cấu trúc lưới mới nhờ thuộc tínhgrid-area
không đổi. Quá trình này đơn giản và mạnh mẽ hơn rất nhiều so với việc điều chỉnhfloat
,width
,margin
thủ công cho từng breakpoint.
Tự động điều chỉnh số cột với auto-fit
/auto-fill
và minmax()
Đây là một kỹ thuật Grid cực kỳ hữu ích để tạo ra các layout danh sách sản phẩm, gallery hình ảnh, v.v., mà không cần media query phức tạp để điều chỉnh số lượng cột.
.gallery {
display: grid;
/* Cột sẽ tự động lấp đầy hàng, mỗi cột có chiều rộng TỐI THIỂU 200px
và TỐI ĐA là 1fr (chiếm phần còn lại) */
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
}
<div class="gallery">
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<div class="item">Item 3</div>
<div class="item">Item 4</div>
<div class="item">Item 5</div>
<div class="item">Item 6</div>
<div class="item">Item 7</div>
</div>
- Giải thích:
repeat(auto-fit, ...)
: Lặp lại cấu trúc cột (ở đây làminmax(200px, 1fr)
) để lấp đầy không gian có sẵn trong container. Nó sẽ tạo ra càng nhiều cột càng tốt với kích thước được chỉ định.repeat(auto-fill, ...)
: Tương tựauto-fit
, nhưng nó sẽ tạo ra càng nhiều cột càng tốt, kể cả khi không có đủ items để điền vào tất cả các ô trống được tạo ra.auto-fit
sẽ "ép" các cột lại để lấp đầy không gian, cònauto-fill
có thể để lại khoảng trống ở cuối hàng nếu không đủ items. Trong hầu hết các trường hợp layout danh sách,auto-fit
là thứ bạn cần.minmax(min_size, max_size)
: Định nghĩa một dải kích thước cho cột hoặc hàng. Ở đây, cột sẽ không bao giờ nhỏ hơn 200px (min_size
), nhưng có thể mở rộng lên đến1fr
(max_size
) nếu có không gian.
- Kết quả: Khi container
.gallery
đủ rộng, nó sẽ tạo ra 3 cột (ví dụ). Nếu bạn thu nhỏ trình duyệt, khi không đủ không gian cho 3 cột mỗi cột ít nhất 200px, nó sẽ tự động giảm xuống 2 cột, rồi 1 cột, mà không cần bạn phải viết bất kỳ media query nào để thay đổigrid-template-columns
. Đây là kỹ thuật cực kỳ mạnh mẽ cho responsive design!
Các Thuộc tính Grid khác (Tham khảo thêm)
CSS Grid còn rất nhiều thuộc tính mạnh mẽ khác mà bạn có thể tìm hiểu sâu hơn:
grid-auto-columns
,grid-auto-rows
: Định nghĩa kích thước cho các hàng/cột được tạo ra ngầm định (implicit grid) khi có item được đặt ngoài lưới được định nghĩa bằnggrid-template
.grid-auto-flow
: Kiểm soát cách các items tự động đặt mình vào lưới nếu bạn không chỉ định vị trí cụ thể cho chúng.grid
: Thuộc tính viết tắt cho nhiều thuộc tính Grid khác.
Khi nào nên dùng Grid và khi nào nên dùng Flexbox?
Đây là câu hỏi thường gặp. Quy tắc chung là:
- Sử dụng Flexbox cho layout một chiều (1D): ví dụ: thanh điều hướng (nav bar), form, căn chỉnh các items trên một hàng hoặc một cột duy nhất.
- Sử dụng CSS Grid cho layout hai chiều (2D): ví dụ: bố cục trang web tổng thể (header, sidebar, main, footer), gallery ảnh, bảng giá sản phẩm phức tạp.
Thực tế, bạn sẽ thường xuyên kết hợp cả hai. Ví dụ, bạn dùng Grid để tạo layout trang chính (hai chiều), và bên trong mỗi ô lưới (như sidebar hoặc footer), bạn lại dùng Flexbox để căn chỉnh nội dung bên trong (một chiều).
Comments