Chapter 5. Responsive by Default

When creating a responsive design, being able to align items individually and as a group is important. We can’t work in absolute values, lining things up relative to a fixed point on a screen, if we have no fixed point to work from. Alignment is just one way in which we see how these modern methods of layout are responsive by default, designed for a world of flexible grids. In this chapter, we’ll look at those flexible grids, and think about how to make the most of the functionality in the specification.

In Chapter 1, we looked at a simple example of creating a layout using floats that had a flexible grid. To do this, we need to calculate the width of our floated column as a percentage of the available width, making sure to account for any margins between columns. We also need to employ media queries to change the number of columns, or to cause blocks to span more columns as the viewport size decreases. We can end up with a fair amount of CSS for each layout just to cope with this rearrangement of items at different breakpoints.

In flexbox, however, we can easily have three equal-width columns (Fig 5.1):

.cards {
  display: flex;
}
.cards li {
  flex: 1;
}

Code example: http://bkaprt.com/ncl/05-01/

Figure

Fig 5.1: Three equal-width flex items.

We can also allow the container width to dictate how many items will display in each row—without the need of a media query or breakpoint (Fig 5.2).

Figure

Fig 5.2: Wrapped flex items.

Because we explored the difference between flexbox and Grid in Chapter 3, you understand that the reason the cards on Row 2 don’t line up in a grid formation is because flexbox is one-dimensional. To create a strict grid, we can switch to Grid Layout.

.cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}

Note that these two examples differ. In the flexbox example, if we have more available space, the items will accumulate incrementally in one row before wrapping. If space decreases, we’ll eventually drop down to a single item per row.

We can achieve similar results with Grid Layout; doing so reminds us that Grid works from the outside in. We define a grid, and then put items into it.

To make a grid with a flexible number of columns, we need to create the column listing on the grid container itself; we need to instruct it to “add as many column tracks as will fit.” With that in mind, let’s take another look at the repeat() notation used in the earlier examples. Our current grid always has three tracks; I’ve used repeat() notation to create my track listing, and requested three column tracks each of 1fr.

Figure

Fig 5.3: At narrower screen widths, the rows contain fewer items.

What I want to do is to say “as many as will fit” rather than 3, as the first value of repeat(). So instead of using the integer 3, I use the keyword auto-fill (Fig 5.4). For the column-track width, I then need to use an absolute value; if we used 1fr, we would only get one column track, since 1fr would use all the available space in the grid container.

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, 200px);
}

Code example: http://bkaprt.com/ncl/05-02/

Figure

Fig 5.4: As many 200-pixel column tracks as will fit into the container.

If you drag the window wider and smaller, you can see that the browser creates as many 200-pixel column tracks as it can in the grid container. This isn’t quite the same as flexbox, however, since we now have fixed track sizes. If the container doesn’t divide neatly by 200 pixels, we get a gap at the end.

Figure

Fig 5.5: As many 200-pixels tracks as will fit, with the additional space distributed evenly.

To address this, we can employ another trick, with the minmax() function. Using minmax() in track sizing enables the setting of a minimum and a maximum size for that track. If we update the 200-pixel track definition to minmax(200px, 1fr), the browser will work out how many 200-pixel columns will fit into the container, and then take the leftover space and assign it equally to the tracks, since the maximum is 1fr.

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px,1fr));
}

Code example: http://bkaprt.com/ncl/05-03/

Figure

Fig 5.6: With auto-fill, the space for empty tracks is reserved.

We now have as many tracks as will fit in the container. The tracks have a flexible width, but won’t collapse below a minimum, and the layout remains a strict grid.

auto-fill vs. auto-fit

There are two possible keywords to use in repeat(): auto-fill and auto-fit. They do the same thing insofar as they add as many column tracks as will fit into the container. The difference is that if you use auto-fill but don’t have enough items to fill the tracks, the tracks remain open, so you will get space at the end where the reserved column tracks are.

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
}

Code example: http://bkaprt.com/ncl/05-04/

If we instead use auto-fit, once all the items have been laid out, any completely empty tracks will be collapsed. In the case of a flexible listing such as ours, the available space will then be distributed to the filled tracks (Fig 5.7).

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
}

Figure

Fig 5.7: With auto-fit, the tracks are collapsed. If we are distributing space with a max of 1fr, the leftover space is reassigned.

Use auto-fill if you want to maintain the tracks; use auto-fit if you want the content to fill the container (in case there are fewer items than tracks).

