Home > Designing, Others > Modern web layout with flexbox

Modern web layout with flexbox

September 22nd, 2015 Leave a comment Go to comments

Let me say this first: flexbox is easy. Whether it’s because of early-adoption issues, or because we’ve learned to think in terms of floats and blocks, I often hear from web designers that they think Flexbox is complex to implement. But I repeat: flexbox is easy.

There’s one trick to understanding flexbox which I’ll teach you, after which all will be clear. But first, I want to talk about what flexbox is.

What is flexbox?

Flexbox (or the Flexible Box Layout Module to give it its correct name) is a set of CSS properties that combine to lay out content in a responsive way.

Flexbox is the first CSS layout technique that works for the modern web. It’s fair to say that until flexbox was supported, there was no layout technique that worked well with responsive web design.

Plenty of hacks have been floating around, like setting elements to display:inline-block and allowing them to wrap. But this presented a number of problems for flexible sites, not least the lack of control and the knock-on effect of elements resizing. For this reason, many responsive websites still use JavaScript to position content.

What is flexbox for?

Flexbox is described by the W3C as being intended for laying out UI elements—things like menu items—it’s not intended for laying out whole pages.

The reason it’s not intended for layout out whole pages, is that flexbox has a companion CSS module, the Grid Layout Module which is intended for layout. Grid Layout is considered to be the most appropriate technique for full page layouts. Sadly there is very limited support for grid at the present time, and by ‘limited’ I mean ‘none.’ Even the latest version of Chrome fails to support it without flags.

Happily browser support for flexbox is much more comprehensive. And because it solves so many modern layout problems, flexbox is an ideal tool until grid is finally adopted.

Styling components

Flexbox is ideal for styling components on a page. The real power of flexbox, and the reason it works so much better than other layout options, comes from declaring styles on groups of items instead of individual items.

When it comes to layout, we rarely need to position a single item—if we’re planning to do so, it’s much better to use positioning. Flexbox works best when it controls how groups of elements relate to each other.

Always design a thing by considering it in its next larger context – a chair in a room, a room in a house, a house in an environment, an environment in a city plan. — Eliel Saarinen

As such, the flexbox specification is divided into two parts, one set of properties that refer to the container element, and one set of properties that refer to the elements within.

Browser support

Browser support is, typically, a complex question. Made all the more complex for flexbox because flexbox has several different syntaxes. Early indecision, independent development by some browser suppliers, and an iterative process, has left us with several different versions of flexbox that all need to be catered to.

Happily, with the addition of some browser prefixes for legacy browsers, we can now rely on it. According to caniuse.com, of current mainstream browsers only IE9 will present a problem for us.

Fortunately, like all CSS, if flexbox fails, it fails silently. Meaning that IE9 will just ignore it.

Flexbox versions

Thanks to a staggering lack of cooperation between various branches of committees and corporations, flexbox’s implementation has very nearly rivalled the chaos of the early ’00s when every single browser implemented every single property uniquely, and incorrectly, at the same time. Giving everyone the benefit of the doubt, flexbox’s three implementations were simply an iterative approach that has resulted in the most helpful common solution.

There are three versions of the flexbox syntax that you need to be aware of: old, inbetween, and new. Different browsers support different syntaxes, IE10 for example supports the inbetween syntax.

Just like browser prefixes, we write Flexbox syntax from oldest to newest, so that the newest supported syntax overrides the older syntaxes.

For example, we write display:flex like so:

display:-webkit-box; /*old prefixed for webkit*/
display:-moz-box; /*old prefixed for mozilla*/
display:-ms-flexbox; /*inbetween prefixed for ie*/
display:-webkit-flex; /*new prefixed for webkit*/
display:flex; /*new*/

In the interests of clarity, I’m not going to dive into the older syntaxes, or debate the merits of pre-processors, post-processors, and different conversion scripts. In all of my examples I’m going to use the correct, new syntax.

When it comes to production code I use a series of Sass mixins to extend flexbox support to older browsers.

There’s an excellent Sass mixin here, which you can either use to prevent your code bloating, or extract the legacy syntax.

The secret to understanding flexbox

