We're live-coding on Twitch! Join us!
Layouts of the Future - Flexbox Layout

Layouts of the Future - Flexbox Layout

Code Demo Project

What is CSS Layout?

CSS Layout(s) is basically a way for developers to use style sheets and define the layout of a web page.

If you are a Web Development veteran, then you might be familiar with using table to create a layout, no matter how complex. However, since the advent of HTML5, table based layouts have become a thing of the past.

Why should I care?

Thanks to HTML5, we no longer have to maintain multiple HTML files per device size for a single view. This gives us an added responsibility of making our web pages/views responsive from the get go.

CSS3 enables us in more ways than one to do this. Responsive web design along with server side components, a.k.a RESS, help us design and divide our web pages into logical pieces, which we then render as different parts of our layout.

What is the current approach?

One of the most popular approach for layouts is to divide the page into logical segments using percent based calculations and then adjusting the elements to fit within that predefined percent space.

What is the problem with it?

But that is not as easy as said. Why? Because the elements do not by default sit next to each other as we would want them, atleast not the ones which have their display property set to block anyway.

So how do we get this nice little layout? We rely on a couple of CSS properties to achieve this functionality:

  1. Applying CSS property float i.e float the elements to the left or right of each other
  2. Changing the display property of the elements to either inline or inline-block

But dont be fooled because these come with their own set of gotchas.

Option 1 collapses the element and its parent element which "contains" these floated elements (which in reality are outside the regular stack order now) hence breaking some other visual aspect of the view.

Option 2 has more limitations than you would think because inline elements are selective of where you can and cannot apply padding, margin, height or width. inline-block on the other hand is not considerate of the auto margins if you are intersted in using them.

And if thats not bad enough, you (or your browser) still need to do the division and/or calculation of your percent values into pixels which can considerably affect your render/paint times depending on the complexity of your layout.

How can we fix it?

But this is 2017, isnt there an easier way? Yes there is, and it has been around for quiet some time now, we just didn't give it the attention it deserves because of browser incompatabilities and support nightmares. We as a community need to adapt to the newer and better concepts so that we are always facing in the right direction, which is forward.

Now, before we start off investigating and exploring the new possibilities, lets take a look at what we want to build here. we are going to render a bunch of cards with user details on each of the cards. The data will be extracted from the API http://mocker.egen.io/.

Flexbox Layout

What is Flexbox Layout you say? Well let me start off by saying what it isn't. It is not a single property that you define and forget about which can magically position your elements in a way you wish for it to work. Its a module or more specifically a layout module. This means that it's a combination of properties that can be applied to the parent element and to its children which we hope to place in a certain fashion.

Now, lets try to create a neat little layout which can automatically adjust based on the number of elements that we try to display on the screen.

Parent

The parent element in this case is nothing but a standard container with its display set to flex i.e

<article class="flex-container">

</article>

And then we have its corresponding CSS class:

.flex-container {
    /* 
        display: set display flex 
        this is where we set the items to be flex property
    */
    display: flex; 
    /* 
        flex-direction: set flex direction 
        defining the direction of the items, could also be row-reverse, column or column-reverse
    */
    flex-direction: row;
    /* 
        flex-wrap: wrap if necessary
        prevent wrap by using nowrap instead, but this would create a horizontal scroll if overflown
        also available is wrap-reverse for reverse wrapping (RTL instead of LTR by default)
    */
    flex-wrap: wrap;
    /* 
        align-items: set positioning for items
        although center is most commonly used, you can also use 

        flex-start - aligns the items to the top and left of the parent
        flex-end   - aligns the items to the bottom and right of the parent
        baseline   - aligns items to where their text ends creating a more jaggered look
        stretch    - stretches the items to fill the space as necessary
        center     - aligns the items to the vertical center of the parent, +1 to this cause it aligns each
        item to the center of the parent vertically irrespective of the items own height.
    */
    align-items: center;
    /* 
        justify-content: placing the entire set of items
        although space-around is most commonly used, you can also use 

        flex-start    - aligns the entire group of items to the top and left of the parent
        flex-end      - aligns the entire group of items to the bottom and right of the parent
        stretch       - stretches the entire group of items to fill the space as necessary
        center        - aligns the entire group of items to the vertical center of the parent
        space-between - spaces out the rows with equal spacing between the rows only, first and 
                        last row are pressed to the edges.
        space-around -  spaces out the rows with equal spacing between the rows including the top of the first and 
                        bottom of the last row creating a uniform look for the entire set of items.
    */
    justify-content: space-around;


    /* Other Styles*/

    background: tomato;
    color: white;
    padding: 10px;
}

Child Elements

Now that we have the parent all set, lets take a look at the child elements:

<article class="flex-container">
    <h1 class="flex-item header">
      Flexbox Layout
    </h1>
    <div class="flex-item">

    </div>
    <div class="flex-item footer">
      Footer
    </div>
</article>

The flex-items here are what define the child elements of the container. Which has the CSS as follows:

.flex-item {
    /* 
        flex-grow: this can be given as a ratio, if given as 1, after taking up the required space,
                    the rest of the space is divided equally among all the elements

        in this case we ask items to not grow any more than needed 
    */
    flex-grow: 0;

    /* Other styles */
    border: 1px dashed purple;
    margin: 10px 5px;

}

.flex-item:nth-child(even) {
    /* for fun :) let the `even` elements grow upto twice of their counterpart `odd` using the remaining space
        for example, if 300px space is left on a row containing one `even` and one `odd` element, split the `extra`
        space in the ration of `2:1` or `200px` and `100px` respectively in this case.
     */
    flex-grow: 2;
}

.header, .footer {
    /* 
        flex-basis: set the width in percentage, px, em or rem, the calculation for the `remaining space` is done 
                    after excluding this space from the entire width of the row.

        here we ask the header and footer to take 100% width so that it takes the entire space and the next items starts
        from the next line
    */
    flex-basis : 100%;

     /* Other Styles*/
    text-align: center;
    padding: 10px 0;
    border: 1px dashed purple;
}

Extras

There are a lot of other CSS properties available that we could have used for the flex-items class, for example:

flex-shrink: Let the elements shrink if necessary (wraps the content in the items) align-self: FTW, change the alignment of an item (basically disobey the align-items property from the parent element)

Danger Zone

order: Lets us specify the order in which the elements are placed on the screen.

Although this may look seemingly innocent at first, incorrect usage can cause serious usability issues. For example you may render the elements in your HTML bottom to up and happily fix this by setting the order accordingly. This would throw off screen readers and is also symantically not correct. Use the order property wisely :).

Output

The output of the above code looks as follows:

An important thing to note is that now the view is responsive out of the box without requiring any media queries thanks to the flex-grow property on the child elements which automatically adjust the amount of space they take and share the remaining with other elements.

Tablet

Mobile

Conclusion

Although you cannot explicitly specify the number of rows and columns you want, you can still create a layout which spans in 2 dimensions when the size of the rows and its items are not fixed. A big win here is that we were able to avoid any dependency on the float property and the baggage that comes with it.

Code sample used in the article available here.

Like this article? Follow @ngsculptor on Twitter