Responsive Web Design with HTML5 and CSS3(Second Edition)
上QQ阅读APP看书,第一时间看更新

Getting Flexy

Flexbox has four key characteristics: direction, alignment, ordering, and flexibility. We'll cover all these characteristics and how they relate by way of a few examples.

The examples are deliberately simplistic; just moving some boxes and their content around so we can understand the principals of how Flexbox works.

Perfect vertically centered text

Note that this first Flexbox example is example_03-03:

Perfect vertically centered text

Here's the markup:

<div class="CenterMe">
    Hello, I'm centered with Flexbox!
</div>

Here is the entire CSS rule that's styling that markup:

.CenterMe {
    background-color: indigo;
    color: #ebebeb;
    font-family: 'Oswald', sans-serif;
    font-size: 2rem;
    text-transform: uppercase;
    height: 200px;
    display: flex;
    align-items: center;
    justify-content: center;
}

The majority of the property/value pairs in that rule are merely setting colors and font sizing. The three properties we are interested in are:

.CenterMe {    
    /* other properties */
    display: flex;
    align-items: center;
    justify-content: center;
}

If you have not used Flexbox or any of the properties in the related Box Alignment specification (http://www.w3.org/TR/css3-align/) these properties probably seem a little alien. Let's consider what each one does:

  • display: flex: This is the bread and butter of Flexbox. This merely sets the item to be a Flexbox (as opposed to a block, inline-block, and so on).
  • align-items: This aligns the items within a Flexbox in the cross axis (vertically centering the text in our example).
  • justify-content: This sets the main axis centring of the content. With a Flexbox row, you can think of it like the button in a word processor that sets the text to the left, right, or center (although there are additional justify-content values we will look at shortly).

OK, before we get further into the properties of Flexbox, we will consider a few more examples.

Tip

In some of these examples I'm making use of the Google hosted font 'Oswald' (with a fallback to a sans-serif font). In Chapter 5, CSS3 – Selectors, Typography, Color Modes, and New Features, we will look at how we can use the @font-face rule to link to custom font files.

Offset items

How about a simple list of navigation items, but with one offset to one side?

Here's what it looks like:

Offset items

Here's the markup:

<div class="MenuWrap">
    <a href="#" class="ListItem">Home</a>
    <a href="#" class="ListItem">About Us</a>
    <a href="#" class="ListItem">Products</a>
    <a href="#" class="ListItem">Policy</a>
    <a href="#" class="LastItem">Contact Us</a>
</div>

And here is the CSS:

.MenuWrap {
    background-color: indigo;
    font-family: 'Oswald', sans-serif;
    font-size: 1rem;
    min-height: 2.75rem;
    display: flex;
    align-items: center;
    padding: 0 1rem;
}

.ListItem,
.LastItem {
    color: #ebebeb;
    text-decoration: none;
}

.ListItem {
    margin-right: 1rem;
}

.LastItem {
    margin-left: auto;
}

How about that—not a single float, inline-block, or table-cell needed! When you set display: flex; on a wrapping element, the children of that element become flex-items which then get laid out using the flex layout model. The magic property here is margin-left: auto which makes that item use all available margin on that side.

Reverse the order of items

Want to reverse the order of the items?

Reverse the order of items

It's as easy as adding flex-direction: row-reverse; to the wrapping element and changing margin-left: auto to margin-right: auto on the offset item:

.MenuWrap {
    background-color: indigo;
    font-family: 'Oswald', sans-serif;
    font-size: 1rem;
    min-height: 2.75rem;
    display: flex;
    flex-direction: row-reverse;
    align-items: center;
    padding: 0 1rem;
}

.ListItem,
.LastItem {
    color: #ebebeb;
    text-decoration: none;
}

.ListItem {
    margin-right: 1rem;
}

.LastItem {
    margin-right: auto;
}

How about if we want them laid out vertically instead?

Simple. Change to flex-direction: column; on the wrapping element and remove the auto margin:

.MenuWrap {
    background-color: indigo;
    font-family: 'Oswald', sans-serif;
    font-size: 1rem;
    min-height: 2.75rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 0 1rem;
}

.ListItem,
.LastItem {
    color: #ebebeb;
    text-decoration: none;
}

Column reverse

Want them stacked in the opposite direction? Just change to flex-direction: column-reverse; and you're done.

Note

You should be aware that there is a flex-flow property that is shorthand for setting flex-direction and flex-wrap in one. For example, flex-flow: row wrap; would set the direction to a row and set wrapping on. However, at least initially, I find it easier to specify the two settings separately. The flex-wrap property is also absent from the oldest Flexbox implementations so can render the whole declaration void in certain browsers.

Different Flexbox layouts inside different media queries

As the name suggests, Flexbox is inherently flexible so how about we go for a column list of items at smaller viewports and a row style layout when space allows. It's a piece of cake with Flexbox:

.MenuWrap {
    background-color: indigo;
    font-family: 'Oswald', sans-serif;
    font-size: 1rem;
    min-height: 2.75rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 0 1rem;
}

@media (min-width: 31.25em) {
    .MenuWrap {
        flex-direction: row;
    }    
}

.ListItem,
.LastItem {
    color: #ebebeb;
    text-decoration: none;
}

@media (min-width: 31.25em) {
    .ListItem {
        margin-right: 1rem;
    }
    .LastItem {
        margin-left: auto;
    }
}

You can view that as example_03-05. Be sure to resize the browser window to see the different layouts.

Inline-flex

Flexbox has an inline variant to complement inline-block and inline-table. As you might have guessed it is display: inline-flex;. Thanks to its beautiful centering abilities you can do some wacky things with very little effort.

Inline-flex

Here's the markup:

<p>Here is a sentence with a <a href="http://www.w3.org/TR/css-flexbox-1/#flex-containers" class="InlineFlex">inline-flex link</a>.</p>

And here is the CSS for that:

.InlineFlex {
    display: inline-flex;
    align-items: center;    
    height: 120px;
    padding: 0 4px;
    background-color: indigo;
    text-decoration: none;
    border-radius: 3px;
    color: #ddd;
}

When items are set as inline-flex anonymously (for example, their parent element is not set to display: flex;) then they retain whitespace between elements, just like inline-block or inline-table do. However, if they are within a flex container, then whitespace is removed, much as it is with table-cell items within a table.

Of course, you don't always have to center items within a Flexbox. There are a number of different options. Let's look at those now.

Flexbox alignment properties

If you want to play with this example, you can find it at example_03-07. Remember the example code you download will be at the point where we finish this section so if you want to 'work along' you may prefer to delete the CSS in the example file and start again.

The important thing to understand with Flexbox alignment is the concept of axis. There are two axis to consider, the 'main axis' and the 'cross axis'. What each of these represents depends upon the direction the Flexbox is heading. For example, if the direction of your Flexbox is set to row, the main axis will be the horizontal axis and the cross axis will be the vertical axis.

Conversely, if your Flexbox direction is set to column, the main axis will be the vertical axis and the cross axis will be the horizontal.

The specification (http://www.w3.org/TR/css-flexbox-1/#justify-content-property) provides the following illustration to aid authors:

Flexbox alignment properties

Here's the basic markup of our example:

<div class="FlexWrapper">
    <div class="FlexInner">I am content in the inner Flexbox.</div>
</div>

Let's set basic Flexbox related styles:

.FlexWrapper {
    background-color: indigo;
    display: flex;
    height: 200px;
    width: 400px;
}

.FlexInner {
    background-color: #34005B;
    display: flex;
    height: 100px;
    width: 200px;
}

In the browser, that produces this:

Flexbox alignment properties

Right, let's test drive the effects of some of these properties.

The align-items property

The align-items property positions items in the cross axis. If we apply this property to our wrapping element like so:

.FlexWrapper {
    background-color: indigo;
    display: flex;
    height: 200px;
    width: 400px;
    align-items: center;
}

As you would imagine, the item within that box gets centered vertically:

The align-items property

The same effect would be applied to any number of children within.

The align-self property

Sometimes, you may want to pull just one item into a different alignment. Individual flex items can use the align-self property to align themselves. At this point, I'll remove the previous alignment properties, add another two items into the markup (they have been given the .FlexInner HTML class), and on the middle one I'll add another HTML class (.AlignSelf) and use it to add the align-self property. Viewing the CSS at this point may be more illustrative:

.FlexWrapper {
    background-color: indigo;
    display: flex;
    height: 200px;
    width: 400px;
}
.FlexInner {
    background-color: #34005B;
    display: flex;
    height: 100px;
    width: 200px;
}

.AlignSelf {
    align-self: flex-end;
}

Here is the effect in the browser:

The align-self property

Wow! Flexbox really makes these kinds of changes trivial. In that example the value of align-self was set to flex-end. Let's consider the possible values we could use on the cross axis before looking at alignment in the main axis.

Possible alignment values

For cross axis alignment, Flexbox has the following possible values:

  • flex-start: Setting an element to flex-start would make it begin at the 'starting' edge of its flex container
  • flex-end: Setting to flex-end would align the element at the end of the flex container
  • center: Puts it in the middle of the flex container
  • baseline: Sets all flex items in the container so that their baselines align
  • stretch: Makes the items stretch to the size of their flex container (in the cross axis)

Note

There are some particulars inherent to using these properties, so if something isn't playing happily, always refer to the specification for any edge case scenarios: http://www.w3.org/TR/css-flexbox-1/.

The justify-content property

Alignment in the main axis is controlled with justify-content (for non Flexbox/block-level items, the justify-self property has also been proposed (http://www.w3.org/TR/css3-align/). Possible values for justify-content are:

  • flex-start
  • flex-end
  • center
  • space-between
  • space-around

The first three do exactly what you would now expect. However, let's take a look what space-between and space-around do. Consider this markup:

<div class="FlexWrapper">
    <div class="FlexInner">I am content in the inner Flexbox 1.</div>
    <div class="FlexInner">I am content in the inner Flexbox 2.</div>
    <div class="FlexInner">I am content in the inner Flexbox 3.</div>
</div>

And then consider this CSS. We are setting the three flex-items (FlexInner) to each be 25% width, wrapped by a flex container (FlexWrapper) set to be 100% width.

.FlexWrapper {
    background-color: indigo;
    display: flex;
    justify-content: space-between;
    height: 200px;
    width: 100%;
}
.FlexItems {
    background-color: #34005B;
    display: flex;
    height: 100px;
    width: 25%;
}

As the three items will only take up 75% of the available space, justify-content explains what we would like the browser to do with the remaining space. A value of space-between puts equal amount of space between the items and space-around puts it around. Perhaps a screenshot here will help: This is space-between.

The justify-content property

And here is what happens if we switch to space-around.

The justify-content property

Those two values are pretty handy I think you will agree.

Tip

The various alignment properties of Flexbox are currently being specified into the CSS Box Alignment Module Level 3. This should give the same fundamental alignment powers to other display properties, such as display: block; and display: table;. The specification is still being worked upon so check the status at http://www.w3.org/TR/css3-align/.

The flex property

We've used the width property on those flex-items but it's also possible to define the width, or 'flexiness' if you will, with the flex property. To illustrate, consider another example; same markup, but amended CSS for the items:

.FlexItems {
    border: 1px solid #ebebeb;
    background-color: #34005B;
    display: flex;
    height: 100px;
    flex: 1;
}

The flex property is actually a shorthand way of specifying three separate properties: flex-grow, flex-shrink, and flex-basis. The specification covers these individual properties in more detail at http://www.w3.org/TR/css-flexbox-1/. However, the specification recommends that authors use the flex shorthand property, so that's what we're rolling with here, capiche?

The flex property

For flex-items, if a flex property is present (and the browser supports it), it is used to size the item rather than a width or height value (if present). Even if the width or height value is specified after the flex property, it will still have no effect. Let's look at what each of these values do.

  • flex-grow (the first value you can pass to flex) is the amount, relevant to the other flex items, the flex-item can grow when free space is available
  • flex-shrink is the amount the flex-item can shrink relevant to the other flex-items when there is not enough space available
  • flex-basis (the final value you can pass to Flex) is the basis size the flex-item is sized to

Although it's possible to just write flex: 1, I recommend writing all the values into a flex property. I think it's clearer what you intend to happen. For example: flex: 1 1 auto means that the item will grow into 1 part of the available space, it will also shrink 1 part when space is lacking and the basis size for the flexing is the intrinsic width of the content (the size the content would be if flex wasn't involved).

Let's try another: flex: 0 0 50px means this item will neither grow nor shrink and it's basis is 50px (so it will be 50px regardless of any free space). How about flex: 2 0 50%—that's going to take two 'lots' of available space, it won't shrink and its basis size is 50%. Hopefully, these brief examples have demystified the flex property a little.

Tip

If you set the flex-shrink value to zero, then the flex basis effectively behaves like a minimum width.

You can think of the flex property as a way to set ratios. With each flex-item set to 1, they each take an equal amount of space:

The flex property

Right, so to test the theory, let's amend the HTML classes in the markup:

<div class="FlexWrapper">
    <div class="FlexItems FlexOne">I am content in the inner Flexbox 1.</div>
    <div class="FlexItems FlexTwo">I am content in the inner Flexbox 2.</div>
    <div class="FlexItems FlexThree">I am content in the inner Flexbox 3.</div>
</div>

And then here is the amended CSS:

.FlexItems {
    border: 1px solid #ebebeb;
    background-color: #34005B;
    display: flex;
    height: 100px;
}

.FlexOne {
    flex: 1.5 0 auto;
}

.FlexTwo,
.FlexThree {
    flex: 1 0 auto;
}

In this instance, FlexOne takes up 1.5 the amount of space that FlexTwo and FlexThree take up.

This shorthand syntax really becomes useful for quickly bashing out relationships between items. For example, if the request comes in, "that needs to be 1.8 times wider than the others", you could easily facilitate that request with the flex property.

Hopefully, the incredibly powerful flex property is starting to make a little sense now?

I could write chapters and chapters on Flexbox! There are so many examples we could look at. However, before we move on to the other main topic of this chapter (responsive images) there are just two more things I would like to share with you.

Simple sticky footer

Suppose you want a footer to sit at the bottom of the viewport when there is not enough content to push it there. This has always been a pain to achieve but with Flexbox it's simple. Consider this markup (which can be viewed in example_03-08):

<body>
    <div class="MainContent">
        Here is a bunch of text up at the top. But there isn't enough content to push the footer to the bottom of the page.
    </div>
    <div class="Footer">
        However, thanks to flexbox, I've been put in my place.
    </div>
</body>

And here's the CSS:

html,
body {
    margin: 0;
    padding: 0;
}

html {
    height: 100%;
}

body {
    font-family: 'Oswald', sans-serif;
    color: #ebebeb;
    display: flex;
    flex-direction: column;
    min-height: 100%;
}

.MainContent {
    flex: 1;
    color: #333;
    padding: .5rem;
}

.Footer {
    background-color: violet;
    padding: .5rem;
}

Take a look at that in the browser and test adding more content into .MainContentdiv. You'll see that when there is not enough content, the footer is stuck to the bottom of the viewport. When there is, it sits below the content.

This works because our flex property is set to grow where space is available. As our body is a flex container of 100% minimum height, the main content can grow into all that available space. Beautiful.

Changing source order

Since the dawn of CSS, there has only been one way to switch the visual ordering of HTML elements in a web page. That was achieved by wrapping elements in something set to display: table and then switching the display property on the items within, between display: table-caption (puts it on top), display: table-footer-group (sends it to the bottom), and display: table-header-group (sends it to just below the item set to display: table-caption). However, as robust as this technique is, it was a happy accident, rather than the true intention of these settings.

However, Flexbox has visual source re-ordering built in. Let's have a look at how it works.

Consider this markup:

<div class="FlexWrapper">
    <div class="FlexItems FlexHeader">I am content in the Header.</div>
    <div class="FlexItems FlexSideOne">I am content in the SideOne.</div>
    <div class="FlexItems FlexContent">I am content in the Content.</div>
    <div class="FlexItems FlexSideTwo">I am content in the SideTwo.</div>
    <div class="FlexItems FlexFooter">I am content in the Footer.</div>
</div>

You can see here that the third item within the wrapper has a HTML class of FlexContent—imagine that this div is going to hold the main content for the page.

OK, let's keep things simple. We will add some simple colors to more easily differentiate the sections and just get these items one under another in the same order they appear in the markup.

.FlexWrapper {
    background-color: indigo;
    display: flex;
    flex-direction: column;
}

.FlexItems {
    display: flex;
    align-items: center;
    min-height: 6.25rem;
    padding: 1rem;
}

.FlexHeader {
    background-color: #105B63;    
}

.FlexContent {
    background-color: #FFFAD5;
}

.FlexSideOne {
    background-color: #FFD34E;
}

.FlexSideTwo {
    background-color: #DB9E36;
}

.FlexFooter {
    background-color: #BD4932;
}

That renders in the browser like this:

Changing source order

Now, suppose we want to switch the order of .FlexContent to be the first item, without touching the markup. With Flexbox it's as simple as adding a single property/value pair:

.FlexContent {
    background-color: #FFFAD5;
    order: -1;
}

The order property lets us revise the order of items within a Flexbox simply and sanely. In this example, a value of -1 means that we want it to be before all the others.

Tip

If you want to switch items around quite a bit, I'd recommend being a little more declarative and add an order number for each. This makes things a little easier to understand when you combine them with media queries.

Let's combine our new source order changing powers with some media queries to produce not just a different layout at different sizes but different ordering.

Note

Note: you can view this finished example at example_03-09.

As it's generally considered wise to have your main content at the beginning of a document, let's revise our markup to this:

<div class="FlexWrapper">
    <div class="FlexItems FlexContent">I am content in the Content.</div>
    <div class="FlexItems FlexSideOne">I am content in the SideOne.</div>
    <div class="FlexItems FlexSideTwo">I am content in the SideTwo.</div>
    <div class="FlexItems FlexHeader">I am content in the Header.</div>
    <div class="FlexItems FlexFooter">I am content in the Footer.</div>
</div>

First the page content, then our two sidebar areas, then the header and finally the footer. As I'll be using Flexbox, we can structure the HTML in the order that makes sense for the document, regardless of how things need to be laid out visually.

For the smallest screens (outside of any media query), I'll go with this ordering:

.FlexHeader {
    background-color: #105B63;
    order: 1;
}

.FlexContent {
    background-color: #FFFAD5;
    order: 2;
}

.FlexSideOne {
    background-color: #FFD34E;
    order: 3;
}

.FlexSideTwo {
    background-color: #DB9E36;
    order: 4;
}

.FlexFooter {
    background-color: #BD4932;
    order: 5;
}

Which gives us this in the browser:

Changing source order

And then, at a breakpoint, I'm switching to this:

@media (min-width: 30rem) {
    .FlexWrapper {
        flex-flow: row wrap;
    }
    .FlexHeader {
        width: 100%;
    }
    .FlexContent {
        flex: 1;
        order: 3;
    }
    .FlexSideOne {
        width: 150px;
        order: 2;
    }
    .FlexSideTwo {
        width: 150px;
        order: 4;
    }
    .FlexFooter {
        width: 100%;
    }
}

Which gives us this in the browser:

Changing source order

Note

In that example, the shortcut flex-flow: row wrap has been used. That allows the flex items to wrap onto multiple lines. It's one of the poorer supported properties, so depending upon how far back support is needed, it might be necessary to wrap the content and two side bars in another element.

Wrapping up Flexbox

There are near endless possibilities when using the Flexbox layout system and due to its inherent 'flexiness', it's a perfect match for responsive design. If you've never built anything with Flexbox before, all the new properties and values can seem a little odd and it's sometimes disconcertingly easy to achieve layouts that have previously taken far more work. To double-check implementation details against the latest version of the specification, make sure you check out http://www.w3.org/TR/css-flexbox-1/.

I think you'll love building things with Flexbox.

Note

Hot on the heels of the Flexible Box Layout Module is the Grid Layout Module Level 1: http://www.w3.org/TR/css3-grid-layout/.

It's relatively immature compared to Flexbox (much like the early history of Flexbox, grid layout has already been through some major changes) and as such we aren't looking at it in detail here. However, it's definitely one to keep an eye on as it promises us even more layout powers.