The ability to maintain proportions is a key requirement for responsive designs, and our new layout methods enable this. We briefly met the methods for creating flexibly sized items and grid tracks in previous chapters; now it’s time to fully understand how they work. The key to flexible sizing in flexbox lies in three properties: flex-grow, flex-shrink, and flex-basis. These are applied to the flex items. In Grid, we have the fr unit, which is used when creating track sizes.

Controlling Sizing with the flex Properties

To control the sizing of flex items, we need to add rules to the flex items themselves, and we do this by using three properties:

flex-grow

flex-shrink

flex-basis

The flex-basis property can be used to set a width (if flex-direction is row) or height (if flex-direction is column) for the flex item. If you give all items a flex-basis of 200 pixels, the browser will assign 200 pixels of space to each item.

The flex-grow property defines whether an item can grow larger than the size set in flex-basis. If you set flex-grow to 1, you allow items to grow larger than 200 pixels in order to take up any free space in the flex container.

The flex-shrink property determines whether an item can shrink smaller than the flex-basis value. If you have a non-wrapping 500-pixel-width container and three items with a flex-basis of 200 pixels, they will overflow unless flex-shrink is greater than 0.

The flex shorthand

The specification advises that authors use the flex shorthand rather than the individual properties of flex-grow, flex-shrink, and flex-basis (http://bkaprt.com/ncl/05-05/). Given their interaction with one another, it’s certainly easier to see what you’re doing if you treat these three values as one property. Therefore, my examples all use the flex property rather than the individual components; the order of the values is as follows:

flex-grow

flex-shrink

flex-basis

In this first example, I have three flex items. They all have a flex-grow value of 0, flex-shrink of 0, and flex-basis of 200px (Fig 5.8). Therefore, they do not grow to fill the flex container; if it becomes smaller than the total size needed to display the items, they’ll overflow.

.cards li {
  flex: 0 0 200px;
}

Figure

Fig 5.8: The items display at 200 pixels wide.

If I change flex-grow to 1, the items now stretch equally to fill the container (Fig 5.9).

.cards li {
  flex: 1 0 200px;
}

Figure

Fig 5.9: The items can now grow wider than 200 pixels.

If I target the first item and change flex-grow on just that item to 2, it grows more than the other two items (Fig 5.10). In this way, the flex properties allow you to scale items in proportion to one another.

.cards li {
  flex: 1 0 200px;
}
.cards li:first-child {
  flex: 2 0 200px;
}

Code example: http://bkaprt.com/ncl/05-06/

Figure

Fig 5.10: The first item now has a flex-grow factor of 2 and gets twice as much of the leftover space.

Note that Item 1 has now increased to twice the size of the other two items. Our flex-basis value is 200 pixels, which means that the browser first accounts for 3 × 200 pixels, and then, with the remaining space, distributes it per the flex-grow factor. The increase in size can therefore be quite subtle. If you want space to be distributed evenly, set flex-basis to 0 (Fig 5.11).

Figure

Fig 5.11: Set flex-grow to 0 for equal distribution of all the available space.

.cards li {
  flex: 1 0 0;
}
.cards li:first-child {
  flex: 2 0 0;
}

The content and auto keywords for flex-basis

In addition to accepting a length unit, flex-basis can take the keyword values of content and auto.

A value of content means that the flex-basis is taken from the content size of the item in the main dimension. Working with flex-direction set to row, the main dimension is the row, so flexbox will look at the width of the item and use that in the same way it uses a length unit you specify.

Then we have the very useful value of auto. Use auto as your flex-basis and, if you’ve set a width on the item, that width will be used as the flex-basis value. If you haven’t set a width, auto resolves to the content size.

In this example, I have three items and have set the flex-basis to auto (Fig 5.12). I’ve then given an absolute width to the third item, with the width property. Flexbox is now using that width as the flex-basis for the third item; the other two use the content width.

.cards li {
  flex: 1 1 auto;
}
.cards li:last-child {
 width: 400px;
}

Code example: http://bkaprt.com/ncl/05-07/

Figure

Fig 5.12: The width on the last item is used as the flex-basis value.

I would suggest using auto as your starting point for flex-basis unless you know that you have a specific requirement. Doing so means that by setting a width on your item, or by way of the content requiring a certain amount of space, things will often work out well. But there is much value in playing with and testing exactly how the flex properties work; the things that flexbox does that seem mysterious are very much tied up in them.

Keeping things in proportion in Grid Layout with fr units

In CSS Grid Layout, we generally define our proportions when we define our tracks, rather than work from the content out. For this we have the fr unit, which we’ve already met. So far, we have only seen the fr unit in action creating equal-width tracks, but it can also be used much like flex-grow to define a portion of the available space.

In my next example, I’ve created a layout with three tracks; the sidebars are 1fr each and the middle track is 4fr. The available space in the grid container is divided into six: one part given to each of the 1fr tracks, and four parts to the 4fr track (Fig 5.13).

Figure

Fig 5.13: The fr unit layout. The column tracks grow and shrink proportionally.

.example {
   display: grid;
   grid-template-columns: 1fr 4fr 1fr;
   grid-gap: 20px;
}
.content {
  grid-column: 2;
}
.sidebar1 {
  grid-column: 1;
  grid-row: 1;
}
.sidebar2 {
  grid-column: 3;
  grid-row: 1;
}

Code example: http://bkaprt.com/ncl/05-08/

The nice thing about fraction units is that, because they define a fraction of the available space, they can be mixed with tracks set using absolute units.

In the next example, I’ve changed my track listing to create a layout that used to be referred to as the “Holy Grail.” The Holy Grail layout describes fixed-width sidebars and a stretchy middle, with the main content area coming first in the source (Fig 5.14). The middle track is 1fr, so it takes up all available space after the fixed sidebar, and any grid-gap has been accounted for.

.example {
   display: grid;
   grid-template-columns: 150px 1fr 150px;
   grid-gap: 20px;
}

Figure

Fig 5.14: The “Holy Grail” layout.

The behavior of auto in Grid Layout

In the section about flex-basis, I described how auto works in flexbox, taking the width of the item or resolving to content. In Grid Layout, auto works in roughly the same way; however, be aware that it affects the entire row or column track.

In this example, the middle column track is set to auto, and since we have not set a track size for rows, the auto-generated rows will also be auto-sized. The item card2 appears in the middle track and, as the widest item in that track, causes the entire track to take 300 pixels. The item card4 is on the second row and forces the entire row track to 300 pixels tall (Fig 5.15).

.cards {
  display: grid;
  grid-gap: 20px;
  grid-template-columns: 1fr auto 1fr;
}
.cards .card2 {
  width: 300px;
}
.cards .card4 {
  height: 300px;
}

Code example: http://bkaprt.com/ncl/05-09/

Although allowing rows to auto-size is generally desirable, in some cases, setting columns to auto may result in strange sizing if something in the track affects the widths. Something to keep in mind!

Figure

Fig 5.15: Auto-sized row tracks may have their size affected by items, or by the content of items.

Using auto as a maximum in minmax()

Because auto can generally be expected to mean “content-sized,” it can be usefully employed as the maximum in minmax(). This enables the creation of tracks that are always a minimum height or width, but that expand if more content is added than expected.

In this example, I’ve created a very neat set of boxes. Each row track is a minimum of 150 pixels tall; if an area spans two rows, it becomes 320 pixels tall, including the grid-gap of 20px. The item card2 is an example of this (Fig 5.16).

.cards {
  display: grid;
  grid-gap: 20px;
  grid-template-columns: 1fr 1fr 1fr;
  grid-auto-rows: minmax(150px, auto);
}
.card1 {
  grid-column: 1;
  grid-row: 1; 
}
.card2 {
  grid-column: 2 / 4;
  grid-row: 1 / 3;
}

Code example: http://bkaprt.com/ncl/05-10/

Figure

Fig 5.16: A neat grid with 150-pixel-tall tracks.

The track sizing is achieved using the minmax() function we met earlier, with our minimum set to the ideal size of the track—150px—and our maximum set to auto. This means that as long as the content is shorter than 150px, we get a 150-pixel track, as designed.

And if someone comes along and adds additional text into any box, the design adapts: the full row extends down to contain the content. The item card2 is now also taller, as the full row track 1 has expanded (Fig 5.17).

Figure

Fig 5.17: Row 1 expands to contain the content.

As you can see from this small example, Grid enables the creation of very neat, precise layouts that accommodate the real world of content on the web and the variety of devices we need to support. The ability to easily place items anywhere on a grid is powerful and will make existing layouts easier, and enable new design on the web. However, it brings with it a whole new range of potential accessibility issues. We’ll look at those next.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset