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.
Table of Contents
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:
- Applying CSS property
float
i.e float the elements to the left or right of each other - Changing the
display
property of the elements to eitherinline
orinline-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-item
s 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.
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