The secret of flexbox, is that it isn’t a box at all. Up until now, all layout on the Web used a cartesian system of x and y, to plot points. Flexbox, by contrast, is a vector.

Sounds great, right? Flexbox is a vector, meaning we define a length and we define an angle. We can change the angle, without affecting the length; and we can alter the length, without affecting the angle.

This new approach means that some of flexbox’s terminology is initially complex. For example, when we align items to the top of a flex container, we use flex-start, not top—because if we change the orientation, flex-start will still apply, but top will not.

Once you understand this approach, much of flexbox is demystified.

How to use flexbox containers

Flexbox’s syntax is split into two groups: those properties that control the container, and those properties that control the container’s direct descendants. The former is the most useful, so let’s start there.

Getting started

Let’s say we want to space out items within a block. We want them spaced evenly. Using flexbox it’s really easy to do. The first thing we need is some markup to try this out on:

<!DOCTYPE html>
 <html>
 <head>
 <meta charset="utf-8">
 <title>Flexbox Layout</title>
 
 <style>
 
 .house {
 background:#FF5651;
 }
 
 .room {
 background:#00AAF2;
 width:90px;
 height:90px;
 }
 
 </style>
 
 </head>
 
 <body>
 
 <div class="house">
 <div class="room kitchen"></div>
 <div class="room washroom"></div>
 <div class="room lounge"></div>
 <div class="room bedroom"></div>
 <div class="room bedroom"></div>
 </div>
 
 </body>
 </html>

It’s a simple HTML page, with a house div, that contains five room divs each of which has a class identifying their function, kitchen, washroom, lounge, and bedroom.

What we’re going to do is set the house to be a flexbox container. Any flexbox properties we set on house will then be applied to its children (the rooms).

Creating a flexible box

In order to layout the rooms we need to first declare house as a flexbox container, and then tell it how we want the child elements arranged.

To declare house as a flexbox container we use the following code:

.house {
background:#FF5651;
display:flex;
}

And that’s it. We’ve created a flexible box. All we need to do now is tell flexbox how to layout the rooms. We can do that using the justify-content property.

justify-content has five possible values: center, flex-start, flex-end, space-around, and space-between. These center the items, align them to the start (which is either top or left, as we’ll discuss below), the end (which is either the bottom or the right), spaces them with equal space around each item, or with equal space between each item, respectively.

We’ll use space-around:

.house {
background:#FF5651;
display:flex;
justify-content: space-around;
}

Here’s a demo. Notice how there’s double the space between the items as there is on the two outer edges? That’s because each item has the same space to the left and to the right. If we had used space-between instead there would be no space on the edges.

I’m not sure about you, but the washroom in my house is a little smaller than the lounge, and although I have a small kitchen, I’d love a much larger one. So let’s make some additions to our code to reflect that:

.kitchen {
width:300px;
height:300px;
}

.washroom {
width:45px;
height:60px;
}

As you can see in this example, our rooms are still evenly spaced. Even though we’ve changed the size. Flexbox is brilliant at organizing content.

Changing the angle

As we discussed above, one of the reasons that flexbox works so well for designers is that it is described as a vector. At present, flexbox only supports four angles: 0, 90, 180, and 270 degrees. So it’s a lot simpler to think of them as an axis.

To change the axis, we use the flex-direction property, which has four possible values: row (the default — as you can see in the examples above, our rooms are laid out left to right), row-reverse, column, and column-reverse.

If we change the flex-direction to column, you’ll see in this demo that the rooms are now laid out vertically, top to bottom:

.house {
background:#FF5651;
display:flex;
justify-content: space-around;
flex-direction:column;
}

As I’m sure you can guess, row-reverse and column-reverse reverse the order of the items in your flex container.

Flex vs. Flex-inline

You’ll notice in the last example that while house stretches all the way across the viewport, it does stretch to the full height, even when set to flex-direction:column.

In order to play nicely with CSS’ cartesian-based coordinate system the flexbox container is treated as a block level element.

Just as display:block has a companion display:inline-block, so to display:flex has display:inline-flex.

inline-flex is currently most useful if you’re dropping flexbox elements into an existing layout and you need it to play nice with floats and positioned elements.

Cross-axis alignment

As we’ve seen, flexbox doesn’t mind whether it lays out horizontally or vertically, whichever direction you choose is considered to be flexbox’s primary axis.

But flexbox also allows us to control position along the opposite, secondary axis.

Position on the secondary axis is controlled by the align-items property; it has five possible values: baseline, center, flex-end, flex-start, and stretch.

baseline will normally be the most useful option. It ensures that the baselines of text line up. To test this, we need to make some modifications to our HTML file:

<!DOCTYPE html>
 <html>
 <head>
 <meta charset="utf-8">
 <title>Flexbox Layout</title>
 <link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
 <style>
 
 body
 {
 font-family: 'Open Sans', sans-serif;
 color:#FFFFFF;
 }
 
 .house {
 background:#FF5651;
 display:flex;
 justify-content: space-around;
 }
 
 .room {
 background:#00AAF2;
 width:90px;
 height:90px;
 }
 
 .kitchen {
 width:300px;
 height:300px;
 font-size:3em;
 }
 
 .washroom {
 width:60px;
 height:60px;
 
 font-size:0.4em;
 }
 
 </style>
 </head>
 <body>
 
 <div class="house">
 <div class="room kitchen">Kitchen</div>
 <div class="room washroom">Washroom</div>
 <div class="room lounge">Lounge</div>
 <div class="room bedroom">Bedroom</div>
 <div class="room bedroom">Bedroom</div>
 </div>
 
 </body>
 </html>

You’ll see I’ve added some text, and imported, then applied a Google font. I’ve also changed the font-size in the kitchen and washroom so there’s a clear distinction.

When we set the align-items property to baseline you’ll see in this demo that the baseline of the text matches up.

.house {
background:#FF5651;
display:flex;
justify-content:space-around;
align-items:baseline;
}

align-items:center center aligns, either vertically (if flex-direction:row, or flex-direction:row-reverse) or horizontally (if flex-direction:column, or flex-direction:column-reverse). Here’s a demo.

align-items:flex-start aligns to the top, and align-items:flex-end aligns to the bottom (if flex-direction:row, or flex-direction:row-reverse), or to the left and right (if flex-direction:column, or flex-direction:column-reverse). Here’s a demo.

stretch, is another highly useful option. stretch will resize the items, so that each one is the same height (they’ll grow, not shrink). Here’s a demo.

In our other examples, we’ve explicitly set the height of the items, which is enough to override flexbox, so to see align-items:stretch work correctly you’ll need to remove the height from all the classes except one.

Wrapping onto multiple lines

So far we’ve used flexbox to layout elements so that they resize across the space, but what happens when the content of the items is too large to fit onto one line?

To demo this, I’m going to increase the size of my rooms:

<!DOCTYPE html>
 <html>
 <head>
 <meta charset="utf-8">
 <title>Flexbox Layout</title>
 <link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
 <style>
 
 body
 {
 font-family: 'Open Sans', sans-serif;
 color:#FFFFFF;
 }
 
 .house {
 background:#FF5651;
 display:flex;
 justify-content:space-around;
 align-items:baseline;
 }
 
 .room {
 background:#00AAF2;
 width:180px;
 height:90px;
 font-size:1.5em;
 }
 
 .kitchen {
 width:450px;
 height:300px;
 font-size:4.5em;
 }
 
 .washroom {
 width:120px;
 height:60px;
 font-size:0.6em;
 }
 
 </style>
 </head>
 <body>
 
 <div class="house">
 <div class="room kitchen">Kitchen</div>
 <div class="room washroom">Washroom</div>
 <div class="room lounge">Lounge</div>
 <div class="room bedroom">Bedroom</div>
 <div class="room bedroom">Bedroom</div>
 </div>
 
 </body>
 </html>

Here’s how it looks now.

If you shrink down your browser window you’ll see that the content gets cut off, as there isn’t space for it. Happily flexbox defines a flex-wrap property for just this eventuality, which has three possible values: no-wrap (the default), wrap, and wrap-reverse.

wrap causes any items that would extend beyond the container to wrap onto a subsequent line, just as a line of text wraps.

