Cinderella wearing European fashion of the mid-1860’s
Belle, based on 1770’s French court fashion
Mulan, based on the Ming Dynasty period
Sleeping Beauty, based on European fashions in 1485
Snow White, based on 16th century German fashion
Ariel wearing an evening gown of the 1890’s
Art © Claire Hummel

Pinterest launched three years ago with a signature and immediately emulated visual style: vertical columns of collected snippets. Pinterest uses JavaScript to achieve this layout, but for a long time I’ve wanted to replicate it using CSS alone. Based on the pioneering work of Kushagra Agarwal and using the outstanding illustrations of Claire Hummel, I’m pleased to offer this solution.

Achieving The Layout

The pictures and their captions are entered as semantic HTML5 (I’ll show just the first two images, as the code quickly becomes repetitive):

<div id="columns">
	<figure>
		<img src="cinderella.jpg" alt>
		<figcaption>Cinderella wearing European fashion of the mid-1860’s</figcaption>
	</figure>
	<figure>
		<img src="rapunzel.jpg" alt>
		<figcaption>Rapunzel, clothed in 1820’s period fashion</figcaption>
…
</div>

CSS multi-column layout is perfect suited to achieving this effect. The code is shown sans vendor prefixes for clarity:

#columns {
	column-width: 320px;
	column-gap: 15px;
	width: 90%;
	max-width: 1100px;
	margin: 50px auto;
}

Setting the columns to a fixed width paradoxically makes them more : the spec says that the browser must base its layout of the columns on the answer to the question “how many 320px wide columns (plus any gap) can fit evenly into the space provided?” After that calculation the layout is achieved: 6 columns or 1, whatever works.

The CSS for the individual <figure> elements and the images they contain is almost as straightforward:

div#columns figure {
	background: #fefefe;
	border: 2px solid #fcfcfc;
	box-shadow: 0 1px 2px rgba(34, 25, 25, 0.4);
	margin: 0 2px 15px;
	padding: 15px;
	padding-bottom: 10px;
	transition: opacity .4s ease-in-out;
	column-break-inside: avoid;
	display: inline-block;
}
div#columns figure img {
	width: 100%;
	height: auto;
	border-bottom: 1px solid #ccc;
	padding-bottom: 15px;
	margin-bottom: 5px;
}

column-break-inside: avoid; should be enough to force each <figure> element to fall completely inside the column it starts within, without “slopping over” to the next, but no browser completely adheres to that rule yet. To get around that, I’ve added display: inline-block to the rule.

Finally, we use the deeply unappreciated :not pseudo-selector to create the hover effect, using the transition we added earlier to the <figure> elements:

div#columns:hover figure:not(:hover) {
	opacity: 0.4;
}

Translated into English, the selector reads “if the user is hovering anywhere in the container element, set the figure elements inside to opacity: 0.4, except for any figure element that the user is hovering over directly”.

Mobile Considerations

Finally, you could add an @media query at the end to tidy up the presentation at small screen sizes:

@media screen and (max-width: 750px) {
	#columns { column-gap: 0px; }
	#columns figure { width: 100%; }
}

Alternatives

It’s possible to use to achieve this layout; the only condition is that the outer container must have an explicit height, otherwise the inner items will continue in a single unbroken column.

There’s also a lot of code repetition in the HTML: adding an image to the layout requires wrapping it in a <figure> element and inserting <figcaption> text. This process can be automated with JavaScript, while preserving the principles of progressive enhancement.

Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.
Check out the CodePen demo for this article at https://codepen.io/dudleystorey/pen/yqrhw