Flexbox Fundamentals

Alignment Digest

Notes from MDN web docs taken while following What The Flexbox course from Wes Bos.

Introduction

Flexbox is short for Flexible Box Module. A layout model that allows easy control of space distribution and alignment between html elements [2].

Flexbox controls positioning in just one dimension at time (row or column). For two dimension control CSS Grid Layout comes in place [2].

Given the following template:

<body>
  <div class="container">
    <div class="box box-1">1</div>
    <div class="box box-2">2</div>
    <div class="box box-3">3</div>
    <div class="box box-4">4</div>
    <div class="box box-5">5</div>
    <div class="box box-6">6</div>
    <div class="box box-7">7</div>
    <div class="box box-8">8</div>
    <div class="box box-9">9</div>
    <div class="box box-10">10</div>
  </div>
</body>

The default behavior for the above divs, respecting the normal html document flow, is to be rendered from top to bottom, left to right and to take the entire body width, since its display property defaults to block.

Normal document flow

Flex Items

When display: flex is applied to the .container div, all direct child divs become flex-items, and gain new behavior [2]:

Display flex

Items shrink to fit

For visualization purposes let’s stretch the container to take the entire height.

Flex Container

display: flex makes the container expand the whole width available. As opposed to display: inline-flex, which makes the container collapse to the content’s width.

Display inline flex

Flex Direction

Once declared as a flex container, that element can be thought of in two axis. The main axis, defined by the flex-direction property. And the cross axis, which is perpendicular to the first [2].

There are four values for the flex-direction property: row, row-reverse, column and column-reverse.

The default value is row, and it sets the main axis horizontally, from left to right and the cross axis intercepts it vertically, from top to bottom. Analogously, the column value sets the axis vertically, from top to bottom and the cross axis from left to right. The reverse property on both options reverses main axis 180°. The cross axis remains the same [1][2].

The flex-items behavior for those values can be observed below:

Flex direction

Flex Wrap

flex-wrap is the property that deals with the flex items when space in the container isn’t big enough to fit them all [3].

By default flex-wrap is set to nowrap, which means that if the container cannot fit the items in a row with their original width, they will shrink to fit. If for some reason they are not able to shrink they will then, overflow the container [1][3].

By setting a 300px width to the items, the nowrap option outputs this result:

flex-wrap: nowrap

in which, each item has shrank to approximately 70px to fit the container.

When the property is updated to wrap, items’ width will now actually have their original value, 300px. Instead of overflowing the container, when the first row is not wide enough to fit 300px, the item will wrap to a new line [3]. Each line should be thought of as an individual flex container. The space distribution in one container does not affect the other ones neighboring it [2].

flex-wrap: wrap

But why are flex items taking the whole screen height? In the first section, container height was set to 100vh, so the space available is divided equally by the four rows necessary to fit the 300px items. Hadn’t we set the 100vh, container height would then respect the item content height, as in the image below [1]:

Wrap/height unset

Another option is wrap-reverse, that inverts the cross axis. Set from top to bottom by the flex-direction property, wrap-reverse transforms it into bottom to top [1].

flex-wrap: wrap-reverse

By inverting the main axis with flex-direction: column, the elements that don’t fit wrap to another column and remaining space is evenly divided [1].

flex-wrap/column

And the wrap-reverse option along with column direction inverts the cross axis from right to left, producing the following output:

flex-wrap/column

Since flexbox is a single dimension layout, on reversing the wrap, items are laid out from bottom to top (for row direction), but keep the left to right structure. Only the cross axis is altered.

Flex Flow

flex-direction and flex-wrap can be declared in a single property: flex-flow: [direction] [wrap] [2].

.flex-container {
  flex-flow : column wrap;
}

Gutter Between Items

Back to row/wrap. The entirety of the container can be filled by Applying width: 33.3333% to items:

item width: 33.33%

But if you wish to have a gap between the child divs, they won’t wrap as expected:

Broken gutter

That can be solved by calc() CSS function [1]:

.flex-item {
  width: calc(33.33333% - 40px);
  margin: 20px;
}

Gutter

To get rid of the space in the edge of the container use negative margin on the container [3]:

Gutter no edge

.flex-container {
  margin: -20px;
}

Order

The order property allows change to the visual order items appear in. Order is assigned to groups. By default all flex items are set to order: 0, which means that all items belong to the same group, and they will be positioned by source order. In case of two or more groups, groups are ordered relatively to their integer values [4].

In the example below, there are three ordinal groups, -1, 0 and 1, laid out in that order.

.box-3 { order:  1; }
.box-7 { order:  1; }
.box-8 { order: -1; }

Order

This property redistributes items visually, but keeps their original source position on interactions like traversing them with tab key. That may be taken under consideration if items order matter for accessibility. the same goes for flex-direction [4].

Order and Accessibility

Alignment

Alignment Digest

In Flexbox, items alignment and space distribution along axis can be controlled by four properties [5]:

