Hướng dẫn toàn tập về Flexbox

August 31, 2017 (7y ago)

Background

The Flexbox Layout (Flexible Box) module nhằm cung cấp một cách hiệu quả hơn việc bố trí, sắp xếp và cân đối không gian giữa các phần tử trong một Container, ngay cả khi kích thước của các phần tử này chưa được biết hay nói cách khác là động (flex).

Mục đích chính của Flex layout là cung cấp một flex container có khả năng thay đổi các item bên trong nó chiều rộng, chiều cao, thứ tự của các item giúp cho Flex container này hiển thị tốt trên mọi thiết bị và mọi kích cỡ màn hình. Các item trong một flex container sẽ tự mở rộng để lấp đầy không gian hoặc tự co lại để ngăn chặn chống tràn (Overflow).

Quan trọng nhất, Flexbox layout dựa trên phương hướng bố trí ngược với cách bố trí bình thường (block theo chiều dọc và inline theo chiều ngang). Mặc dù những trang này hoạt động tốt, nhưng chúng thiếu tính linh hoạt để hỗ trợ các ứng dụng lớn hoặc phức tạp (đặc biệt liên quan đến thay đổi hướng, thay đổi kích cỡ, kéo dài, co lại, etc.).

Chú ý: Flexbox layout là thích hợp nhất với các thành phần của một ứng dụng, và bố trí quy mô nhỏ, trong khi đó Grid layout là dành cho bố trí quy mô lớn hơn.

Khái niệm cơ bản & Thuật ngữ

Flexbox là một module toàn bộ chứ không phải một thuộc tính duy nhất, nó bao gồm rất nhiều thứ kể cả các thuộc tính. Một số trong chúng là để được đặt cho container (element cha, được gọi là "flex container") trong khi những thứ khác là để được đặt cho children (gọi là "flex items").

Flex container là thành phần lớn bao quanh chứa các phần tử (Flex items) bên trong.

Flex items là phần tử con nằm trong thành phần lớn bao quanh gọi là những Flex items, các bạn có thể sắp xếp thứ tự của các Flex items này.

Nếu cách bố trí thông thường dựa trên luồng block và inline, flex layout là dựa vào "flex-flow directions". Nhìn vào những thông số kỹ thuật và giải thích ý tưởng của flex layout ở hình bên dưới.

Về cơ bản, những items sẽ được sắp xếp theo một trong hai trục chính (từ main-start đên main-end) hoặc trục dọc (từ cross-start đến cross-end).

Properties for the Parent (flex container)

display

Đây là định nghĩa một flex container; inline hoặc block tùy thuộc vào giá trị nhất định. Nó cho phép một flex context cho tất cả con trực tiếp của nó.

.container {
  display: flex; /* or inline-flex */
}

Chú ý rằng CSS columns không có tác dụng trong vùng flex container.

flex-direction

Định nghĩa này cho trục chính, do đó định nghĩa direction flex items đặt ở trong flex container. Flexbox là một khái niệm layout đơn hướng. Hãy nghĩ flex items là chủ yếu đặt trong hàng ngang hoặc cột dọc.

.container {
  flex-direction: row | row-reverse | column | column-reverse;
}

flex-wrap

Mặc định, flex items sẽ cố gắng đặt vào 1 dòng. Bạn có thể thay đổi điều đó và những items sẽ wrap khi có thuộc tính này.

.container {
  flex-wrap: nowrap | wrap | wrap-reverse;
}

Có một vài ví dụ thực tế của flex-wrap ở đây.

flex-flow (Áp dụng cho: phần tử cha flex container)

Đây là cách viết gọn của thuộc tính flex-direction và flex-wrap, chúng cùng nhau định nghĩa flex container's trục chính hay trục dọc. Mặc định là row nowrap.

flex-flow: <‘flex-direction’> || <‘flex-wrap’>;

justify-content