.house {
background:#FF5651;
display:flex;
justify-content:space-around;
align-items:baseline;
flex-wrap:wrap;
}

If you squish down your browser you’ll see in this demo the items now wrap onto new lines. Notice how the align-items:baseline property is still applied? Flexbox’s real power is in combining its properties.

One important concept, that I’ll cover later, is that flex-wrap:wrap-reverse does not reverse the order of the items, it causes any items that would extend beyond the container to wrap onto a prior line, as seen in this demo.

Using flexbox’s shorthand properties

Like many CSS properties, some flexbox properties have shorthand versions that are implemented more consistently by browser manufacturers.

The flex-flow property is shorthand for the flex-direction and flex-wrap properties. So instead of writing:

flex-direction:row-reverse;
flex-wrap:wrap;

We can use the shorthand:

flex-flow:row-reverse wrap;

Cross-axis alignment (part 2)

Above, we used the align-items property to align items to the baseline, position, and stretch items. As you can see in the last example, that is still occurring, but it occurs on a line by line basis.

When our items have been wrapped, we have the option to set how they will be laid out on the secondary axis if the container is larger than required.

If we set the min-height of our house to 1200px, our layout looks like this.

.house {
background:#FF5651;
min-height:1200px;
display:flex;
justify-content:space-around;
flex-wrap:wrap;
}

We can now use the align-content property to lay out the items across the full secondary axis.

The align-content property has six settings: center, flex-end, flex-start, space-around, space-between, and stretch. These values deliver the same results as elsewhere: centering, aligning to one end, spacing evenly, or stretching.

Here’s a demo with the property set to space-around.

How to use flexbox items

Until now, all of the properties we’ve looked at have been set on the container element. In order to fine-tune items flexbox also provides us with a number of properties that can be set on individual items.

To clarify this, let’s remove some of our code:

<!DOCTYPE html>
 <html>
 <head>
 <meta charset="utf-8">
 <title>Flexbox Layout</title>
 <link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
 <style>
 
 body
 {
 font-family: 'Open Sans', sans-serif;
 color:#FFFFFF;
 }
 
 .house {
 background:#FF5651;
 
 display:flex;
 
 }
 
 .room {
 background:#00AAF2;
 height:90px;
 }
 
 .kitchen {
 height:300px;
 }
 
 .washroom {
 height:60px;
 }
 
 </style>
 </head>
 <body>
 
 <div class="house">
 <div class="room kitchen">Kitchen</div>
 <div class="room washroom">Washroom</div>
 <div class="room lounge">Lounge</div>
 <div class="room bedroom”>Master Bedroom</div>
 <div class="room bedroom">Bedroom</div>
 </div>
 
 </body>
 </html>

Here’s how it looks.

Controlling individual items

The first flexbox item property is the confusingly named flex property. flex is a value of the display property on the container, but it is also a property in its own right.

The flex property is shorthand for the flex-grow, flex-shrink, and flex-basis properties. The role of these properties is to stretch or squash the size of content so that it fills the space entirely.

The flex-grow property tells the items that they can enlarge, if required, so that the items fill the width (for flex-direction:row) and the height (for flex-direction:column) fully.

The flex-grow property takes a ratio. So if we set the flex-grow property to 1, all of our items will be the same size as you can see here.

.room {
background:#00AAF2;
height:90px;
flex-grow:1;
}

In addition to taking an equal size, we can also set different ratios. I still want my kitchen to be bigger than my washroom, so let’s fix that in this demo.

.kitchen {
height:300px;
flex-grow:2;
}

flex-shrink works exactly the same as flex-grow except that it shrinks the item if required, instead of expanding it.

If you resize the browser you’ll see that at different sizes, my kitchen isn’t exactly double the width of the other rooms. That’s due to the flex-basis property.

flex-basis determines how the flex-grow and flex-shrink properties are calculated. It has 2 values: auto (the default), and 0.

If set to 0, the whole of the item is taken into account. If set to auto it is the padding around the item’s content that is resized. In other words, 0 ignores the size of the item’s content when calculating, and auto takes it into account. There’s a handy diagram provided by the W3C that explains this.

Here’s a demo of what happens when I set flex-basis:0 on the above example.

flex-basis:0 is particularly useful if you want to space items evenly, regardless of their content.

As stated above, the flex property is a shorthand for these properties, and you’ll find it’s the most reliable way of setting these values.

The order of values is flex-grow, flex-shrink, flex-basis. So, to set a grow value of 2, a shrink value of 1 and a basis of 0, you’d use:

.room {
background:#00AAF2;
height:90px;
flex: 2 1 0;
}

Controlling order with flexbox

One of the best features of flexbox is its ability to change the order of items, without affecting the markup; this is particularly useful for accessibility, as it allows us to code markup appropriately, and then display it optimally.

To do this we use the item’s order property.

Looking at our example, the washroom is right next to the kitchen. I’d rather it was next to the bedrooms, so I can hop straight into the shower of a morning, so let’s move it. It’s currently the 2nd item, we want it to be the third. So we set its order property accordingly in this demo.

.washroom {
height:60px;
order:2;
}

See how it hopped all the way to the end of the row? It’s next to the bedrooms, but not how we intended. This is a major gotcha in flexbox, because many developers (myself included) initially expect order to operate like an array.

order actually works like CSS’ z-index, meaning that if you don’t explicitly set an order for an item it is assumed to be lower in the order than an item for which a value has been explicitly set.

We want the washroom to swap with the lounge, so in this demo the only other items that need an order are the bedrooms.

.washroom {
height:60px;
order:2;
}
 
.bedroom {
order:3;
}

Notice how both bedrooms share the same order number? In cases like those the browser will fall back on the DOM order.

Breaking alignment

The final item property is align-self. It is used to change the item’s alignment on the secondary axis. align-items is used to align all of the items, and this property gives you the option to opt-out of that blanket setting.

It takes the same five values as the equivalent property on the container: baseline, center, flex-end, flex-start, and stretch.

Here’s a demo of the washroom re-aligned.

.washroom {
height:60px;
order:2;
align-self:flex-end;
}

Modern web layout with flexbox

Flexbox is a powerful option for laying out content. Although it’s not meant for laying out whole pages, its superior support to Grid Layout makes it a great option, especially for mobile-first websites.

To demonstrate, we’re going to put together a simple page that uses some of the techniques described above.

Getting started

First up is our markup, and some basic styles for the page.

<!DOCTYPE html>
 <html>
 <head>
<meta charset="utf-8">
 <title>Flexbox Demo</title>
 <link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
 <style>
 
 body {
 margin:0 2em;
 font-family: 'Open Sans', sans-serif;
 } 
 
 a {
 text-decoration:none;
 }
 
 #header > a {
 padding:1em 0;
 }
 
 #header > .logo {
 font-size:2.4em;
 }
 
 ul {
 /* remove list styling */
 list-style: none;
 margin:0;
 padding:0;
 }
</style>
</head>
 <body>
 
 <div id="page">
 
 <ul id="news">
 <li class="newsItem">A</li>
 <li class="newsItem">B</li>
 <li class="newsItem">C</li>
 <li class="newsItem">D</li>
 <li class="newsItem">E</li>
 <li class="newsItem">F</li>
 <li class="newsItem">G</li>
 </ul>
 
 <nav id="header">
 <a class="logo" href="#">Logo</a>
 <a href="#">Ultricies</a>
 <a href="#">Ridiculus</a>
 <a href="#">Condimentum</a>
 </nav>
 
 <nav id="footer">
 <a class="logo" href="#">Logo</a>
 <p>Etiam porta sem malesuada magna mollis euismod. Nulla vitae elit libero, a pharetra augue. Maecenas sed diam eget risus varius blandit sit amet non magna. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.</p>
 <ul>
 <li><a href="#">Elit Sem</a></li>
 <li><a href="#">Condimentum</a></li>
 <li><a href="#">Parturient</a></li>
 <li><a href="#">Vestibulum</a></li>
 </ul>
 </nav>
 </div>
 </body>
 </html>

As you can see, I have a div with the id page. Inside page I have an unordered list of news items. Following the news items I have the header, which is below the news content for the benefit of accessibility. Finally, I have a footer.

Changing the content order

