Bài 9.4: Thực hành layout phức tạp với Grid

Bài 9.4: Thực hành layout phức tạp với Grid
Trong thế giới phát triển Front-end hiện đại, CSS Grid đã trở thành một công cụ không thể thiếu để kiến tạo các bố cục trang web. Nếu bạn đã làm quen với những khái niệm cơ bản về Grid như container, items, rows và columns, thì giờ là lúc chúng ta nâng tầm kỹ năng bằng cách thực hành với các layout phức tạp hơn nhiều.
Grid thực sự tỏa sáng khi bạn cần xây dựng những cấu trúc bố cục đa chiều mà Flexbox (chủ yếu là một chiều) sẽ gặp khó khăn hoặc đòi hỏi những kỹ thuật "hack" phức tạp. Với Grid, việc tạo ra các khu vực layout riêng biệt, căn chỉnh các thành phần theo cả hàng và cột, và làm cho chúng linh hoạt thích ứng với mọi kích thước màn hình trở nên dễ dàng và trực quan hơn bao giờ hết.
Vậy, làm thế nào để biến những khái niệm cơ bản thành khả năng kiến tạo những layout phức tạp như các trang báo điện tử, dashboard quản trị, hay các ứng dụng web phức tạp? Chúng ta sẽ cùng nhau khám phá những kỹ thuật mạnh mẽ nhất của Grid.
Sức Mạnh Của grid-template-areas
Đây có lẽ là tính năng mạnh mẽ và trực quan nhất của CSS Grid khi đối mặt với layout phức tạp. Thay vì chỉ định vị trí từng item bằng số dòng (line numbers), bạn có thể đặt tên cho các khu vực trong layout của mình và "vẽ" bố cục đó bằng cách sử dụng các tên này.
Hãy tưởng tượng bạn muốn tạo một layout trang web cổ điển với header, sidebar, nội dung chính và footer.
Đầu tiên là cấu trúc HTML đơn giản:
<div class="container">
<header class="header">Header</header>
<nav class="sidebar">Sidebar</nav>
<main class="content">Nội dung chính</main>
<footer class="footer">Footer</footer>
</div>
Bây giờ, chúng ta sử dụng CSS Grid và grid-template-areas
để định nghĩa bố cục:
.container {
display: grid;
/* Định nghĩa các cột: sidebar rộng 200px, content chiếm phần còn lại */
grid-template-columns: 200px 1fr;
/* Định nghĩa các hàng: header/footer tự động, content chiếm phần còn lại */
grid-template-rows: auto 1fr auto;
/* Định nghĩa các khu vực layout bằng tên */
grid-template-areas:
"header header" /* Hàng 1: header chiếm cả 2 cột */
"sidebar content" /* Hàng 2: sidebar cột 1, content cột 2 */
"footer footer"; /* Hàng 3: footer chiếm cả 2 cột */
height: 100vh; /* Chỉ để hiển thị rõ hơn */
gap: 10px; /* Tạo khoảng cách giữa các item */
}
/* Đặt từng item vào khu vực đã định nghĩa */
.header { grid-area: header; background-color: #f0f0f0; }
.sidebar { grid-area: sidebar; background-color: #e0e0e0; }
.content { grid-area: content; background-color: #d0d0d0; }
.footer { grid-area: footer; background-color: #c0c0c0; }
Giải thích:
- Chúng ta bật Grid cho
.container
bằngdisplay: grid;
. grid-template-columns
vàgrid-template-rows
định nghĩa cấu trúc lưới cơ bản (2 cột, 3 hàng). Đơn vị1fr
rất hữu ích, nó cho biết cột/hàng đó sẽ chiếm một phần không gian còn lại sau khi các cột/hàng có kích thước cố định hoặcauto
đã được phân bổ.grid-template-areas
là phần kỳ diệu. Mỗi cặp dấu ngoặc kép""
đại diện cho một hàng. Các tên bên trong (header
,sidebar
,content
,footer
) đại diện cho các ô trong hàng đó. Việc lặp lại tên (ví dụ"header header"
) cho biết item được đặt tên đó sẽ trải dài qua số cột tương ứng. Dấu chấm.
có thể dùng để tạo ra một ô trống.- Cuối cùng, chúng ta chỉ định mỗi item (ví dụ
.header
) sẽ nằm ở khu vực nào bằng thuộc tínhgrid-area
.
Kỹ thuật này cho phép bạn nhìn thấy bố cục ngay trong CSS và dễ dàng thay đổi cấu trúc chỉ bằng cách chỉnh sửa chuỗi ký tự trong grid-template-areas
.
Kết Hợp grid-row
và grid-column
cho Vị Trí Chính Xác và Span
Trong khi grid-area
tuyệt vời cho các khu vực lớn, đôi khi bạn cần kiểm soát chi tiết hơn việc một item bắt đầu và kết thúc ở dòng lưới nào, hoặc nó sẽ trải dài (span) qua bao nhiêu cột/hàng. Đây là lúc grid-row
và grid-column
(hoặc các thuộc tính viết tắt như grid-row-start
, grid-row-end
, grid-column-start
, grid-column-end
) phát huy tác dụng.
Giả sử trong layout trên, bạn muốn nội dung chính (.content
) đôi khi có một thành phần đặc biệt (.featured-item
) nằm đè lên hoặc chiếm một khoảng không gian lớn hơn. Bạn có thể thêm item đó vào HTML:
<div class="container">
... (các item cũ)
<div class="featured-item">Item nổi bật</div>
</div>
Và sử dụng grid-row
/grid-column
để định vị nó:
.container {
display: grid;
grid-template-columns: repeat(4, 1fr); /* Ví dụ: 4 cột bằng nhau */
grid-template-rows: repeat(3, auto); /* Ví dụ: 3 hàng tự động */
gap: 10px;
/* Không dùng grid-template-areas cho ví dụ này,
chỉ định vị trí từng item bằng dòng */
}
.header {
grid-column: 1 / span 4; /* Bắt đầu từ dòng 1, trải dài 4 cột */
grid-row: 1; /* Ở hàng 1 */
background-color: #f0f0f0;
}
.sidebar {
grid-column: 1; /* Ở cột 1 */
grid-row: 2 / span 2; /* Bắt đầu từ hàng 2, trải dài 2 hàng */
background-color: #e0e0e0;
}
.content {
grid-column: 2 / span 3; /* Bắt đầu từ dòng cột 2, trải dài 3 cột */
grid-row: 2; /* Ở hàng 2 */
background-color: #d0d0d0;
}
.footer {
grid-column: 1 / span 4; /* Bắt đầu từ dòng 1, trải dài 4 cột */
grid-row: 3; /* Ở hàng 3 */
background-color: #c0c0c0;
}
.featured-item {
/* Đặt item này đè lên một phần của content và sidebar */
grid-column: 2 / span 2; /* Bắt đầu từ dòng cột 2, trải dài 2 cột */
grid-row: 2; /* Ở hàng 2 */
background-color: yellow;
z-index: 1; /* Đảm bảo nó nằm trên các item khác */
text-align: center;
padding: 20px;
}
Giải thích:
- Chúng ta dùng
repeat(số_lần, kích_thước)
để tạo nhanh nhiều cột/hàng có cùng kích thước. Ở đây là 4 cột1fr
và 3 hàngauto
. grid-column: 1 / span 4;
là cách viết tắt chogrid-column-start: 1;
vàgrid-column-end: span 4;
. Điều này có nghĩa là item sẽ bắt đầu từ dòng lưới số 1 và kéo dài qua 4 cột.grid-row: 2 / span 2;
tương tự, item bắt đầu từ dòng hàng số 2 và kéo dài qua 2 hàng.- Các thuộc tính như
grid-column: 1;
chỉ đơn giản là đặt item vào cột 1 (nó sẽ tự động kết thúc ở dòng lưới tiếp theo, tức là dòng 2). .featured-item
được đặt cố ý vào khu vực chồng lấn (grid-column: 2 / span 2; grid-row: 2;
) để minh họa khả năng xếp chồng các item trong Grid.
Việc sử dụng kết hợp số dòng và span
mang lại sự linh hoạt đáng kinh ngạc khi bạn cần các item không tuân theo cấu trúc lưới cứng nhắc mà "nhảy" qua nhiều ô.
Làm Việc Với Kích Thước Linh Hoạt: fr
, auto
, minmax()
Để layout phức tạp của bạn thực sự mạnh mẽ và thích ứng, việc hiểu và sử dụng đúng các đơn vị kích thước là rất quan trọng.
fr
(fraction): Đơn vị này biểu thị một phần không gian còn lại trong container Grid. Như ví dụ trên,1fr 2fr
có nghĩa là cột thứ hai sẽ rộng gấp đôi cột thứ nhất sau khi trừ đi không gian của các item có kích thước cố định hoặcauto
. Đây là chìa khóa để tạo các cột/hàng co giãn linh hoạt.auto
: Kích thước của cột/hàng sẽ tự động điều chỉnh để phù hợp với nội dung bên trong. Rất hữu ích cho header, footer hoặc sidebar có chiều rộng/cao phụ thuộc vào nội dung.minmax(min, max)
: Đây là một hàm cực kỳ mạnh mẽ cho phép định nghĩa một khoảng kích thước tối thiểu và tối đa cho một cột hoặc hàng. Ví dụ:grid-template-columns: minmax(100px, 300px) 1fr;
nghĩa là cột đầu tiên sẽ có chiều rộng ít nhất 100px và nhiều nhất 300px, còn lại cột thứ hai sẽ chiếm phần không gian còn lại. Điều này giúp kiểm soát sự co giãn của các phần tử, đảm bảo chúng không quá nhỏ hoặc quá lớn.
Ví dụ kết hợp auto
và fr
:
.dashboard-layout {
display: grid;
/* Sidebar rộng tự động theo nội dung, cột chính chiếm phần còn lại */
grid-template-columns: auto 1fr;
/* Header/footer tự động, khu vực giữa chiếm phần còn lại, hàng cuối cùng tự động */
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header"
"sidebar main"
"sidebar footer"; /* Sidebar kéo dài xuống hàng footer */
height: 100vh;
}
.header { grid-area: header; background-color: lightblue; padding: 10px; }
.sidebar { grid-area: sidebar; background-color: lightgreen; padding: 10px; min-width: 150px; } /* Đảm bảo sidebar có min-width */
.main { grid-area: main; background-color: lightcoral; padding: 10px; }
.footer { grid-area: footer; background-color: lightyellow; padding: 10px; }
Giải thích:
- Sidebar (
auto
) sẽ có chiều rộng vừa đủ với nội dung bên trong nó, nhưng không nhỏ hơnmin-width: 150px
. - Cột
main
(1fr
) sẽ co giãn để lấp đầy không gian còn lại. - Hàng
header
,sidebar
vàfooter
(auto
) sẽ có chiều cao tự động theo nội dung của chúng. Hàngmain
(1fr
) sẽ chiếm phần chiều cao còn lại. - Lưu ý trong
grid-template-areas
,sidebar
kéo dài xuống hàng thứ 3, chồng lấn vớifooter
ở cột đầu tiên.
Sự kết hợp của fr
, auto
, và minmax()
mang lại khả năng tùy chỉnh kích thước các thành phần Grid một cách mạnh mẽ và phản hồi linh hoạt, điều này là cốt lõi của các layout phức tạp trong thế giới thực.
Kiểm Soát Khoảng Cách (Gaps) và Căn Chỉnh (Alignment)
Các thuộc tính gap
, row-gap
, column-gap
giúp bạn dễ dàng tạo khoảng cách giữa các item Grid mà không cần dùng margin trên từng item, tránh các vấn đề về margin collapsing hoặc phức tạp khi điều chỉnh responsive.
Các thuộc tính căn chỉnh như justify-items
, align-items
(trên container, căn chỉnh từng item bên trong ô của nó) và justify-content
, align-content
(trên container, căn chỉnh toàn bộ lưới bên trong container) cùng với justify-self
, align-self
(trên từng item, ghi đè căn chỉnh của container) cho phép bạn kiểm soát chính xác vị trí của nội dung bên trong từng ô Grid hoặc vị trí của toàn bộ lưới.
Ví dụ căn chỉnh:
.gallery-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); /* Tự động tạo cột */
gap: 15px; /* Khoảng cách 15px giữa các item */
/* Căn chỉnh các item vào giữa ô của chúng */
justify-items: center;
align-items: center;
/* Căn chỉnh toàn bộ lưới vào giữa container */
justify-content: center;
align-content: center;
height: 400px; /* Ví dụ */
border: 1px solid black;
}
.gallery-item {
background-color: lightblue;
padding: 20px;
border: 1px solid blue;
/* Có thể ghi đè căn chỉnh của container cho item cụ thể */
/* justify-self: start; */
/* align-self: end; */
}
Giải thích:
gap: 15px;
đặt khoảng cách 15px cả theo chiều ngang và chiều dọc.justify-items: center;
căn nội dung của mỗi item vào giữa theo chiều ngang trong ô Grid mà nó chiếm giữ.align-items: center;
căn nội dung của mỗi item vào giữa theo chiều dọc trong ô Grid mà nó chiếm giữ.justify-content: center;
căn chỉnh toàn bộ lưới vào giữa container theo chiều ngang (nếu tổng chiều rộng các cột nhỏ hơn container).align-content: center;
căn chỉnh toàn bộ lưới vào giữa container theo chiều dọc (nếu tổng chiều cao các hàng nhỏ hơn container).
Những thuộc tính này là không thể thiếu để tinh chỉnh bố cục, đảm bảo mọi thứ được căn chỉnh hoàn hảo và có khoảng cách hợp lý, tạo nên một layout phức tạp nhưng chuyên nghiệp và dễ đọc.
Responsive Design Với Grid và Media Queries
Một layout phức tạp chỉ thực sự hoàn hảo khi nó thích ứng được với mọi kích thước màn hình. Tin tốt là CSS Grid kết hợp với Media Queries làm cho điều này trở nên cực kỳ dễ dàng.
Bạn có thể định nghĩa một cấu trúc Grid cho màn hình lớn, và sau đó, bên trong Media Query, bạn chỉ cần định nghĩa lại grid-template-areas
, grid-template-columns
, hoặc grid-template-rows
cho các kích thước màn hình nhỏ hơn.
Quay lại ví dụ layout header, sidebar, content, footer sử dụng grid-template-areas
. Trên màn hình lớn, sidebar nằm bên cạnh content. Trên màn hình nhỏ hơn, chúng ta muốn sidebar nằm bên dưới header và trên content, mỗi khu vực chiếm toàn bộ chiều rộng.
CSS cho màn hình lớn (đã có ở trên):
.container {
display: grid;
grid-template-columns: 200px 1fr;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header"
"sidebar content"
"footer footer";
gap: 10px;
/* ... các style khác ... */
}
CSS cho màn hình nhỏ hơn (sử dụng Media Query):
@media (max-width: 768px) {
.container {
/* Chỉ còn 1 cột duy nhất chiếm toàn bộ chiều rộng */
grid-template-columns: 1fr;
/* Các hàng: header, sidebar, content, footer xếp chồng lên nhau */
grid-template-rows: auto auto 1fr auto; /* Content vẫn chiếm phần còn lại */
/* Định nghĩa lại các khu vực để xếp chồng */
grid-template-areas:
"header"
"sidebar"
"content"
"footer";
}
/* Không cần thay đổi grid-area của từng item vì tên vẫn giữ nguyên */
}
Giải thích:
- Bên trong
@media (max-width: 768px)
, chúng ta chỉ định lại các thuộc tính Grid cho.container
. grid-template-columns: 1fr;
thay đổi layout từ 2 cột thành 1 cột duy nhất.grid-template-rows: auto auto 1fr auto;
định nghĩa 4 hàng riêng biệt (header, sidebar, content, footer).grid-template-areas
được cập nhật để mỗi tên khu vực chiếm một hàng riêng biệt ("header" "sidebar" "content" "footer"
), tạo ra hiệu ứng xếp chồng.
Với cách này, bạn có thể hoàn toàn biến đổi cấu trúc layout của mình ở các breakpoint khác nhau một cách có tổ chức và dễ bảo trì. Đây là lý do tại sao Grid là lựa chọn tuyệt vời cho Responsive Design.
Comments