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:

  1. 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.
  2. 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.
  3. 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.
  4. 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).
  5. align-items: Căn chỉnh các flex item dọc theo trục phụ (cross axis).
  6. flex-grow, flex-shrink, flex-basis (hoặc thuộc tính rút gọn flex): 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-headerdisplay: flex;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ách gap.
  • Khi màn hình nhỏ hơn 768px, @media (max-width: 768px) kích hoạt.
  • .site-header chuyển flex-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ển flex-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ới flex-wrap: wrap;. Điều này rất quan trọng. Khi tổng kích thước flex-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 .cardflex-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ừ đi gap). flex-grow: 1flex-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ướng row (mặc định).
  • .sidebar sử dụng shorthand flex: 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 đặt flex-shrink: 0 nên nó sẽ không co).
  • .main-content sử dụng shorthand flex: 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ằng flex: unset; (hoặc có thể dùng flex-grow: 0; flex-shrink: 0; flex-basis: auto; tùy trường hợp). Sau đó, dùng width: 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 đặt order: 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

There are no comments at the moment.