The first thing I want to do is to move the header up to the top of the page. To do that, we’ll set page to be a flexbox container. Then we’ll set the direction to column. Then assign an order to each of page‘s child elements.

#page {
display:flex;
}
#page {
display:flex;
flex-flow:column;
}
#header {
order:1;
}
 
#news {
order:2;
}
 
#footer {
order:3;
}

Here’s how it looks.

Nesting flexbox containers

The next step is to lay out the header. Child elements inside flexbox containers can be flexbox containers in their own right, so we can use flexbox to evenly space the header elements.

Let’s set the header to be a flexbox container, then let’s space the content evenly, with no extra padding at the sides, and finally let’s make sure the menu items are on the same baseline as the logo.

#header {
order:1;
display:flex;
justify-content:space-between;
align-items:baseline;
}

Here’s how it looks.

Complex content wrapping

Our news items are going to be placeholders for featured stories, so they need to be a substantial size in order to make room for text, and an image, or video. So we need them to wrap.

Let’s set the news div to be a flexbox container. Next, let’s give the newsItems some minimum dimensions and some color so we can see them clearly.

#news {
order:2;
display:flex;
}

.newsItem {
min-width:300px;
min-height:250px;
background:#79797B;
border:1px solid #706667;
}

Here’s how it looks.

Now we need to set the news container to wrap, and the newsItems to expand to fill the available space.

#news {
order:2;
display:flex;
flex-flow:row wrap;
}

Here’s how it looks.

The challenge we have now, is that when the newsItems aren’t evenly distributed, the largest item (G) is at the bottom. On most sites, we want to give the largest amount of space to the topmost content. We can fix this by setting the wrap to wrap-reverse.

#news {
order:2;
display:flex;
flex-flow:row wrap-reverse;
}

Here’s how it looks.

That re-orders the items, but reading from top left, they’re now out of order. So we need to reverse the order, by setting the direction to row-reverse.

#news {
order:2;
display:flex;
flex-flow:row-reverse wrap-reverse;
}

Here’s how it looks.

That places the items in consecutive order, reading left to right, top to bottom; with the larger items always at the top. But I don’t want to have to alter the order of stories in my markup to force item A to come before item B. So the last thing I need to do is change the order of the items.

.newsItem:nth-of-type(7) {
order:1;
}
.newsItem:nth-of-type(6) {
order:2;
}
.newsItem:nth-of-type(5) {
order:3;
}
.newsItem:nth-of-type(4) {
order:4;
}
.newsItem:nth-of-type(3) {
order:5;
}
.newsItem:nth-of-type(2) {
order:6;
}
.newsItem:nth-of-type(1) {
order:7;
}

Here’s how it looks.

Mixing flexbox and media queries

The last element to be styled is the footer. We want to lay it out, much like the header, but because the footer only has three child elements (compared to the header‘s four) we’re going to set the paragraph to be twice as wide as the logo, and the list.

#footer {
order:3;
display:flex;
align-items:baseline;
flex-direction:row;
}
 
#footer > * {
flex:1;
}
 
#footer > p {
flex:2;
padding-right:2em;
}

Here’s how it looks.

That works great at desktop sizes, but shrink your browser down and you’ll see that it’s too cramped on small devices. So let’s change the code so that it’s laid out as columns below 600px, and reverts to a row above 600px.

#footer {
order:3;
display:flex;
align-items:baseline;
flex-direction:column;
padding:2em 0;
}
 
#footer > * {
flex:1;
}
 
#footer > p {
flex:2;
}
 
@media only screen and (min-width:600px) {

#footer {
flex-direction:row;
padding:0;
}
 
#footer > p {
margin-right:2em;
}
}

Here’s how it looks.

Conclusion

And there you have it. A modern approach to laying out content on a web page, that is widely supported and easy to implement.

The power of flexbox is that its properties can be combined to give us precise control over our layouts, with the simplest possible syntax.

Featured image, flexible image via Shutterstock.

Trend Rough Family Featuring 20 Fashionable Fonts – only $19!

Source

Categories: Designing, Others Tags:
  1. No comments yet.
  1. No trackbacks yet.
You must be logged in to post a comment.