justify-content

justify-content digest

Applied to container, justify-content handles items across the main axis. The six most-used options for its value are: flex-start, flex-end, center, space-around, space-between, space-evenly, being flex-start the default.

align-items

align-items digest

Also applied to container, the align-items property handles alignment along the cross axis. Its default value is stretch and the other option are flex-start, flex-end, center and baseline [5].

The stretch option makes all items stretch either to the container height, if set, or to the height of the tallest item [5]. The first picture shows container height set to 100vh, in the second one height not set.

align-content

align-content digest

The last of the four properties applied to flex container, align-content distributes spaces between flex lines in the cross axis. As the latter, its initial value is stretch and similarly justify-content, accepts the following options: flex-start, flex-end, center, space-around, space-between, space-evenly [5].

align-self

align-self digest

The align-items property actually works by setting align-self on all flex items inside a container. By setting align-self individually, it’s possible to override the general value. It accepts the same values as align-items and ‘auto’ [5].

The auto option resets the align-self to the value defined globally for the container by align-items [5].

Flexbox Sizing

Sizing and flexibility of items can be controlled by three properties: flex-grow, flex-shrink and flex-basis. All three act on the main axis [2].

flex-grow

The flex grow factor set by this property is a ratio that handles items size in relation to each other [7].

The default value is 0, which means that if there is available space, place it after the last item [1].

flex-grow-default

In the above example, direction is set to row, and each flex item width is set to 60px. Since the container is 980px wide, there is 680px of available space. That space is called positive free space [7].

By setting flex-grow to 1, the amount of positive free space is equally divided between flex items. Each item will get its width increased by 136px, totaling 196px [7].

flex-grow: 1

By appling flex-grow: 2 to the third item, it gets twice the amount of positive free space available, 286px than the remaining items, 173px [7].

The picture below shows items with the flex-grow property set to its content value.

flex-grow: variable

flex-shrink

flex-shrink handles items size, when there is not enough available space to fit them all in a container. So, it divides the negative free space among items by shrinking them [7].

The next picture shows the 980px container, which holds five 300px wide items. Since there is no room to accommodate the 1500px needed, the default flex shrink factor value of 1 makes every item shrink evenly to 196px.

flex-shrink-default

By setting a ratio of 2 for the third item, it becomes two times smaller than the rest.

flex-shrink-1

The last picture in this section shows each item holding its content value as flex shrink ratio.

flex-shrink-variable

flex-basis

flex-basis is the property that checks what is the size each item should have before it actually setting how much space available there will be. The default value is auto, and the item width is either explicitly set by width property, or it takes the content width. It also accepts pixels values [7].

The gif below shows a 800px wide container and five flex items set to flex-basis: 160px. This tells the browser: ideally there is enough room to place all items, respecting their 160px width, and there is no positive/negative free space. In case there is not enough space, since flex-shrink defaults to 1, all items are shrunk evenly. In case there is extra space, flex-grow defaults to 0 and the empty space is place after the last item.

flex-basis

Next gif shows item 1 set to flex-shrink: 10 and item 4 set to flex-grow: 10. For negative free space, item 1 get 10 times less width. And for positive free space item 4 gets 10 times more width than others.

flex-basis

flex-basis also accepts the value content, which regardless of width being set or not, the width considered for free space calculation is the item’s content. If you do not want to consider item width for that calculation set basis to 0.

flex

flex is the shorthand property for flex-grow, flex-shrink and flex-basis, in that order [2].

It accepts the following predefined values:

Autoprefixer

For cross browser compatibility is important to set properties with all necessary prefixes, in order to assure full support [1].

Auto prefixing every property by hand can be a very tedious task, besides making the styles very hard to maintain. Gulp is an alternative for automating those tasks.

In order to use Gulp, we have to add it to the project as a dependency. That’s done in the package.json file, responsible for tracking dependencies and its versions. To create the file type in the terminal [1]:

🌹  npm init

You will be prompted for project’s info. Just hit enter until it’s done. Output file will be something like this:

{
  "name": "project-name",
  "version": "1.0.0",
  "description": "Project description",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Author Name",
  "license": "ISC"
}

Install gulp globally:

🌹  npm install gulp -g

Install gulp and gulp-autoprefixer as a project dependencies:

🌹  npm install gulp --save-dev
🌹  npm install gulp-autoprefixer --save-dev

They should appear in package.json file under devDependencies key.

Create a gulpfile.js file:

🌹  touch gulpfile.js

Add the following [9]:

//gulpfile.js

var gulp = require('gulp');
var autoprefixer = require('gulp-autoprefixer');

gulp.task('styles', function() {
  return gulp.src('./styles.css')
      .pipe(autoprefixer({ browsers: ['last 2 versions'], cascade: false }))
      .pipe(gulp.dest('build'));
});

gulp extracts the content out of styles.css and passes it through gulp-autoprefixer. The result is placed under build folder.

References