Điều này xác định sự sắp xếp theo trục chính. It helps distribute extra free space left over when either all the flex items on a line are inflexible, or are flexible but have reached their maximum size. It also exerts some control over the alignment of items when they overflow the line.

.container {
  justify-content: flex-start | flex-end | center | space-between | space-around
    | space-evenly;
}

align-items

This defines the default behaviour for how flex items are laid out along the cross axis on the current line. Think of it as the justify-content version for the cross-axis (perpendicular to the main-axis).

.container {
  align-items: flex-start | flex-end | center | baseline | stretch;
}

align-content

This aligns a flex container's lines within when there is extra space in the cross-axis, similar to how justify-content aligns individual items within the main-axis.

Note: this property has no effect when there is only one line of flex items.

.container {
  align-content: flex-start | flex-end | center | space-between | space-around |
    stretch;
}

Thuộc tính của Children (flex items)

order

Mặc định các phần tử được sắp xếp theo giá trị nguồn html, với thuộc tính order chúng ta có thể xắp xếp lại thứ tự các phần tử theo ý muốn mà ko cần thay đổi giá trị nguồn html.

.item {
  order: <integer>;
}

flex-grow

This defines the ability for a flex item to grow if necessary. It accepts a unitless value that serves as a proportion. It dictates what amount of the available space inside the flex container the item should take up.

If all items have flex-grow set to 1, the remaining space in the container will be distributed equally to all children. If one of the children has a value of 2, the remaining space would take up twice as much space as the others (or it will try to, at least).

.item {
  flex-grow: <number>; /* default 0 */
}

Số âm không hợp lệ với thuộc tính này.

flex-shrink

Định nghĩa một flex item có thể khả năng thu nhỏ nếu cần thiết.

.item {
  flex-shrink: <number>; /* default 1 */
}

Số âm không hợp lệ với thuộc tính này.

flex-basis

Định nghĩa kích thước mặc định của một phần tử trước khi phân bố không gian của phần còn lại. Nó có thể là một chiều dài (e.g. 20%, 5rem, etc.) hay một từ khóa. Từ khóa auto có nghĩa là "nhìn vào thuộc tính width hoặc height của tôi" (which was temporarily done by the main-size keyword until deprecated). The content keyword means "size it based on the item's content" - this keyword isn't well supported yet, so it's hard to test and harder to know what its brethren max-content, min-content, and fit-content do.

.item {
  flex-basis: <length> | auto; /* default auto */
}

If set to 0, the extra space around content isn't factored in. If set to auto, the extra space is distributed based on its flex-grow value. See this graphic.

flex

Đây là viết tắt cho flex-grow, flex-shrink và flex-basis. Các thông số thứ hai và thứ ba (flex-shrink và flex-basis) là tùy chọn. Mặc định là 0 1 auto.

.item {
  flex: none | [ < 'flex-grow' > < 'flex-shrink' >? || < 'flex-basis' >];
}

It is recommended that you use this shorthand property rather than set the individual properties. The short hand sets the other values intelligently.

align-self

This allows the default alignment (or the one specified by align-items) to be overridden for individual flex items.

Please see the align-items explanation to understand the available values.

.item {
  align-self: auto | flex-start | flex-end | center | baseline | stretch;
}

Note that float, clear and vertical-align have no effect on a flex item.

Examples

Let's start with a very very simple example, solving an almost daily problem: perfect centering. It couldn't be any simpler if you use flexbox.

.parent {
  display: flex;
  height: 300px; /* Or whatever */
}

.child {
  width: 100px; /* Or whatever */
  height: 100px; /* Or whatever */
  margin: auto; /* Magic! */
}

This relies on the fact a margin set to auto in a flex container absorb extra space. So setting a vertical margin of auto will make the item perfectly centered in both axis.

Now let's use some more properties. Consider a list of 6 items, all with a fixed dimensions in a matter of aesthetics but they could be auto-sized. We want them to be evenly and nicely distributed on the horizontal axis so that when we resize the browser, everything is fine (without media queries!).

