Bài 3.4: Thực hành layout responsive với Flexbox

Bài 3.4: Thực hành layout responsive với Flexbox
Chào mừng bạn quay trở lại với chuỗi bài học Lập trình Web Front-end! Trong bài viết này, chúng ta sẽ đi sâu vào một trong những công cụ mạnh mẽ và linh hoạt nhất của CSS để xây dựng layout: Flexbox. Đặc biệt, chúng ta sẽ tập trung vào việc sử dụng Flexbox để tạo ra các giao diện responsive – tức là các giao diện có khả năng tự động thích ứng và hiển thị đẹp mắt trên mọi loại thiết bị, từ điện thoại nhỏ gọn đến màn hình máy tính để bàn rộng lớn.
Quên đi những kỹ thuật phức tạp và cứng nhắc ngày xưa, Flexbox mang đến một cách tiếp cận trực quan và hiệu quả hơn rất nhiều cho việc căn chỉnh, phân phối không gian và sắp xếp các phần tử trong một container. Hãy cùng bắt tay vào thực chiến ngay thôi!
Vì sao Flexbox lại "đỉnh" cho Responsive Layout?
Trước khi đi vào các ví dụ, hãy nhắc lại nhanh lý do vì sao Flexbox lại là lựa chọn tuyệt vời cho responsive design:
- Linh hoạt: Flexbox được thiết kế để phân phối không gian dọc theo một trục (main axis) và vuông góc với nó (cross axis). Các phần tử (flex items) bên trong container (flex container) có thể tự động co giãn (shrink) hoặc mở rộng (grow) để lấp đầy không gian trống hoặc tránh tràn ra ngoài.
- Kiểm soát luồng: Bạn có thể dễ dàng thay đổi hướng sắp xếp của các phần tử (ngang hay dọc) và thậm chí là đảo ngược thứ tự chỉ với vài thuộc tính CSS.
- Căn chỉnh dễ dàng: Căn giữa phần tử? Căn đều khoảng trống? Căn chỉnh theo baseline? Flexbox làm tất cả những việc này trở nên đơn giản hơn bao giờ hết.
- Responsive tự nhiên: Một số thuộc tính như
flex-wrap
cho phép các phần tử tự động xuống dòng khi không còn đủ chỗ trên cùng một hàng/cột, tạo ra một hành vi responsive tự nhiên mà không cần quá nhiều media queries phức tạp.
Với Flexbox, việc xây dựng các bố cục phức tạp và đảm bảo chúng hoạt động tốt trên mọi kích thước màn hình trở nên nhẹ nhàng hơn rất nhiều.
Bắt đầu: Flex Container và Flex Items
Để sử dụng Flexbox, bạn cần thiết lập một flex container bằng cách đặt thuộc tính display: flex;
hoặc display: inline-flex;
cho phần tử cha. Ngay lập tức, tất cả các phần tử con trực tiếp bên trong phần tử đó sẽ trở thành flex items.
Ví dụ cơ bản nhất:
<div class="flex-container">
<div class="flex-item">Item 1</div>
<div class="flex-item">Item 2</div>
<div class="flex-item">Item 3</div>
</div>
.flex-container {
display: flex; /* Biến div này thành flex container */
/* Mặc định: flex-direction: row; flex-wrap: nowrap; */
}
.flex-item {
padding: 20px;
margin: 10px;
background-color: lightblue;
border: 1px solid blue;
}
Giải thích: Chỉ cần thêm display: flex;
vào .flex-container
, các .flex-item
sẽ tự động xếp thành một hàng ngang (hướng mặc định là row
). Mặc định, chúng sẽ cố gắng nằm trên một hàng (mặc định flex-wrap: nowrap
).
Thực hành Responsive với các ví dụ
Bây giờ, hãy cùng xem Flexbox giúp chúng ta tạo responsive layout như thế nào thông qua các ví dụ thực tế.
Ví dụ 1: Header linh hoạt (Wrapping Header)
Một header phổ biến thường có logo ở một bên và menu điều hướng ở bên kia. Trên màn hình nhỏ, chúng ta muốn menu có thể xuống dòng hoặc hiển thị theo cách khác.
<header class="site-header">
<div class="site-logo">Logo Của Tôi</div>
<nav class="site-nav">
<ul>
<li><a href="#">Trang chủ</a></li>
<li><a href="#">Giới thiệu</a></li>
<li><a href="#">Dịch vụ</a></li>
<li><a href="#">Liên hệ</a></li>
</ul>
</nav>
</header>
.site-header {
display: flex;
justify-content: space-between; /* Căn logo và nav ra hai đầu */
align-items: center; /* Căn giữa theo chiều dọc */
padding: 10px 20px;
background-color: #f0f0f0;
flex-wrap: wrap; /* *** Quan trọng cho responsive *** */
}
.site-logo {
font-size: 1.5em;
font-weight: bold;
}
.site-nav ul {
list-style: none;
padding: 0;
margin: 0;
display: flex; /* Biến ul thành flex container để căn chỉnh li */
}
.site-nav li {
margin-left: 20px; /* Khoảng cách giữa các menu item */
}
/* Media query để điều chỉnh khi màn hình nhỏ hơn */
@media (max-width: 600px) {
.site-header {
justify-content: center; /* Căn giữa logo và nav khi chúng xuống dòng */
}
.site-nav ul {
flex-direction: column; /* Chuyển menu thành cột dọc */
align-items: center; /* Căn giữa các menu item */
margin-top: 10px;
}
.site-nav li {
margin: 5px 0; /* Khoảng cách giữa các menu item dọc */
}
}
Giải thích:
- Chúng ta đặt
display: flex;
cho.site-header
. justify-content: space-between;
đẩy.site-logo
và.site-nav
ra hai phía.align-items: center;
căn chỉnh chúng theo chiều dọc giữa header.flex-wrap: wrap;
là điểm mấu chốt: Khi không còn đủ không gian trên một hàng, các phần tử (logo và nav) sẽ tự động xuống dòng.- Bên trong
.site-nav
, chúng ta cũng dùngdisplay: flex;
choul
để cácli
(menu items) nằm ngang. - Media query
@media (max-width: 600px)
được sử dụng để điều chỉnh hành vi chỉ khi màn hình có chiều rộng tối đa là 600px.justify-content: center;
trên.site-header
sẽ căn giữa logo và nav khi chúng đã xuống dòng (mỗi cái một hàng).flex-direction: column;
trên.site-nav ul
sẽ biến menu items từ hàng ngang thành cột dọc.align-items: center;
trên.site-nav ul
sẽ căn giữa các menu items trong cột đó.
Ví dụ này cho thấy sự kết hợp hài hòa giữa Flexbox và Media Queries để tạo ra responsive layout mạnh mẽ và dễ kiểm soát.
Ví dụ 2: Layout cột co giãn (Flexible Column Layout)
Đây là một bố cục rất phổ biến: nội dung chính ở giữa và các sidebar ở hai bên. Chúng ta muốn các sidebar "biến mất" hoặc chuyển xuống dưới nội dung chính trên màn hình nhỏ.
<div class="page-wrapper">
<aside class="sidebar left-sidebar">
<h2>Sidebar Trái</h2>
<p>Nội dung sidebar...</p>
</aside>
<main class="main-content">
<h1>Nội dung Chính</h1>
<p>Đây là nội dung chính của trang. Nội dung này sẽ chiếm không gian còn lại.</p>
<p>Với Flexbox, việc tạo các cột linh hoạt trở nên rất dễ dàng.</p>
</main>
<aside class="sidebar right-sidebar">
<h2>Sidebar Phải</h2>
<p>Nội dung sidebar...</p>
</aside>
</div>
.page-wrapper {
display: flex; /* Biến wrapper thành flex container */
max-width: 1200px;
margin: 0 auto; /* Căn giữa wrapper */
padding: 20px;
gap: 20px; /* Tạo khoảng cách giữa các cột */
}
.sidebar {
background-color: #e0e0e0;
padding: 15px;
/* flex-basis: Kích thước cơ sở ưa thích */
/* flex-grow: Tỷ lệ mở rộng */
/* flex-shrink: Tỷ lệ co lại */
flex: 0 0 250px; /* Không co giãn, kích thước cơ sở 250px */
}
.main-content {
background-color: #ffffff;
padding: 15px;
flex: 1; /* *** Quan trọng cho responsive *** */
/* flex: grow shrink basis */
/* flex: 1 1 0%; -> Cho phép mở rộng/co lại, kích thước cơ sở 0 (sẽ tính toán dựa vào không gian trống) */
/* flex: 1; là shorthand cho flex: 1 1 0%; */
}
/* Media query để stack các cột trên màn hình nhỏ */
@media (max-width: 768px) {
.page-wrapper {
flex-direction: column; /* *** Quan trọng: chuyển thành cột dọc *** */
}
.sidebar,
.main-content {
flex: auto; /* Cho phép các item co giãn và sử dụng kích thước tự nhiên/đầy đủ chiều rộng */
width: auto; /* Đảm bảo không có width cố định gây cản trở flex */
}
}
/* Media query để ẩn một sidebar trên màn hình rất nhỏ (tùy chọn) */
@media (max-width: 480px) {
.right-sidebar {
display: none; /* Ẩn sidebar phải */
}
}
Giải thích:
.page-wrapper
là flex container, hướng mặc định làrow
(hàng ngang).- Chúng ta sử dụng thuộc tính
flex
trên các item (.sidebar
,.main-content
).flex
là shorthand choflex-grow
,flex-shrink
, vàflex-basis
. flex: 0 0 250px;
cho.sidebar
:flex-grow: 0
: Sidebar sẽ không mở rộng để lấp đầy không gian trống.flex-shrink: 0
: Sidebar sẽ không co lại nhỏ hơn kích thước cơ sở của nó (trừ khi container quá hẹp).flex-basis: 250px
: Kích thước "mong muốn" ban đầu của sidebar là 250px.
flex: 1;
cho.main-content
:flex-grow: 1
: Main content sẽ mở rộng để lấp đầy toàn bộ không gian trống còn lại sau khi các sidebar đã chiếm chỗ.flex-shrink: 1
: Cho phép main content co lại nếu cần.flex-basis: 0%
: Kích thước cơ sở là 0 (hoặc auto). Kết hợp vớiflex-grow: 1
, nó sẽ chiếm phần lớn không gian.
- Media query
@media (max-width: 768px)
:flex-direction: column;
trên.page-wrapper
là thay đổi then chốt. Nó biến hướng của flex container từ ngang sang dọc, khiến các item xếp chồng lên nhau.flex: auto;
trên.sidebar
và.main-content
trong media query này cho phép chúng tự động điều chỉnh kích thước dựa trên nội dung và không gian có sẵn trong hướng dọc (chiếm toàn bộ chiều rộng).
- Media query
@media (max-width: 480px)
(tùy chọn) minh họa cách bạn có thể ẩn hoàn toàn một phần tử trên màn hình rất nhỏ bằngdisplay: none;
.
Ví dụ này làm nổi bật cách flex-direction
và thuộc tính flex
trên item có thể tạo ra sự chuyển đổi bố cục mượt mà giữa các kích thước màn hình.
Ví dụ 3: Grid sản phẩm/hình ảnh (Flexible Grid)
Tạo một grid responsive, nơi các item tự động xuống dòng và điều chỉnh số lượng item trên mỗi hàng tùy thuộc vào chiều rộng màn hình.
<div class="product-grid">
<div class="product-item">Sản phẩm 1</div>
<div class="product-item">Sản phẩm 2</div>
<div class="product-item">Sản phẩm 3</div>
<div class="product-item">Sản phẩm 4</div>
<div class="product-item">Sản phẩm 5</div>
<div class="product-item">Sản phẩm 6</div>
<!-- Thêm nhiều item khác -->
</div>
.product-grid {
display: flex;
flex-wrap: wrap; /* *** Quan trọng: Cho phép xuống dòng *** */
gap: 20px; /* Khoảng cách giữa các item */
padding: 20px;
justify-content: center; /* Căn giữa các item trên hàng cuối nếu không đầy */
}
.product-item {
background-color: #fff;
border: 1px solid #ddd;
padding: 15px;
text-align: center;
/* flex: grow shrink basis */
flex: 1 1 250px; /* *** Quan trọng cho responsive grid *** */
/* Các item sẽ cố gắng có kích thước cơ sở 250px,
cho phép co giãn và tự động xuống dòng khi cần */
max-width: 300px; /* Giới hạn kích thước tối đa */
}
/* Điều chỉnh kích thước cơ sở cho màn hình lớn hơn */
@media (min-width: 768px) {
.product-item {
flex-basis: 300px; /* Kích thước cơ sở lớn hơn trên màn hình rộng */
}
}
/* Điều chỉnh kích thước cơ sở cho màn hình nhỏ hơn */
@media (max-width: 600px) {
.product-item {
flex-basis: 100%; /* Chiếm toàn bộ chiều rộng trên màn hình nhỏ */
max-width: none; /* Bỏ giới hạn max-width */
}
}
Giải thích:
.product-grid
là flex container vớidisplay: flex;
.flex-wrap: wrap;
là không thể thiếu cho một grid responsive với Flexbox. Nó cho phép các.product-item
tự động nhảy sang hàng mới khi hết chỗ.gap: 20px;
tạo khoảng cách giữa các item (một thuộc tính CSS hiện đại, thay thế việc dùng margin phức tạp).justify-content: center;
giúp hàng cuối cùng của grid được căn giữa nếu số lượng item không đủ lấp đầy hàng.flex: 1 1 250px;
trên.product-item
là cốt lõi của responsive grid:flex-grow: 1
: Cho phép item mở rộng nếu có không gian trống (ví dụ: lấp đầy khoảng trống nhỏ ở cuối hàng).flex-shrink: 1
: Cho phép item co lại nếu container quá hẹp.flex-basis: 250px
: Đây là kích thước "lý tưởng" ban đầu của mỗi item. Flexbox sẽ cố gắng đặt càng nhiều item có kích thước 250px (cộng thêm khoảng cáchgap
) trên một hàng càng tốt trước khi xuống dòng.
- Media queries được sử dụng để tinh chỉnh
flex-basis
ở các breakpoints khác nhau, kiểm soát số lượng item mong muốn trên mỗi hàng (ví dụ: 1 cột trên mobile, 2-3 cột trên tablet, 4+ cột trên desktop).flex-basis: 100%;
trên mobile đảm bảo mỗi item chiếm trọn chiều rộng.
Ví dụ grid này cho thấy cách Flexbox, đặc biệt là thuộc tính flex-wrap
và flex-basis
, giúp bạn tạo ra các bố cục lưới động mà không cần dùng đến CSS Grid (mặc dù CSS Grid thường là lựa chọn tốt hơn cho layout lưới 2 chiều phức tạp hơn).
Ví dụ 4: Căn giữa hoàn hảo (Perfect Centering)
Một nhiệm vụ tưởng chừng đơn giản nhưng lại gây khó khăn trước kia: căn giữa một phần tử (hoặc nhiều phần tử) cả theo chiều ngang và chiều dọc bên trong một container. Flexbox làm điều này siêu dễ.
<div class="center-container">
<div class="centered-item">Tôi đã được căn giữa!</div>
</div>
.center-container {
display: flex; /* Biến container thành flex container */
justify-content: center; /* Căn giữa theo trục chính (ngang mặc định) */
align-items: center; /* Căn giữa theo trục vuông góc (dọc mặc định) */
height: 300px; /* Cần có chiều cao để thấy căn giữa theo chiều dọc */
border: 2px dashed gray;
}
.centered-item {
padding: 20px;
background-color: lightgreen;
}
Giải thích:
- Chỉ cần áp dụng
display: flex;
lên phần tử cha (.center-container
). justify-content: center;
căn chỉnh các flex item dọc theo trục chính của container (mặc định là trục ngang).align-items: center;
căn chỉnh các flex item dọc theo trục vuông góc với trục chính (mặc định là trục dọc).- Kết hợp cả hai, mọi item bên trong container sẽ được căn giữa hoàn hảo cả theo chiều ngang và chiều dọc, bất kể kích thước của item hay kích thước của container (miễn là container có đủ không gian). Đây là một kỹ thuật cực kỳ hữu ích trong responsive design khi bạn cần căn giữa nội dung một cách động.
Một vài lưu ý khi sử dụng Flexbox cho Responsive
- Kết hợp với Media Queries: Mặc dù Flexbox mang lại tính responsive tự nhiên (nhờ
flex-wrap
,flex-grow
,flex-shrink
), bạn vẫn cần sử dụng Media Queries để thay đổiflex-direction
,justify-content
,align-items
, hoặcflex
properties trên item ở các breakpoints khác nhau để tạo ra các bố cục thực sự khác biệt cho mobile, tablet, desktop. - Hiểu rõ thuộc tính
flex
: Thuộc tínhflex
(shorthand củaflex-grow
,flex-shrink
,flex-basis
) là chìa khóa để kiểm soát cách các item co giãn và phân bổ không gian. Hãy dành thời gian làm quen với nó. gap
là bạn tốt: Sử dụng thuộc tínhgap
(hoặccolumn-gap
vàrow-gap
) trên flex container thay vì margin phức tạp trên item để tạo khoảng cách giữa các item. Nó đơn giản và trực quan hơn rất nhiều.- Không áp dụng Flexbox lên Text/Inline Elements: Flexbox chỉ hoạt động hiệu quả nhất với các block-level element hoặc inline-block element được đặt trong flex container.
Comments