Joe Bergantine | Designer

Create a Vertically-Even Series of Elements in HTML

Given a series of elements nested in another element, as in the following where innerEl is nested inside outerEl, we want to create a vertically even row where the outerEl elements are all the same height. (One additional wrapper element, a non-semantic div is needed inside each .innerEl for the JavaScript solution). The HTML markup in its simplest form looks something like this:

Thing One

Thing One

Thing Two

Thing Three

Thing One

Thing Two

A JavaScript Solution

See a full demo of the JavaScript solution on CodePen

I initially encountered this problem in a scenario where I couldn't use CSS Flexbox or CSS Grid as a solution and turned instead to JavaScript to get the heights of each object, find the largest value, and then set everything to that value.

Getting everything to stack horizontally can be done with CSS:

.collectionEl {
    text-align: center;
    overflow: hidden; /* clearfix */
}

.collectionEl .outerEl {
    float: left;
    width: 30%;
}

.collectionEl .innerEl {
    overflow: hidden; /* prevent margin collapsing */
}

.collectionEl .outerEl {
    /* affects the middle child after the overrides that follow */
    /* use progressive enhancement to calculate width using calc() */
    margin-left: 1.6%;
    margin-left: calc(10% / 6);
    margin-right: 1.6%;
    margin-right: calc(10% / 6);
}

.collectionEl .outerEl:first-child {
    margin-left: 0;
    margin-right: 3.3%;
    margin-right: calc(10% / 3);
}


.collectionEl .outerEl:last-child {
    margin-left: 3.3%;
    margin-left: calc(10% / 3);
    margin-right: 0;
}

CSS Flexbox

See a full demo of the Flexbox solution on CodePen.

This is possible with CSS Flexbox. Flexbox's align-items default value is stretch creating equal-height columns out of the box. display: flex aligns the child elements in a row by default. justify-content: space-between as applied to .collectionEl tells the .outerEl elements to stack leaving space between each box based on a box width of 30vw — a little less than 1/3 of the container width — as set on .outerEl. What remains is to fill the vertical space within each .outerEl and center the child content.

.collectionEl {
    display: flex;
    justify-content: space-between;
}

/* each element is a little less than 1/3 page wide */
.collectionEl .outerEl {
    width: 30vw;
}

/* fill up the vertical space */
.collectionEl .outerEl {
    display: flex;
    justify-content: center;
}

/* each inner element fills up the horizontal space and the children are stacked vertically about the center */
.collectionEl .innerEl {
    width: 100%;
    display: flex;
    flex-direction: column;
    justify-content: center;
}

/* re-introduce margin collapsing by removing the top margin from subsequent elements within .innerEl; 
flex container’s margins do not collapse with the margins of its contents */
.collectionEl .innerEl p + p {
    margin-top: 0; 
}

CSS Grid + Flexbox

See a full demo of the Grid + Flexbox solution on CodePen.

This is simpler still, and much more explicit with regards to the width of each column and gutter, using CSS Grid to setup the row and Flexbox to set the heights.

.collectionEl {
    display: grid;
    grid-template-columns: repeat(3, 1fr); /* 3 columns, each 1/3rd the width of the available space */
    grid-column-gap: 3.2%; /* match the column gap to the previous examples */
}

.outerEl {
    /*  bring inner el height to 100%  */
    display: flex;
}

.innerEl {
    width: 100%; /* setting the outer element to flex causes the inner element to otherwise collapse to the content width */
    display: flex;
    flex-direction: column;
    justify-content: center;
}

Revisions

June 3, 2019
Some revisions to the explanations for clarity. Moved the links to CodePen above the code examples to provide a link to see the result in action before reading how to do it.
June 1, 2019
Added a variation using CSS Grid with Flexbox.
May 16, 2019
Added links to demos on CodePen. Switched the JS example to use `float` after box model sizing issues of width with `display: inline-block`.
May 15, 2019
Added code for doing the same thing using Flexbox and revised the JavaScript to vertically center the child elements to match the Flexbox example. Changed the JavaScript to use plain old JavaScript rather than jQuery.