.flex-container {
  /* We first create a flex layout context */
  display: flex;

  /* Then we define the flow direction and if we allow the items to wrap 
   * Remember this is the same as:
   * flex-direction: row;
   * flex-wrap: wrap;
   */
  flex-flow: row wrap;

  /* Then we define how is distributed the remaining space */
  justify-content: space-around;
}

Done. Everything else is just some styling concern. Below is a pen featuring this example. Be sure to go to CodePen and try resizing your windows to see what happens.

See the Pen Demo Flexbox 1.

Let's try something else. Imagine we have a right-aligned navigation on the very top of our website, but we want it to be centered on medium-sized screens and single-columned on small devices. Easy enough.

/* Large */
.navigation {
  display: flex;
  flex-flow: row wrap;
  /* This aligns items to the end line on main-axis */
  justify-content: flex-end;
}

/* Medium screens */
@media all and (max-width: 800px) {
  .navigation {
    /* When on medium sized screens, we center it by evenly distributing empty space around items */
    justify-content: space-around;
  }
}

/* Small screens */
@media all and (max-width: 500px) {
  .navigation {
    /* On small screens, we are no longer using row direction but column */
    flex-direction: column;
  }
}

See the Pen Demo Flexbox 2.

Let's try something even better by playing with flex items flexibility! What about a mobile-first 3-columns layout with full-width header and footer. And independent from source order.

.wrapper {
  display: flex;
  flex-flow: row wrap;
}

/* We tell all items to be 100% width */
.header,
.main,
.nav,
.aside,
.footer {
  flex: 1 100%;
}

/* We rely on source order for mobile-first approach
 * in this case:
 * 1\. header
 * 2\. nav
 * 3\. main
 * 4\. aside
 * 5\. footer
 */

/* Medium screens */
@media all and (min-width: 600px) {
  /* We tell both sidebars to share a row */
  .aside {
    flex: 1 auto;
  }
}

/* Large screens */
@media all and (min-width: 800px) {
  /* We invert order of first sidebar and main
   * And tell the main element to take twice as much width as the other two sidebars 
   */
  .main {
    flex: 2 0px;
  }

  .aside-1 {
    order: 1;
  }
  .main {
    order: 2;
  }
  .aside-2 {
    order: 3;
  }
  .footer {
    order: 4;
  }
}

See the Pen Demo Flexbox 3.

Prefixing Flexbox

Flexbox requires some vendor prefixing to support the most browsers possible. It doesn't just include prepending properties with the vendor prefix, but there are actually entirely different property and value names. This is because the Flexbox spec has changed over time, creating an "old", "tweener", and "new" versions.

Perhaps the best way to handle this is to write in the new (and final) syntax and run your CSS through Autoprefixer, which handles the fallbacks very well.

Alternatively, here's a Sass @mixin to help with some of the prefixing, which also gives you an idea of what kind of things need to be done:

@mixin flexbox() {
  display: -webkit-box;
  display: -moz-box;
  display: -ms-flexbox;
  display: -webkit-flex;
  display: flex;
}

@mixin flex($values) {
  -webkit-box-flex: $values;
  -moz-box-flex: $values;
  -webkit-flex: $values;
  -ms-flex: $values;
  flex: $values;
}

@mixin order($val) {
  -webkit-box-ordinal-group: $val;
  -moz-box-ordinal-group: $val;
  -ms-flex-order: $val;
  -webkit-order: $val;
  order: $val;
}

.wrapper {
  @include flexbox();
}

.item {
  @include flex(1 200px);
  @include order(2);
}

Related Properties

Bugs

Flexbox is certainly not without its bugs. The best collection of them I've seen is Philip Walton and Greg Whitworth's Flexbugs. It's an open source place to track all of them, so I think it's best to just link to that.