Bài 3.1: Giới thiệu Flexbox và thuộc tính cơ bản

Chào mừng trở lại với series blog về Lập trình Web Front-end! Sau khi đã làm quen với cấu trúc HTML và cách thêm "gia vị" bằng CSS, hôm nay chúng ta sẽ đến với một chủ đề cực kỳ quan trọngvô cùng hữu ích giúp bạn làm chủ việc sắp xếp các phần tử trên trang web: CSS Flexbox (Flexible Box Layout Module).

Nếu bạn đã từng "vật lộn" để căn giữa một div, hay đau đầu với floatclear để tạo ra các cột ngang, hoặc gặp khó khăn khi làm layout đáp ứng trên các kích thước màn hình khác nhau, thì xin chúc mừng, Flexbox chính là vị cứu tinh mà bạn tìm kiếm!

Flexbox là một mô hình bố cục một chiều (one-dimensional layout model). Nó được thiết kế để giúp bạn phân phối không gian giữa các phần tử con bên trong một phần tử cha và căn chỉnh chúng một cách hiệu quả và linh hoạt, bất kể kích thước hay thứ tự của chúng. Điều này làm cho việc xây dựng các thành phần giao diện phức tạp, đặc biệt là các thanh điều hướng (navigation bars), các gallery hình ảnh, hoặc bất kỳ nhóm items nào cần sắp xếp theo hàng hoặc cột, trở nên dễ dàng hơn bao giờ hết.

Bí quyết của Flexbox nằm ở mối quan hệ giữa phần tử chứa (container) và các phần tử con (items) bên trong nó. Khi bạn biến một phần tử cha thành flex container, tất cả các phần tử con trực tiếp của nó sẽ tự động trở thành flex items và tuân theo các quy tắc bố cục của Flexbox.

Các thuộc tính Flexbox được chia thành hai nhóm chính:

  1. Thuộc tính áp dụng cho Flex Container (phần tử cha).
  2. Thuộc tính áp dụng cho Flex Items (phần tử con).

Chúng ta hãy cùng đi sâu vào các thuộc tính cơ bản nhất của cả hai nhóm này nhé!

Các thuộc tính của Flex Container (Áp dụng cho phần tử cha)

Để một phần tử trở thành flex container, bạn chỉ cần áp dụng thuộc tính display cho nó.

1. display: flex; (hoặc inline-flex;)

Đây là điểm khởi đầu! Thuộc tính này biến phần tử cha thành một flex container, và các phần tử con trực tiếp của nó thành flex items.

  • flex: Tạo một flex container cấp block (chiếm toàn bộ chiều rộng có thể).
  • inline-flex: Tạo một flex container cấp inline (chỉ chiếm không gian vừa đủ cho nội dung).

Ví dụ:

<div class="container">
  <div class="item">Item 1</div>
  <div class="item">Item 2</div>
  <div class="item">Item 3</div>
</div>
.container {
  display: flex; /* Biến div.container thành flex container */
  border: 1px solid black; /* Chỉ để dễ nhìn */
}

.item {
  background-color: lightblue;
  margin: 5px;
  padding: 10px;
}

Giải thích: Ngay lập tức, các .item con bên trong .container sẽ tự động xếp thành một hàng ngang theo mặc định. Đây là hiệu quả của display: flex;.

2. flex-direction

Thuộc tính này xác định trục chính (main axis) mà các flex items sẽ được sắp xếp dọc theo, cũng như hướng (direction) của trục đó.

  • row (mặc định): Trục chính chạy theo chiều ngang (từ trái sang phải trong ngôn ngữ LTR).
  • row-reverse: Trục chính chạy theo chiều ngang, nhưng từ phải sang trái.
  • column: Trục chính chạy theo chiều dọc (từ trên xuống dưới).
  • column-reverse: Trục chính chạy theo chiều dọc, nhưng từ dưới lên trên.

Ví dụ với column:

.container {
  display: flex;
  flex-direction: column; /* Xếp items theo chiều dọc */
  height: 200px; /* Cần chiều cao để thấy rõ hiệu quả khi xếp dọc */
  border: 1px solid black;
}

.item {
  background-color: lightcoral;
  margin: 5px;
  padding: 10px;
}

Giải thích: Các items giờ đây xếp chồng lên nhau theo chiều dọc, bắt đầu từ trên cùng.

3. flex-wrap

Mặc định, flex items sẽ cố gắng nằm trên một dòng duy nhất (nowrap), có thể gây tràn ra ngoài container nếu không đủ không gian. Thuộc tính này kiểm soát hành vi của items khi chúng không đủ không gian trên trục chính: có nên xuống dòng/cột hay không?

  • nowrap (mặc định): Tất cả items cố gắng nằm trên một dòng/cột duy nhất.
  • wrap: Items sẽ xuống dòng (hoặc xuống cột) khi không đủ không gian.
  • wrap-reverse: Items sẽ xuống dòng (hoặc xuống cột) theo hướng ngược lại của trục phụ.

Ví dụ với wrap:

<div class="container-wrap">
  <div class="item-wrap">Item 1</div>
  <div class="item-wrap">Item 2</div>
  <div class="item-wrap">Item 3</div>
  <div class="item-wrap">Item 4</div>
  <div class="item-wrap">Item 5</div>
  <div class="item-wrap">Item 6</div>
</div>
.container-wrap {
  display: flex;
  flex-wrap: wrap; /* Cho phép items xuống dòng */
  width: 300px; /* Giới hạn chiều rộng để buộc items phải xuống dòng */
  border: 1px solid black;
}

.item-wrap {
  width: 100px; /* Mỗi item có chiều rộng cố định */
  background-color: lightgreen;
  margin: 5px;
  padding: 10px;
}

Giải thích: Khi tổng chiều rộng của các .item-wrap vượt quá 300px, các items sẽ tự động nhảy xuống dòng mới thay vì bị nén lại hoặc tràn ra ngoài.

4. justify-content

Thuộc tính này kiểm soát việc căn chỉnh các flex items dọc theo trục chính (main axis) của container. Nó xác định cách phân phối không gian trống giữaxung quanh các items.

  • flex-start (mặc định): Items được dồn về điểm bắt đầu của trục chính.
  • flex-end: Items được dồn về điểm kết thúc của trục chính.
  • center: Items được căn giữa trục chính.
  • space-between: Items được phân phối đều dọc theo trục chính; item đầu tiên nằm ở điểm bắt đầu, item cuối cùng nằm ở điểm kết thúc. Không có khoảng trống ở hai đầu container.
  • space-around: Items được phân phối đều dọc theo trục chính với khoảng trống bằng nhau ở hai bên mỗi item. Lưu ý: khoảng trống giữa hai items liền kề sẽ gấp đôi khoảng trống ở hai đầu container.
  • space-evenly: Items được phân phối sao cho khoảng trống giữa mọi cặp items liền kề, cũng như khoảng trống ở hai đầu container, là bằng nhau.

Ví dụ với center:

.container-justify {
  display: flex;
  justify-content: center; /* Căn giữa items theo chiều ngang (mặc định flex-direction: row) */
  border: 1px solid black;
}

.item-justify {
  background-color: lightgoldenrodyellow;
  margin: 5px;
  padding: 10px;
}

Giải thích: Các items giờ đây nằm gọn gàng ở giữa container theo chiều ngang. Thay center bằng space-between hoặc space-around để xem các hiệu ứng phân phối không gian khác nhau.

5. align-items

Trong khi justify-content căn chỉnh trên trục chính, align-items kiểm soát việc căn chỉnh các flex items dọc theo trục phụ (cross axis), tức là trục vuông góc với trục chính.

  • stretch (mặc định): Các items căng ra để lấp đầy container dọc theo trục phụ (nhưng vẫn tôn trọng min/max-width/height của item).
  • flex-start: Items được căn về điểm bắt đầu của trục phụ.
  • flex-end: Items được căn về điểm kết thúc của trục phụ.
  • center: Items được căn giữa trục phụ.
  • baseline: Items được căn chỉnh dựa trên đường baseline của văn bản bên trong chúng.

Ví dụ với center (kết hợp với justify-content: center để căn giữa cả 2 chiều):

.container-align {
  display: flex;
  justify-content: center; /* Căn giữa theo trục chính (ngang) */
  align-items: center; /* Căn giữa theo trục phụ (dọc) */
  height: 150px; /* Cần chiều cao để thấy hiệu quả căn dọc */
  border: 1px solid black;
}

.item-align {
  background-color: lightsalmon;
  margin: 5px;
  padding: 10px;
}

