Bài 3.3: Responsive design với Flexbox

Bài 3.3: Responsive design với Flexbox
Chào mừng bạn đến với một trong những chủ đề quan trọng nhất khi xây dựng giao diện web hiện đại: Responsive Design. Trong thế giới mà người dùng truy cập website của bạn từ đủ loại thiết bị với kích thước màn hình khác nhau – từ điện thoại thông minh nhỏ gọn đến máy tính để bàn màn hình rộng – việc đảm bảo trải diện mượt mà, dễ dùng trên mọi thiết bị là điều bắt buộc.
Và một trong những công cụ mạnh mẽ, linh hoạt và được yêu thích nhất để đạt được điều này chính là CSS Flexbox.
Tại sao Flexbox lại "thần kỳ" với Responsive Design?
Trước khi có Flexbox, chúng ta phải "vật lộn" với float
, inline-block
, position
... để tạo bố cục (layout). Việc căn chỉnh, phân phối không gian, và đặc biệt là làm cho chúng linh hoạt theo kích thước màn hình là một thử thách thực sự.
Flexbox (Flexible Box Layout) ra đời để giải quyết trực tiếp vấn đề bố cục một chiều (theo hàng hoặc theo cột). Sức mạnh của nó nằm ở khả năng linh hoạt co giãn các phần tử bên trong container theo cách bạn mong muốn, phân phối không gian trống một cách thông minh và căn chỉnh các mục một cách dễ dàng. Những đặc điểm này chính là bí quyết để tạo ra các bố cục responsive một cách hiệu quả.
Flexbox cho phép bạn:
- Kiểm soát hướng của các mục (item) (hàng ngang hoặc cột dọc).
- Kiểm soát cách các mục co lại hoặc giãn ra để lấp đầy không gian hoặc tránh tràn container.
- Kiểm soát sự căn chỉnh của các mục dọc theo trục chính và trục phụ.
- Kiểm soát thứ tự hiển thị của các mục độc lập với thứ tự trong mã HTML.
Tất cả những điều này giúp bạn dễ dàng điều chỉnh bố cục chỉ bằng cách thay đổi một vài thuộc tính CSS, đặc biệt khi kết hợp với Media Queries.
Các Thuộc tính Flexbox Quan trọng cho Responsive
Để khai thác sức mạnh của Flexbox cho responsive, hãy làm quen (hoặc ôn lại) các thuộc tính cốt lõi:
display: flex;
: Biến container thành một flex container, các phần tử con trực tiếp của nó trở thành flex item. Đây là điểm khởi đầu.flex-direction
: Xác định hướng của trục chính (main axis). Giá trị có thể làrow
(mặc định, theo hàng ngang),column
(theo cột dọc),row-reverse
,column-reverse
. Thuộc tính này cực kỳ hữu ích khi bạn muốn thay đổi bố cục từ hàng sang cột (hoặc ngược lại) trên các kích thước màn hình khác nhau.flex-wrap
: Kiểm soát xem các flex item có nên nằm gọn trên một hàng/cột hay được phép xuống dòng/cột mới khi không đủ chỗ. Giá trị:nowrap
(mặc định),wrap
,wrap-reverse
.wrap
là chìa khóa để các item tự động điều chỉnh và không bị tràn.justify-content
: Căn chỉnh các flex item dọc theo trục chính (ví dụ: căn trái, căn giữa, căn phải, phân bố đều khoảng trống).align-items
: Căn chỉnh các flex item dọc theo trục phụ (cross axis).flex-grow
,flex-shrink
,flex-basis
(hoặc thuộc tính rút gọnflex
): Kiểm soát cách các item co giãn.flex-basis
: Kích thước ban đầu ưu tiên của flex item trước khi co giãn.flex-grow
: Hệ số xác định mức độ item có thể phình to để lấp đầy không gian trống.flex-shrink
: Hệ số xác định mức độ item có thể co nhỏ lại khi không đủ không gian.
Kết hợp Flexbox với Media Queries: Sức mạnh Responsive Bùng Nổ
Media Queries là kỹ thuật CSS cho phép bạn áp dụng các style CSS khác nhau dựa trên các đặc điểm của thiết bị xem, phổ biến nhất là kích thước màn hình.
Cú pháp cơ bản:
@media (điều-kiện-về-thiết-bị) {
/* CSS styles chỉ áp dụng khi điều kiện đúng */
}
Khi kết hợp Flexbox với Media Queries, bạn có thể nói rằng: "Trên màn hình rộng, các mục xếp theo hàng (flex-direction: row), nhưng trên màn hình nhỏ, hãy xếp chúng theo cột (flex-direction: column)". Hoặc "Trên màn hình rộng, mỗi mục chiếm 30% chiều rộng, nhưng trên màn hình nhỏ, chúng chiếm 100% và xuống dòng (flex-wrap: wrap; flex-basis: 100%)".
Đây là lúc Flexbox thực sự tỏa sáng!
Ví dụ Minh Họa
Hãy đi qua một vài ví dụ thực tế để thấy Flexbox làm Responsive dễ dàng như thế nào.
Ví dụ 1: Header Đơn Giản (Ngang trên Desktop, Dọc trên Mobile)
Giả sử chúng ta có một header với logo và menu điều hướng.
HTML:
<header class="site-header">
<div class="logo">Logo</div>
<nav class="main-nav">
<a href="#">Trang chủ</a>
<a href="#">Sản phẩm</a>
<a href="#">Liên hệ</a>
</nav>
</header>
CSS (Sử dụng Flexbox và Media Query):
.site-header {
display: flex; /* Biến header thành flex container */
justify-content: space-between; /* Logo và nav ở hai đầu */
align-items: center; /* Căn giữa theo chiều dọc */
padding: 10px 20px;
background-color: #f0f0f0;
}
.main-nav {
display: flex; /* Biến nav thành flex container */
gap: 15px; /* Tạo khoảng cách giữa các link */
}
/* Responsive: Trên màn hình nhỏ hơn hoặc bằng 768px */
@media (max-width: 768px) {
.site-header {
flex-direction: column; /* Xếp logo và nav theo cột */
align-items: flex-start; /* Căn trái các mục */
}
.logo {
margin-bottom: 10px; /* Thêm khoảng cách dưới logo */
}
.main-nav {
flex-direction: column; /* Xếp các link nav theo cột */
gap: 5px; /* Giảm khoảng cách giữa các link */
}
}
Giải thích:
- Mặc định (trên màn hình lớn hơn 768px),
.site-header
códisplay: flex;
vàflex-direction: row;
(mặc định).justify-content: space-between;
đẩy logo và menu về hai phía..main-nav
cũng là flex container xếp các link ngang hàng với khoảng cáchgap
. - Khi màn hình nhỏ hơn 768px,
@media (max-width: 768px)
kích hoạt. .site-header
chuyểnflex-direction: column;
làm logo và nav xếp chồng lên nhau.align-items: flex-start;
căn chúng sang trái..main-nav
cũng chuyểnflex-direction: column;
làm các link xếp dọc.
Chỉ với vài dòng CSS trong Media Query, chúng ta đã thay đổi hoàn toàn bố cục header từ ngang sang dọc!
Ví dụ 2: Các Thẻ Nội Dung (Grid trên Desktop, Stack trên Mobile)
Hãy tạo một phần hiển thị các sản phẩm hoặc bài viết dưới dạng thẻ.
HTML:
<div class="cards-container">
<div class="card">Card 1</div>
<div class="card">Card 2</div>
<div class="card">Card 3</div>
<div class="card">Card 4</div>
<div class="card">Card 5</div>
<div class="card">Card 6</div>
</div>
CSS:
.cards-container {
display: flex; /* Kích hoạt flexbox */
flex-wrap: wrap; /* **Quan trọng:** Cho phép các item xuống dòng */
gap: 20px; /* Khoảng cách giữa các thẻ */
padding: 20px;
}
.card {
flex-basis: calc(33.33% - 20px); /* Ưu tiên mỗi thẻ chiếm khoảng 1/3 chiều rộng trừ khoảng cách */
flex-grow: 1; /* Cho phép thẻ giãn ra nếu còn không gian trống */
flex-shrink: 1; /* Cho phép thẻ co lại nếu không đủ không gian */
background-color: #e9e9e9;
padding: 20px;
box-sizing: border-box; /* Bao gồm padding và border trong kích thước */
text-align: center;
}
/* Responsive: Trên màn hình nhỏ hơn hoặc bằng 768px */
@media (max-width: 768px) {
.card {
flex-basis: 100%; /* Trên màn hình nhỏ, mỗi thẻ chiếm toàn bộ chiều rộng */
/* flex-grow và flex-shrink vẫn có thể giữ nguyên hoặc tùy chỉnh */
}
}
Giải thích:
.cards-container
là flex container vớiflex-wrap: wrap;
. Điều này rất quan trọng. Khi tổng kích thướcflex-basis
của các item vượt quá chiều rộng container, chúng sẽ tự động xuống dòng mới thay vì bị ép vào một hàng duy nhất và tràn ra ngoài.- Mỗi
.card
cóflex-basis: calc(33.33% - 20px);
. Đây là kích thước lý tưởng ban đầu, nhằm mục đích có 3 cột trên màn hình rộng (100% / 3 = 33.33%, trừ đigap
).flex-grow: 1
vàflex-shrink: 1
cho phép chúng co giãn nhẹ nhàng. - Trong Media Query cho màn hình nhỏ hơn 768px, chúng ta đặt
flex-basis: 100%;
cho.card
. Vìflex-wrap
đang hoạt động, mỗi thẻ sẽ chiếm 100% chiều rộng khả dụng và tự động nhảy xuống dòng mới, tạo thành một bố cục xếp chồng (stack).
Chỉ với việc thay đổi flex-basis
trong Media Query, chúng ta đã biến bố cục từ lưới (grid) 3 cột thành bố cục một cột trên mobile.
Ví dụ 3: Sidebar và Nội Dung Chính (Ngang trên Desktop, Sidebar xuống dưới trên Mobile)
Một bố cục phổ biến khác: sidebar ở một bên và nội dung chính ở bên còn lại.
HTML:
<div class="layout-container">
<aside class="sidebar">Sidebar Content</aside>
<main class="main-content">Main Content Area</main>
</div>
CSS:
.layout-container {
display: flex; /* Bố cục ngang trên desktop mặc định */
gap: 20px;
padding: 20px;
}
.sidebar {
flex: 0 0 250px; /* **Quan trọng:** Sidebar có kích thước cố định là 250px, không co (shrink) và không giãn (grow) */
background-color: #d3d3d3;
padding: 15px;
}
.main-content {
flex: 1; /* **Quan trọng:** Nội dung chính sẽ giãn ra để lấp đầy toàn bộ không gian còn lại */
background-color: #f9f9f9;
padding: 15px;
}
/* Responsive: Trên màn hình nhỏ hơn hoặc bằng 768px */
@media (max-width: 768px) {
.layout-container {
flex-direction: column; /* Chuyển sang bố cục cột */
}
.sidebar {
flex: unset; /* Bỏ cài đặt flex cố định */
width: 100%; /* Cho sidebar chiếm toàn bộ chiều rộng */
/* Có thể dùng 'order' để thay đổi vị trí nếu muốn sidebar xuống cuối */
/* order: 2; */
}
.main-content {
flex: unset; /* Bỏ cài đặt flex giãn */
width: 100%; /* Cho nội dung chính chiếm toàn bộ chiều rộng */
/* order: 1; */ /* Đặt nội dung chính lên trên sidebar */
}
}
Giải thích:
- Trên desktop,
.layout-container
là flex container theo hướngrow
(mặc định). .sidebar
sử dụng shorthandflex: 0 0 250px;
. Điều này có nghĩa làflex-grow: 0
,flex-shrink: 0
, vàflex-basis: 250px
. Sidebar sẽ luôn có kích thước 250px (trừ khi container quá hẹp vàflex-shrink
buộc nó co lại, nhưng ở đây ta đặtflex-shrink: 0
nên nó sẽ không co)..main-content
sử dụng shorthandflex: 1;
. Điều này có nghĩa làflex-grow: 1
,flex-shrink: 1
, vàflex-basis: 0%
. Nội dung chính sẽ luôn cố gắng lấp đầy không gian còn lại sau khi sidebar đã chiếm chỗ (nhờflex-grow: 1
).- Trong Media Query cho màn hình nhỏ,
flex-direction: column;
trên.layout-container
khiến sidebar và nội dung chính xếp chồng lên nhau. - Chúng ta reset thuộc tính
flex
trên cả sidebar và nội dung chính bằngflex: unset;
(hoặc có thể dùngflex-grow: 0; flex-shrink: 0; flex-basis: auto;
tùy trường hợp). Sau đó, dùngwidth: 100%;
để đảm bảo chúng chiếm hết chiều ngang. - Tùy chọn: Sử dụng thuộc tính
order
trên các flex item có thể thay đổi thứ tự hiển thị của chúng trên màn hình nhỏ mà không cần thay đổi thứ tự trong HTML. Ở đây, nếu đặtorder: 2;
cho sidebar vàorder: 1;
cho main content, sidebar sẽ xuất hiện sau nội dung chính trên mobile.
Ví dụ này cho thấy cách dùng flex-basis
, flex-grow
, flex-shrink
để định hình kích thước tương đối và cố định, sau đó thay đổi hướng và reset lại kích thước trong Media Query.
Comments