Don't use % for that layout...

This one is about the right balance.
  • Mon storms 71° 47°
  • Tue rain 70° 48°
  • Wed clear 80° 49°

Dust off your HTML and CSS, and think for a second how you would implement the “weather forecast” card above.

The first things you’ll notice, is that the horizontal real estate needs to be evenly distributed between a number of elements (three in this case). The naive approach would be to divide the available space by three, hard-code the width of each inlined element, and call it day. But we know better, and are well aware of the vast diversity of screen dimensions, we could also think of using an all mighty display: inline-block while throwing in some widths using percentage digits. But is it good enough?

Products change, new features come and others go, new trends trigger redesigns, products will be translated into other verbose languages, and let's not don’t forget about A/B testing. This is why the fewer constraints we set in a layout, the easier will be to adopt changes and variations.

Case Study

We are going to walk through a few different approaches that will make your and your colleague's future life easier when working with these kinds of layouts and we will start by simplifying the concepts by only using bare <span> elements, however, the principles will stand for more complex structures with any number of children elements.

1 2 3

Our solution must satisfy the following requirements:

  • The elements should be distributed evenly across the available space.
  • It should handle a variable number of elements.
  • It should handle variable width for each element.

We will take 4 approaches, sorted descending on browser compatibility:

  • Using a table
  • Using display table
  • Using Justify
  • Using flex box

The <table> element

Even though some people could laugh at the thought of using <table> elements in these days, if absolute cross-browser compatibility is a requirement, this is the way to go.

1 2 3
1 222 3
1 2 3 4
1 2 3 4
CSS
table {
  text-align: center;
  width: 100%;
}
HTML
<table>
    <tbody>
        <tr>
        <td>
            <span>1</span>
        </td>
        <td>
            <span>2</span>
        </td>
        <td>
            <span>3</span>
        </td>
        <td>
            <span>4</span>
        </td>
        </tr>
    </tbody>
</table>
Browser Support
Internet Explorer All
Chrome All
Firefox All
Safari All

Display: table

With this approach, we get the benefits of a <table> without the performance hit.

1 2 3
1 222 3
1 2 3 4
1 2 3 4
CSS
div {
  display: table;
  width: 100%;
}
span {
  display: table-cell;
}
HTML
<div>
    <span>1</span>
    <span>2</span>
    <span>3</span>
    <span>4</span>
</div>
Browser Support
Internet Explorer 8+
Chrome All
Firefox All
Safari All

Text-align: justify

The devil is on the details, compare the alignment with the previous examples, you will note as the name implies that with this approach we can 'justify' the content so it starts and ends at the edges of the container element, as well as how it wraps to the next line when the width is overflown by the content.

The way this is achieved is by inserting a pseudo element after the container element forcing the container to reach a full width hence spacing the elements edge to edge.

1 2 3
1 222 3
1 2 3 4
1 2 3 4
CSS
span {
  display: inline-block
}
div {
  text-align: justify;
  width: 100%;
}
div::after {
  content: '';
  display: inline-block;
  width: 100%;
}
/* -- Internet Explorer support -- */
div:after {
  content: '';
  display: inline-block;
  width: 100%;
}
HTML
<div>
    <span>1</span>
    <span>2</span>
    <span>3</span>
    <span>4</span>
</div>
Browser Support
Internet Explorer *8+
Chrome All
Firefox All
Safari All

Display: flex

The easiest way to work with complex layouts is definitely the flex-box, the draw back is the browser support. Depending on the actual objective we use two alternatives to justify: space-around or space-between.

space-around

1 2 3
1 222 3
1 2 3 4
1 2 3 4

space-between

1 2 3
1 222 3
1 2 3 4
1 2 3 4
CSS
/* Space Around */
div {
  display: flex;
  justify-content: space-around;
  width: 100%;
}
/* Space Between */
div {
  display: flex;
  justify-content: space-between;
  width: 100%;
}
HTML
<div>
    <span>1</span>
    <span>2</span>
    <span>3</span>
    <span>4</span>
</div>
Browser Support
Internet Explorer 10+
Chrome All
Firefox All
Safari All

Conclusion

For the initial example, in my opinion display: table is the way to go, here is why:

  • If the number of elements change the task is as simple as adding or removing a list element, no CSS changes are required whatsoever.
  • If the width of the images change the layout will not be affected.
  • If the container gets too small the elements will not overlap with each other maintaining readability.
  • Browser support is high enough to render the intended design without adding overhead to the compliant browsers.
  • Mon storms 71° 47°
  • Tue rain 70° 48°
  • Wed clear 80° 49°
CSS
ul {
    border: 9px solid #333;
    border-radius: 20px;
    display: table;
    margin: auto;
    padding: 14px 0;
    width: 85%;
}
li {
    display: table-cell;
    text-align: center;
}
img {
    display: block;
    margin: auto;
}
span{
    display: inline-block;
    padding: 0 4px;
}
HTML
<ul>
    <li>
        <span>Mon</span>
        <img src="/svgs/storms.svg"
            width="60" height="60"
            alt="storms"/>
        <span>71°</span>
        <span>47°</span>
    </li>
    <li>
        <span>Tue</span>
        <img src="/svgs/rain.svg"
            width="60" height="60"
            alt="rain"/>
        <span>70°</span>
        <span>48°</span>
    </li>
    <li>
        <span>Wed</span>
        <img src="/svgs/clear.svg"
            width="60" height="60"
            alt="clear"/>
        <span>80°</span>
        <span>49°</span>
    </li>
</ul>
— fabián