Giải thích: Với sự kết hợp của justify-content: centeralign-items: center, các items được căn chính giữa container theo cả chiều ngang và chiều dọc. Đây là kỹ thuật cực kỳ phổ biến để căn giữa mọi thứ trong CSS hiện đại!

6. align-content

Thuộc tính này kiểm soát việc căn chỉnh các dòng (lines) của flex items khi chúng được bao bọc (flex-wrap: wrap hoặc wrap-reverse) và có không gian trống trên trục phụ. Nó tương tự như justify-content nhưng áp dụng cho trục phụ và ảnh hưởng đến việc phân phối không gian giữa các dòng items.

  • stretch (mặc định): Các dòng căng ra để lấp đầy không gian trống trên trục phụ.
  • flex-start: Các dòng được dồn về điểm bắt đầu của trục phụ.
  • flex-end: Các dòng được dồn về điểm kết thúc của trục phụ.
  • center: Các dòng được căn giữa trục phụ.
  • space-between: Các dòng được phân phối đều; dòng đầu tiên ở điểm bắt đầu, dòng cuối cùng ở điểm kết thúc.
  • space-around: Các dòng được phân phối đều với khoảng trống ở hai bên mỗi dòng.

Lưu ý: align-content chỉ có hiệu lực khi flex container có nhiều hơn một dòng items (do flex-wrap) và có không gian trống trên trục phụ. Nếu chỉ có một dòng hoặc không có không gian trống, thuộc tính này sẽ không có tác dụng.

Ví dụ với centerwrap:

<div class="container-content">
  <div class="item-content">Item 1</div>
  <div class="item-content">Item 2</div>
  <div class="item-content">Item 3</div>
  <div class="item-content">Item 4</div>
  <div class="item-content">Item 5</div>
  <div class="item-content">Item 6</div>
</div>
.container-content {
  display: flex;
  flex-wrap: wrap;
  align-content: center; /* Căn giữa các dòng items trên trục phụ */
  height: 300px; /* Cần chiều cao đủ lớn và nhiều items để thấy wrap và căn chỉnh */
  width: 200px; /* Giới hạn chiều rộng để buộc wrap */
  border: 1px solid black;
}

.item-content {
  width: 80px;
  background-color: lightblue;
  margin: 5px;
  padding: 10px;
}

Giải thích: Vì container có chiều cao 300px nhưng các items chỉ chiếm một phần nhỏ trên trục dọc, align-content: center sẽ căn giữa toàn bộ khối các dòng items so với không gian trống còn lại theo chiều dọc.

Các thuộc tính của Flex Items (Áp dụng cho phần tử con)

Sau khi đã biến phần tử cha thành flex container, các phần tử con trực tiếp bên trong nó trở thành flex items và có thể nhận các thuộc tính sau:

1. order

Thuộc tính này cho phép bạn thay đổi thứ tự hiển thị trực quan của một flex item mà không làm thay đổi thứ tự của nó trong mã nguồn HTML. Items có order nhỏ hơn sẽ xuất hiện trước. Các items có cùng giá trị order sẽ được sắp xếp theo thứ tự trong mã nguồn HTML. Giá trị mặc định là 0.

Ví dụ:

<div class="container-order">
  <div class="item-order item1">Item 1</div>
  <div class="item-order item2">Item 2</div>
  <div class="item-order item3">Item 3</div>
</div>
.container-order {
  display: flex;
  border: 1px solid black;
}

.item-order {
  background-color: lightyellow;
  margin: 5px;
  padding: 10px;
}

.item1 { order: 2; } /* Item 1 hiển thị sau Item 2 và 3 (có order mặc định 0) */
.item2 { order: 1; } /* Item 2 hiển thị sau Item 3 */
.item3 { order: 0; } /* Item 3 hiển thị đầu tiên */

Giải thích: Mặc dù trong HTML thứ tự là Item 1, Item 2, Item 3, trên trình duyệt, thứ tự hiển thị sẽ là Item 3, Item 2, Item 1 dựa trên giá trị order được gán.

2. flex-grow

Nếu flex container có không gian trống trên trục chính, thuộc tính này xác định khả năng "lớn lên" của một flex item để chiếm phần không gian trống đó. Giá trị là một số không đơn vị biểu thị tỷ lệ. Mặc định là 0 (item không lớn lên).

Nếu tất cả items có flex-grow: 1, chúng sẽ chia sẻ không gian trống còn lại đều nhau. Nếu một item có flex-grow: 2, nó sẽ chiếm gấp đôi không gian trống so với một item có flex-grow: 1.

Ví dụ:

<div class="container-grow">
  <div class="item-grow item-a">Item A</div>
  <div class="item-grow item-b">Item B</div>
  <div class="item-grow item-c">Item C</div>
</div>
.container-grow {
  display: flex;
  width: 400px; /* Chiều rộng cố định để tạo không gian trống */
  border: 1px solid black;
}

.item-grow {
  background-color: lightcyan;
  margin: 5px;
  padding: 10px;
  /* Không đặt width cố định cho item để flex-grow có tác dụng */
}

.item-a { flex-grow: 1; } /* Chiếm 1 phần không gian trống */
.item-b { flex-grow: 2; } /* Chiếm 2 phần không gian trống (gấp đôi Item A và C) */
.item-c { flex-grow: 1; } /* Chiếm 1 phần không gian trống */

Giải thích: Giả sử tổng kích thước ban đầu của các items (dựa trên nội dung) nhỏ hơn 400px. Không gian trống còn lại sẽ được chia thành 1 + 2 + 1 = 4 phần. Item A và C mỗi cái sẽ mở rộng để chiếm 1/4 không gian trống, còn Item B sẽ mở rộng để chiếm 2/4 (tức 1/2) không gian trống.

3. flex-shrink

Nếu tổng kích thước của flex items lớn hơn kích thước của container trên trục chính, thuộc tính này xác định khả năng "co lại" của một flex item để ngăn tràn. Giá trị là một số không đơn vị biểu thị tỷ lệ co lại. Mặc định là 1 (item có thể co lại). Giá trị 0 nghĩa là item đó không co lại, giữ nguyên kích thước ban đầu của nó.

Ví dụ (ngăn Item Y co lại):

<div class="container-shrink">
  <div class="item-shrink item-x">Item X</div>
  <div class="item-shrink item-y">Item Y (Don't Shrink!)</div>
  <div class="item-shrink item-z">Item Z</div>
</div>
.container-shrink {
  display: flex;
  width: 200px; /* Giới hạn chiều rộng để buộc items phải co lại */
  border: 1px solid black;
}

.item-shrink {
  width: 100px; /* Cố tình đặt width lớn hơn khả năng chứa của container */
  background-color: lightpink;
  margin: 5px;
  padding: 10px;
}

.item-y { flex-shrink: 0; } /* Ngăn Item Y co lại */

Giải thích: Mặc dù mỗi item cố định có width: 100px (tổng cộng 300px), container chỉ rộng 200px. Theo mặc định (flex-shrink: 1), tất cả items sẽ co lại để vừa. Nhưng vì item-yflex-shrink: 0, nó sẽ giữ nguyên chiều rộng 100px của mình, trong khi Item X và Item Z phải co lại nhiều hơn để bù đắp không gian thiếu hụt.

4. flex-basis

Thuộc tính này xác định kích thước ban đầu của một flex item trước khi không gian còn lại được phân phối (theo flex-grow) hoặc bị thu hẹp (theo flex-shrink). Giá trị có thể là một độ dài (ví dụ: 100px, 50%, 3em) hoặc các từ khóa như auto (mặc định, sử dụng kích thước nội tại của nội dung hoặc bất kỳ width/height nào được đặt cho item) hoặc content (dựa trên kích thước của nội dung).

Ví dụ:

<div class="container-basis">
  <div class="item-basis item1-b">Item 1</div>
  <div class="item-basis item2-b">Item 2</div>
  <div class="item-basis item3-b">Item 3</div>
</div>
.container-basis {
  display: flex;
  border: 1px solid black;
}

.item-basis {
  background-color: lightsalmon;
  margin: 5px;
  padding: 10px;
}

.item1-b { flex-basis: 150px; } /* Kích thước ban đầu 150px */
.item2-b { flex-basis: 50px; }  /* Kích thước ban đầu 50px */
.item3-b { flex-basis: auto; }  /* Kích thước dựa vào nội dung */

Giải thích: Item 1 và 2 có kích thước ban đầu được thiết lập rõ ràng bằng flex-basis. Item 3 có kích thước ban đầu dựa trên độ rộng nội dung của nó (Item 3) vì flex-basisauto. Sau khi flex-basis được tính toán cho tất cả items, Flexbox sẽ sử dụng flex-growflex-shrink để phân phối không gian trống hoặc xử lý tràn.

5. flex (Shorthand)

Đây là thuộc tính viết tắt cho flex-grow, flex-shrink, và flex-basis, theo thứ tự đó: flex: <flex-grow> <flex-shrink> <flex-basis>;. Đây là thuộc tính rất phổ biến và được khuyến khích sử dụng thay vì viết riêng lẻ 3 thuộc tính trên.

  • Mặc định: flex: 0 1 auto; (không lớn lên, có thể co lại, kích thước ban đầu là auto).
  • Các giá trị tắt phổ biến:
    • flex: 1; tương đương với flex: 1 1 0%; (lớn lên, co lại, kích thước ban đầu là 0% - làm cho item cố gắng lấp đầy không gian).
    • flex: auto; tương đương với flex: 1 1 auto; (lớn lên, co lại, kích thước ban đầu auto - làm item co giãn nhưng dựa trên nội dung).
    • flex: none; tương đương với flex: 0 0 auto; (không lớn lên, không co lại, kích thước ban đầu auto - giữ nguyên kích thước nội tại).
    • flex: 0; tương đương với flex: 0 1 0%; (không lớn lên, co lại, kích thước ban đầu 0%).

Ví dụ:

.container-flex {
  display: flex;
  width: 400px; /* Tạo không gian trống */
  border: 1px solid black;
}

.item-flex {
  background-color: lightseagreen;
  margin: 5px;
  padding: 10px;
}

.item-flex:nth-child(1) { flex: 1; }          /* flex-grow: 1, flex-shrink: 1, flex-basis: 0% */
.item-flex:nth-child(2) { flex: 2 0 100px; } /* flex-grow: 2, flex-shrink: 0, flex-basis: 100px */
.item-flex:nth-child(3) { flex: none; }       /* flex-grow: 0, flex-shrink: 0, flex-basis: auto */

Giải thích: Item đầu tiên sẽ chiếm 1 phần không gian trống. Item thứ hai có kích thước ban đầu 100px, không co lại và nếu còn trống sẽ chiếm 2 phần không gian trống. Item thứ ba giữ nguyên kích thước ban đầu (dựa trên nội dung) và không co giãn. Sử dụng thuộc tính flex giúp code ngắn gọn và dễ đọc hơn.

6. align-self

Trong khi align-items áp dụng việc căn chỉnh trên trục phụ cho tất cả items trong container, align-self cho phép bạn ghi đè giá trị của align-items cho một item cụ thể.

  • auto (mặc định): Kế thừa giá trị từ align-items của container.
  • flex-start: Căn item về điểm bắt đầu của trục phụ.
  • flex-end: Căn item về điểm kết thúc của trục phụ.
  • center: Căn item giữa trục phụ.
  • stretch: Item căng ra để lấp đầy không gian trên trục phụ.
  • baseline: Căn item dựa trên đường baseline của văn bản bên trong nó.

Ví dụ:

<div class="container-self">
  <div class="item-self item-s1">Item 1</div>
  <div class="item-self item-s2">Item 2</div>
  <div class="item-self item-s3">Item 3</div>
</div>
.container-self {
  display: flex;
  align-items: flex-start; /* Container căn tất cả các items lên đầu trục phụ (dọc) */
  height: 150px; /* Cần chiều cao để thấy hiệu quả căn chỉnh dọc */
  border: 1px solid black;
}

.item-self {
  background-color: lightgoldenrod;
  margin: 5px;
  padding: 10px;
}

.item-s2 {
  align-self: center; /* Riêng Item 2 tự căn giữa trên trục phụ */
}

.item-s3 {
  align-self: flex-end; /* Riêng Item 3 tự căn xuống cuối trên trục phụ */
}

Giải thích: Container được đặt align-items: flex-start, khiến các Item 1, 2, 3 ban đầu đều nằm ở phía trên. Tuy nhiên, item-s2 với align-self: center sẽ tự di chuyển ra giữa, và item-s3 với align-self: flex-end sẽ di chuyển xuống cuối container trên trục dọc.

Comments

There are no comments at the moment.